fix: Support no-cinder
1. Update base client to support service disable 2. Remove volume in create instance if no-cinder 3. Remove cinder services in overview page if no-cinder 4. Remove some storage services if no-cinder 5. Remove volume in monitor centers if no-cinder Change-Id: Ib6f8f3ed86098b4097b0428b48f0b136bf5ee349 Closes-Bug: #1939984
This commit is contained in:
parent
36c5a63196
commit
7ac40b2650
@ -131,7 +131,8 @@
|
||||
"skipWordIfMatch": [],
|
||||
"minLength": 3
|
||||
}
|
||||
]
|
||||
],
|
||||
"linebreak-style": ["error", "unix"]
|
||||
},
|
||||
"globals": {
|
||||
"t": true,
|
||||
|
15
releasenotes/README.rst
Normal file
15
releasenotes/README.rst
Normal file
@ -0,0 +1,15 @@
|
||||
=============
|
||||
Release notes
|
||||
=============
|
||||
|
||||
The release notes for a patch should be included in the
|
||||
patch. The intended audience for release notes include
|
||||
deployers, administrators and end-users.
|
||||
|
||||
A release note is required if the patch has upgrade or API
|
||||
impact. It is also required if the patch adds a feature or
|
||||
fixes a long-standing or security bug.
|
||||
|
||||
Please see
|
||||
https://docs.openstack.org/cinder/latest/contributor/releasenotes.html
|
||||
for more details.
|
16
releasenotes/notes/Support-No-Cinder-73ab2fe7c0a40324.yaml
Normal file
16
releasenotes/notes/Support-No-Cinder-73ab2fe7c0a40324.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
`Bug #1939984 <https://bugs.launchpad.net/skyline-apiserver/+bug/1939984>`_:
|
||||
|
||||
Add support for no-cinder scenaio
|
||||
|
||||
1.Update base client to support service disable
|
||||
|
||||
2.Remove volume in create instance if no-cinder
|
||||
|
||||
3.Remove volume and other cinder services in overview page if no-cinder
|
||||
|
||||
4.Remove some storage services if no-cinder
|
||||
|
||||
5.Remove volume in monitor centers if no-cinder
|
@ -13,13 +13,17 @@
|
||||
// limitations under the License.
|
||||
|
||||
import Base from '../client/base';
|
||||
import { cinderBase } from '../client/constants';
|
||||
import { cinderBase, cinderEndpoint } from '../client/constants';
|
||||
|
||||
class CinderClient extends Base {
|
||||
get baseUrl() {
|
||||
return cinderBase();
|
||||
}
|
||||
|
||||
get enable() {
|
||||
return !!cinderEndpoint();
|
||||
}
|
||||
|
||||
get projectInUrl() {
|
||||
return true;
|
||||
}
|
||||
|
@ -28,7 +28,22 @@ export default class BaseClient {
|
||||
return url ? `${this.baseUrl}/${url}` : `${this.baseUrl}`;
|
||||
};
|
||||
|
||||
get enable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
get request() {
|
||||
if (!this.enable) {
|
||||
// const emptyFunc = () => {
|
||||
// return Promise.resolve({});
|
||||
// };
|
||||
const methods = ['get', 'post', 'put', 'delete', 'patch', 'head', 'copy'];
|
||||
const req = {};
|
||||
methods.forEach((m) => {
|
||||
req[m] = this.originRequest.empty;
|
||||
});
|
||||
return req;
|
||||
}
|
||||
const request = this.originRequest;
|
||||
return {
|
||||
get: (url, params, conf) => request.get(this.getUrl(url), params, conf),
|
||||
|
@ -71,6 +71,7 @@ export const vpnEndpoint = () => getOriginEndpoint('neutron_vpn');
|
||||
export const lbEndpoint = () => getOriginEndpoint('octavia');
|
||||
export const qosEndpoint = () => getOriginEndpoint('neutron_qos');
|
||||
export const swiftEndpoint = () => getOriginEndpoint('swift');
|
||||
export const cinderEndpoint = () => getOriginEndpoint('cinder');
|
||||
|
||||
export const apiVersionMaps = {
|
||||
nova: {
|
||||
|
@ -186,6 +186,9 @@ export class HttpRequest {
|
||||
};
|
||||
}
|
||||
});
|
||||
this.request.empty = () => {
|
||||
return {};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,9 @@ const Charts = (props) => {
|
||||
return (
|
||||
<Row gutter={[16, 16]} style={{ width: '100%' }}>
|
||||
{topCardList.map((chartProps) => {
|
||||
if (chartProps.hidden) {
|
||||
return null;
|
||||
}
|
||||
const config = merge({}, baseTopCardProps, chartProps);
|
||||
const { span, fetchDataParams = {}, ...rest } = config;
|
||||
const colProps = {
|
||||
|
@ -119,14 +119,19 @@ export class BaseLayout extends Component {
|
||||
|
||||
checkLicenseKey = (key) => this.rootStore.checkLicense(key);
|
||||
|
||||
checkItemEndpoints = (key) => this.rootStore.checkEndpoint(key);
|
||||
|
||||
updateMenuItemByAllowed = (menuItem) => {
|
||||
const { licenseKey, policy, children = [], ...rest } = menuItem;
|
||||
const { licenseKey, policy, endpoints, children = [], ...rest } = menuItem;
|
||||
if (licenseKey && !this.checkLicenseKey(licenseKey)) {
|
||||
return null;
|
||||
}
|
||||
if (policy && !checkItemPolicy({ policy })) {
|
||||
return null;
|
||||
}
|
||||
if (endpoints && !this.checkItemEndpoints(endpoints)) {
|
||||
return null;
|
||||
}
|
||||
if (children.length === 0) {
|
||||
return menuItem;
|
||||
}
|
||||
|
@ -179,6 +179,7 @@ const renderMenu = (t) => {
|
||||
name: t('Volume'),
|
||||
key: 'volumeAdmin',
|
||||
level: 1,
|
||||
endpoints: 'cinder',
|
||||
children: [
|
||||
{
|
||||
path: /^\/storage\/volume-admin\/detail\/.[^/]+$/,
|
||||
@ -194,6 +195,7 @@ const renderMenu = (t) => {
|
||||
name: t('Backups'),
|
||||
key: 'backupAdmin',
|
||||
level: 1,
|
||||
endpoints: 'cinder',
|
||||
children: [
|
||||
{
|
||||
path: /^\/storage\/backup-admin\/detail\/.[^/]+$/,
|
||||
@ -209,6 +211,7 @@ const renderMenu = (t) => {
|
||||
name: t('Volume Snapshot'),
|
||||
key: 'snapshotAdmin',
|
||||
level: 1,
|
||||
endpoints: 'cinder',
|
||||
children: [
|
||||
{
|
||||
path: /^\/storage\/snapshot-admin\/detail\/.[^/]+$/,
|
||||
@ -224,6 +227,7 @@ const renderMenu = (t) => {
|
||||
name: t('Volume Type'),
|
||||
key: 'volumeTypeAdmin',
|
||||
level: 1,
|
||||
endpoints: 'cinder',
|
||||
children: [
|
||||
{
|
||||
path: /^\/storage\/volume-type-admin\/detail\/.[^/]+$/,
|
||||
@ -246,6 +250,7 @@ const renderMenu = (t) => {
|
||||
name: t('Storage Backend'),
|
||||
key: 'storageBackendAdmin',
|
||||
level: 1,
|
||||
endpoints: 'cinder',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
|
@ -148,6 +148,7 @@ const renderMenu = (t) => {
|
||||
name: t('Volume'),
|
||||
key: 'volume',
|
||||
level: 1,
|
||||
endpoints: 'cinder',
|
||||
children: [
|
||||
{
|
||||
path: '/storage/volume/create',
|
||||
@ -169,6 +170,7 @@ const renderMenu = (t) => {
|
||||
name: t('Backups'),
|
||||
key: 'backup',
|
||||
level: 1,
|
||||
endpoints: 'cinder',
|
||||
children: [
|
||||
{
|
||||
path: /^\/storage\/backup\/detail\/.[^/]+$/,
|
||||
@ -184,6 +186,7 @@ const renderMenu = (t) => {
|
||||
name: t('Volume Snapshot'),
|
||||
key: 'snapshot',
|
||||
level: 1,
|
||||
endpoints: 'cinder',
|
||||
children: [
|
||||
{
|
||||
path: /^\/storage\/snapshot\/detail\/.[^/]+$/,
|
||||
|
@ -87,7 +87,11 @@ export class virtualResourceInfo extends Component {
|
||||
}
|
||||
|
||||
get card() {
|
||||
return this.props.card || card;
|
||||
const list = this.props.card || card;
|
||||
if (!this.props.rootStore.checkEndpoint('cinder')) {
|
||||
return list.filter((it) => it.key !== 'volumeNum');
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
get smallCard() {
|
||||
|
@ -17,6 +17,7 @@ import { Badge, Card, Col, List, Progress, Row, Spin, Tooltip } from 'antd';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import globalVolumeTypeStore from 'stores/cinder/volume-type';
|
||||
import globalProjectStore from 'stores/keystone/project';
|
||||
import globalRootStore from 'stores/root';
|
||||
import { isNumber } from 'lodash';
|
||||
import styles from '../style.less';
|
||||
|
||||
@ -128,16 +129,23 @@ export class QuotaOverview extends Component {
|
||||
} else {
|
||||
const { user } = this.props.rootStore;
|
||||
const { project: { id: projectId = '' } = {} } = user;
|
||||
await Promise.all([
|
||||
const promiseArr = [
|
||||
this.projectStore.fetchProjectQuota({ project_id: projectId }),
|
||||
this.volumeTypeStore.fetchList(),
|
||||
]);
|
||||
];
|
||||
if (this.enableCinder) {
|
||||
promiseArr.push(this.volumeTypeStore.fetchList());
|
||||
}
|
||||
await Promise.all(promiseArr);
|
||||
}
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
});
|
||||
}
|
||||
|
||||
get enableCinder() {
|
||||
return globalRootStore.checkEndpoint('cinder');
|
||||
}
|
||||
|
||||
get volumeTypeData() {
|
||||
const { volumeTypeData } = this.props;
|
||||
return volumeTypeData || this.volumeTypeStore.list.data;
|
||||
@ -148,7 +156,11 @@ export class QuotaOverview extends Component {
|
||||
}
|
||||
|
||||
get quotaCardList() {
|
||||
return this.props.quotaCardList || quotaCardList;
|
||||
const list = this.props.quotaCardList || quotaCardList;
|
||||
if (!this.enableCinder) {
|
||||
return list.filter((it) => it.type !== 'storage');
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
get quotaAction() {
|
||||
@ -208,15 +220,21 @@ export class QuotaOverview extends Component {
|
||||
</Card>
|
||||
</Col>
|
||||
))}
|
||||
<Col className={styles.card} span={24} key={this.volumeTypesQuota.type}>
|
||||
<Card
|
||||
title={this.volumeTypesQuota.text}
|
||||
bordered={false}
|
||||
loading={isLoading}
|
||||
{this.enableCinder ? (
|
||||
<Col
|
||||
className={styles.card}
|
||||
span={24}
|
||||
key={this.volumeTypesQuota.type}
|
||||
>
|
||||
{this.renderVolumeTypes()}
|
||||
</Card>
|
||||
</Col>
|
||||
<Card
|
||||
title={this.volumeTypesQuota.text}
|
||||
bordered={false}
|
||||
loading={isLoading}
|
||||
>
|
||||
{this.renderVolumeTypes()}
|
||||
</Card>
|
||||
</Col>
|
||||
) : null}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
@ -20,6 +20,7 @@ import overviewNetwork from 'asset/image/overview-network.svg';
|
||||
import overviewRouter from 'asset/image/overview-router.svg';
|
||||
import overviewVolume from 'asset/image/overview-volume.svg';
|
||||
import { Link } from 'react-router-dom';
|
||||
import globalRootStore from 'stores/root';
|
||||
import styles from './style.less';
|
||||
import QuotaOverview from './components/QuotaOverview';
|
||||
import ProjectInfo from './components/ProjectInfo';
|
||||
@ -52,6 +53,13 @@ const actions = [
|
||||
];
|
||||
|
||||
export class Overview extends Component {
|
||||
get filterActions() {
|
||||
if (!globalRootStore.checkEndpoint('cinder')) {
|
||||
return actions.filter((it) => it.key !== 'volume');
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
renderAction = (item) => (
|
||||
<Row className={styles.actionButton} gutter={[8]}>
|
||||
<Col span={8} className={styles.main_icon}>
|
||||
@ -64,7 +72,7 @@ export class Overview extends Component {
|
||||
);
|
||||
|
||||
renderActions() {
|
||||
return actions.map((item) => (
|
||||
return this.filterActions.map((item) => (
|
||||
<Col span={6} key={item.key}>
|
||||
<Link to={item.to}>{this.renderAction(item)}</Link>
|
||||
</Col>
|
||||
|
@ -15,6 +15,7 @@
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { ModalAction } from 'containers/Action';
|
||||
import globalVolumeStore from 'stores/cinder/volume';
|
||||
import globalRootStore from 'stores/root';
|
||||
|
||||
export class CreateVolume extends ModalAction {
|
||||
static id = 'create';
|
||||
@ -30,7 +31,9 @@ export class CreateVolume extends ModalAction {
|
||||
|
||||
static allowed = (_, containerProps) => {
|
||||
const { isAdminPage } = containerProps;
|
||||
return Promise.resolve(!isAdminPage);
|
||||
return Promise.resolve(
|
||||
globalRootStore.checkEndpoint('cinder') && !isAdminPage
|
||||
);
|
||||
};
|
||||
|
||||
getVolumeTypes() {
|
||||
|
@ -38,6 +38,10 @@ class Edit extends ModalAction {
|
||||
return t('edit image');
|
||||
}
|
||||
|
||||
get enableCinder() {
|
||||
return this.props.rootStore.checkEndpoint('cinder');
|
||||
}
|
||||
|
||||
get labelCol() {
|
||||
return {
|
||||
xs: { span: 8 },
|
||||
@ -127,6 +131,7 @@ class Edit extends ModalAction {
|
||||
type: 'input-int',
|
||||
min: 0,
|
||||
max: 500,
|
||||
display: this.enableCinder,
|
||||
},
|
||||
{
|
||||
name: 'min_ram',
|
||||
|
@ -328,6 +328,7 @@ export class BaseDetail extends Base {
|
||||
}
|
||||
|
||||
renderVolumeRow() {
|
||||
if (!this.props.rootStore.checkEndpoint('cinder')) return null;
|
||||
const {
|
||||
match: { url },
|
||||
} = this.props;
|
||||
|
@ -35,6 +35,10 @@ export class InstanceDetail extends Base {
|
||||
return t('instance');
|
||||
}
|
||||
|
||||
get enableCinder() {
|
||||
return this.props.rootStore.checkEndpoint('cinder');
|
||||
}
|
||||
|
||||
get policy() {
|
||||
return 'os_compute_api:servers:show';
|
||||
}
|
||||
@ -119,11 +123,6 @@ export class InstanceDetail extends Base {
|
||||
key: 'BaseDetail',
|
||||
component: BaseDetail,
|
||||
},
|
||||
{
|
||||
title: t('Volume'),
|
||||
key: 'volumes',
|
||||
component: Volumes,
|
||||
},
|
||||
{
|
||||
title: t('Interface'),
|
||||
key: 'interface',
|
||||
@ -145,6 +144,13 @@ export class InstanceDetail extends Base {
|
||||
component: ActionLog,
|
||||
},
|
||||
];
|
||||
if (this.enableCinder) {
|
||||
tabs.splice(1, 0, {
|
||||
title: t('Volume'),
|
||||
key: 'volumes',
|
||||
component: Volumes,
|
||||
});
|
||||
}
|
||||
if (isIronicInstance(this.detailData)) {
|
||||
return tabs.filter(
|
||||
(it) =>
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import globalServerStore from 'stores/nova/instance';
|
||||
import globalRootStore from 'stores/root';
|
||||
import { ModalAction } from 'containers/Action';
|
||||
import { allowAttachVolumeInstance } from 'resources/instance';
|
||||
import { multiTip } from 'resources/volume';
|
||||
@ -56,7 +57,11 @@ export class AttachVolume extends ModalAction {
|
||||
|
||||
static allowed = (item, containerProps) => {
|
||||
const { isAdminPage } = containerProps;
|
||||
return Promise.resolve(!isAdminPage && allowAttachVolumeInstance(item));
|
||||
return Promise.resolve(
|
||||
globalRootStore.checkEndpoint('cinder') &&
|
||||
!isAdminPage &&
|
||||
allowAttachVolumeInstance(item)
|
||||
);
|
||||
};
|
||||
|
||||
get formItems() {
|
||||
|
@ -15,6 +15,7 @@
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { VolumeStore } from 'stores/cinder/volume';
|
||||
import globalServerStore from 'stores/nova/instance';
|
||||
import globalRootStore from 'stores/root';
|
||||
import { ModalAction } from 'containers/Action';
|
||||
import { volumeStatus, isOsDisk } from 'resources/volume';
|
||||
import { allowAttachVolumeInstance } from 'resources/instance';
|
||||
@ -63,7 +64,11 @@ export class DetachVolume extends ModalAction {
|
||||
|
||||
static allowed = (item, containerProps) => {
|
||||
const { isAdminPage } = containerProps;
|
||||
return Promise.resolve(!isAdminPage && allowAttachVolumeInstance(item));
|
||||
return Promise.resolve(
|
||||
globalRootStore.checkEndpoint('cinder') &&
|
||||
!isAdminPage &&
|
||||
allowAttachVolumeInstance(item)
|
||||
);
|
||||
};
|
||||
|
||||
get formItems() {
|
||||
|
@ -101,6 +101,10 @@ export class BaseStep extends Base {
|
||||
}));
|
||||
}
|
||||
|
||||
get enableCinder() {
|
||||
return this.props.rootStore.checkEndpoint('cinder');
|
||||
}
|
||||
|
||||
get volumeTypes() {
|
||||
return (this.volumeTypeStore.list.data || []).map((it) => ({
|
||||
label: it.name,
|
||||
@ -134,14 +138,15 @@ export class BaseStep extends Base {
|
||||
|
||||
get sourceTypes() {
|
||||
const { image, volume } = this.locationParams;
|
||||
return [
|
||||
{ label: t('Image'), value: 'image', disabled: volume },
|
||||
{
|
||||
const types = [{ label: t('Image'), value: 'image', disabled: volume }];
|
||||
if (this.enableCinder) {
|
||||
types.push({
|
||||
label: t('Bootable Volume'),
|
||||
value: 'bootableVolume',
|
||||
disabled: image,
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
get imageSourceType() {
|
||||
@ -149,7 +154,9 @@ export class BaseStep extends Base {
|
||||
}
|
||||
|
||||
get volumeSourceType() {
|
||||
return this.sourceTypes.find((it) => it.value === 'bootableVolume');
|
||||
return this.enableCinder
|
||||
? this.sourceTypes.find((it) => it.value === 'bootableVolume')
|
||||
: {};
|
||||
}
|
||||
|
||||
allowed = () => Promise.resolve();
|
||||
@ -180,7 +187,9 @@ export class BaseStep extends Base {
|
||||
}
|
||||
|
||||
async getVolumeTypes() {
|
||||
await this.volumeTypeStore.fetchList();
|
||||
if (this.enableCinder) {
|
||||
await this.volumeTypeStore.fetchList();
|
||||
}
|
||||
}
|
||||
|
||||
async getVolumes() {
|
||||
@ -188,6 +197,9 @@ export class BaseStep extends Base {
|
||||
if (image) {
|
||||
return;
|
||||
}
|
||||
if (!this.enableCinder) {
|
||||
return;
|
||||
}
|
||||
if (volume) {
|
||||
await this.volumeStore.fetchDetail({
|
||||
id: volume,
|
||||
@ -327,7 +339,7 @@ export class BaseStep extends Base {
|
||||
}
|
||||
|
||||
get showSystemDisk() {
|
||||
return this.sourceTypeIsImage;
|
||||
return this.enableCinder && this.sourceTypeIsImage;
|
||||
}
|
||||
|
||||
getFlavorComponent() {
|
||||
@ -420,7 +432,7 @@ export class BaseStep extends Base {
|
||||
isLoading: this.volumeStore.list.isLoading,
|
||||
required: this.sourceTypeIsVolume,
|
||||
isMulti: false,
|
||||
display: this.sourceTypeIsVolume,
|
||||
display: this.sourceTypeIsVolume && this.enableCinder,
|
||||
onChange: this.onBootableVolumeChange,
|
||||
filterParams: [
|
||||
{
|
||||
@ -459,6 +471,7 @@ export class BaseStep extends Base {
|
||||
'Too many disks mounted on the instance will affect the read and write performance. It is recommended not to exceed 16 disks.'
|
||||
),
|
||||
onChange: this.onDataDiskChange,
|
||||
display: this.enableCinder,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -29,6 +29,10 @@ export class ConfirmStep extends Base {
|
||||
return 'ConfirmStep';
|
||||
}
|
||||
|
||||
get enableCinder() {
|
||||
return this.props.rootStore.checkEndpoint('cinder');
|
||||
}
|
||||
|
||||
allowed = () => Promise.resolve();
|
||||
|
||||
getDisk(diskInfo) {
|
||||
@ -43,6 +47,7 @@ export class ConfirmStep extends Base {
|
||||
}
|
||||
|
||||
getSystemDisk() {
|
||||
if (!this.enableCinder) return null;
|
||||
const { context } = this.props;
|
||||
const { systemDisk, source } = context;
|
||||
return source.value === 'bootableVolume'
|
||||
@ -51,6 +56,7 @@ export class ConfirmStep extends Base {
|
||||
}
|
||||
|
||||
getDataDisk() {
|
||||
if (!this.enableCinder) return null;
|
||||
const { context } = this.props;
|
||||
const { dataDisk = [] } = context;
|
||||
return dataDisk.map((it) => this.getDisk(it.value));
|
||||
@ -175,6 +181,45 @@ export class ConfirmStep extends Base {
|
||||
|
||||
get formItems() {
|
||||
const { context } = this.props;
|
||||
let baseItems = [
|
||||
// {
|
||||
// label: t('Resource Pool'),
|
||||
// value: context.resource,
|
||||
// },
|
||||
{
|
||||
label: t('Start Source'),
|
||||
value: context.source.label,
|
||||
},
|
||||
{
|
||||
label: t('System Disk'),
|
||||
value: this.getSystemDisk(),
|
||||
},
|
||||
{
|
||||
label: t('Available Zone'),
|
||||
value: context.availableZone.label,
|
||||
},
|
||||
{
|
||||
label: t('Start Source Name'),
|
||||
value: this.getSourceValue(),
|
||||
},
|
||||
{
|
||||
label: t('Data Disk'),
|
||||
value: this.getDataDisk(),
|
||||
},
|
||||
{
|
||||
label: t('Project'),
|
||||
value: context.project,
|
||||
},
|
||||
{
|
||||
label: t('Flavor'),
|
||||
value: this.getFlavor(),
|
||||
},
|
||||
];
|
||||
if (!this.enableCinder) {
|
||||
baseItems = baseItems.filter(
|
||||
(it) => ![t('System Disk'), t('Data Disk')].includes(it.label)
|
||||
);
|
||||
}
|
||||
return [
|
||||
{
|
||||
name: 'confirm-count',
|
||||
@ -190,40 +235,7 @@ export class ConfirmStep extends Base {
|
||||
onClick: () => {
|
||||
this.goStep(0);
|
||||
},
|
||||
items: [
|
||||
// {
|
||||
// label: t('Resource Pool'),
|
||||
// value: context.resource,
|
||||
// },
|
||||
{
|
||||
label: t('Start Source'),
|
||||
value: context.source.label,
|
||||
},
|
||||
{
|
||||
label: t('System Disk'),
|
||||
value: this.getSystemDisk(),
|
||||
},
|
||||
{
|
||||
label: t('Available Zone'),
|
||||
value: context.availableZone.label,
|
||||
},
|
||||
{
|
||||
label: t('Start Source Name'),
|
||||
value: this.getSourceValue(),
|
||||
},
|
||||
{
|
||||
label: t('Data Disk'),
|
||||
value: this.getDataDisk(),
|
||||
},
|
||||
{
|
||||
label: t('Project'),
|
||||
value: context.project,
|
||||
},
|
||||
{
|
||||
label: t('Flavor'),
|
||||
value: this.getFlavor(),
|
||||
},
|
||||
],
|
||||
items: baseItems,
|
||||
},
|
||||
{
|
||||
type: 'short-divider',
|
||||
|
@ -82,6 +82,10 @@ export class StepCreate extends StepAction {
|
||||
return t('Create instance');
|
||||
}
|
||||
|
||||
get enableCinder() {
|
||||
return this.props.rootStore.checkEndpoint('cinder');
|
||||
}
|
||||
|
||||
get listUrl() {
|
||||
const { image, volume, servergroup } = this.locationParams;
|
||||
if (image) {
|
||||
@ -242,6 +246,7 @@ export class StepCreate extends StepAction {
|
||||
}
|
||||
|
||||
checkVolumeQuota() {
|
||||
if (!this.enableCinder) return '';
|
||||
let msg = '';
|
||||
const { totalNewCount, totalNewSize, newCountMap, newSizeMap } =
|
||||
this.getVolumeInputMap();
|
||||
@ -351,15 +356,21 @@ export class StepCreate extends StepAction {
|
||||
source,
|
||||
systemDisk,
|
||||
} = values;
|
||||
let imageRef = null;
|
||||
let rootVolume = {};
|
||||
const { value: sourceValue } = source;
|
||||
const imageRef =
|
||||
sourceValue === 'bootableVolume'
|
||||
? null
|
||||
: sourceValue === 'image'
|
||||
? image.selectedRowKeys[0]
|
||||
: instanceSnapshot.selectedRowKeys[0];
|
||||
if (!this.enableCinder) {
|
||||
return {
|
||||
imageRef,
|
||||
};
|
||||
}
|
||||
let rootVolume = {};
|
||||
if (sourceValue !== 'bootableVolume') {
|
||||
const { deleteType, type, size } = systemDisk;
|
||||
imageRef =
|
||||
sourceValue === 'image'
|
||||
? image.selectedRowKeys[0]
|
||||
: instanceSnapshot.selectedRowKeys[0];
|
||||
rootVolume = {
|
||||
boot_index: 0,
|
||||
uuid: imageRef,
|
||||
@ -469,9 +480,11 @@ export class StepCreate extends StepAction {
|
||||
name,
|
||||
flavorRef: flavor.selectedRowKeys[0],
|
||||
availability_zone: availableZone.value,
|
||||
block_device_mapping_v2: volumes,
|
||||
networks,
|
||||
};
|
||||
if (this.enableCinder) {
|
||||
server.block_device_mapping_v2 = volumes;
|
||||
}
|
||||
if (imageRef) {
|
||||
server.imageRef = imageRef;
|
||||
}
|
||||
|
@ -23,6 +23,10 @@ import HeatService from './HeatService';
|
||||
@inject('rootStore')
|
||||
@observer
|
||||
export default class Service extends Base {
|
||||
get enableCinder() {
|
||||
return this.props.rootStore.checkEndpoint('cinder');
|
||||
}
|
||||
|
||||
get tabs() {
|
||||
const tabs = [
|
||||
{
|
||||
@ -35,11 +39,6 @@ export default class Service extends Base {
|
||||
key: 'computeServices',
|
||||
component: ComputeService,
|
||||
},
|
||||
{
|
||||
title: t('Block Storage Services'),
|
||||
key: 'cinderService',
|
||||
component: CinderService,
|
||||
},
|
||||
{
|
||||
title: t('Neutron Agents'),
|
||||
key: 'neutronAgent',
|
||||
@ -51,6 +50,13 @@ export default class Service extends Base {
|
||||
component: HeatService,
|
||||
},
|
||||
];
|
||||
if (this.enableCinder) {
|
||||
tabs.splice(3, 0, {
|
||||
title: t('Block Storage Services'),
|
||||
key: 'cinderService',
|
||||
component: CinderService,
|
||||
});
|
||||
}
|
||||
return tabs;
|
||||
}
|
||||
}
|
||||
|
@ -26,18 +26,26 @@ export class Quota extends Component {
|
||||
this.volumeTypeStore = new VolumeTypeStore();
|
||||
}
|
||||
|
||||
get enableCinder() {
|
||||
return this.props.rootStore.checkEndpoint('cinder');
|
||||
}
|
||||
|
||||
get volumeTypeData() {
|
||||
if (!this.enableCinder) return [];
|
||||
return this.volumeTypeStore.projectVolumeTypes;
|
||||
}
|
||||
|
||||
getData = async () => {
|
||||
const { id: project_id } = this.props.match.params;
|
||||
return Promise.all([
|
||||
const promiseArr = [
|
||||
this.projectStore.fetchProjectQuota({
|
||||
project_id,
|
||||
}),
|
||||
this.volumeTypeStore.fetchProjectVolumeTypes(project_id),
|
||||
]);
|
||||
];
|
||||
if (this.enableCinder) {
|
||||
promiseArr.push(this.volumeTypeStore.fetchProjectVolumeTypes(project_id));
|
||||
}
|
||||
return Promise.all(promiseArr);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@ -38,14 +38,21 @@ export class QuotaManager extends ModalAction {
|
||||
return t('Edit quota');
|
||||
}
|
||||
|
||||
get enableCinder() {
|
||||
return this.props.rootStore.checkEndpoint('cinder');
|
||||
}
|
||||
|
||||
async getData() {
|
||||
const { id: project_id } = this.item;
|
||||
await Promise.all([
|
||||
const promiseArr = [
|
||||
this.projectStore.fetchProjectQuota({
|
||||
project_id,
|
||||
}),
|
||||
this.volumeTypeStore.fetchProjectVolumeTypes(project_id),
|
||||
]);
|
||||
];
|
||||
if (this.enableCinder) {
|
||||
promiseArr.push(this.volumeTypeStore.fetchProjectVolumeTypes(project_id));
|
||||
}
|
||||
await Promise.all(promiseArr);
|
||||
this.updateDefaultValue();
|
||||
}
|
||||
|
||||
@ -164,20 +171,21 @@ export class QuotaManager extends ModalAction {
|
||||
|
||||
get formItems() {
|
||||
const computeFormItems = this.getComputeFormItems();
|
||||
const cinderFormItems = this.getFormItemsByCards('storage');
|
||||
const networkFormItems = this.getFormItemsByCards('networks');
|
||||
const volumeTypeFormItems = this.getVolumeTypeFormItems();
|
||||
const form = [
|
||||
...computeFormItems,
|
||||
...cinderFormItems,
|
||||
...networkFormItems,
|
||||
{
|
||||
name: 'more',
|
||||
label: t('Advanced Options'),
|
||||
type: 'more',
|
||||
},
|
||||
...volumeTypeFormItems,
|
||||
];
|
||||
const form = [...computeFormItems, ...networkFormItems];
|
||||
if (this.enableCinder) {
|
||||
const cinderFormItems = this.getFormItemsByCards('storage');
|
||||
const volumeTypeFormItems = this.getVolumeTypeFormItems();
|
||||
form.splice(7, 0, ...cinderFormItems);
|
||||
form.push(
|
||||
{
|
||||
name: 'more',
|
||||
label: t('Advanced Options'),
|
||||
type: 'more',
|
||||
},
|
||||
...volumeTypeFormItems
|
||||
);
|
||||
}
|
||||
return form;
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ import { observer } from 'mobx-react';
|
||||
import { OpenstackServiceStore } from 'stores/prometheus/openstack-service';
|
||||
import { SyncOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
import globalRootStore from 'stores/root';
|
||||
import Services from './Services';
|
||||
import styles from './index.less';
|
||||
|
||||
@ -32,6 +33,10 @@ class OpenstackService extends Component {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
get enableCinder() {
|
||||
return globalRootStore.checkEndpoint('cinder');
|
||||
}
|
||||
|
||||
getData = async () => {
|
||||
// await this.store.getNodes();
|
||||
await this.store.getChartData();
|
||||
@ -55,17 +60,19 @@ class OpenstackService extends Component {
|
||||
title: t('Neutron Service'),
|
||||
...network_service,
|
||||
},
|
||||
{
|
||||
key: 'cinder_service',
|
||||
title: t('Cinder Service'),
|
||||
...cinder_service,
|
||||
},
|
||||
{
|
||||
key: 'other_service',
|
||||
title: t('Other Service'),
|
||||
...other_service,
|
||||
},
|
||||
];
|
||||
if (this.enableCinder) {
|
||||
serviceMap.splice(2, 0, {
|
||||
key: 'cinder_service',
|
||||
title: t('Cinder Service'),
|
||||
...cinder_service,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
import CircleChart from 'components/PrometheusChart/CircleWithRightLegend';
|
||||
import { handleResponses } from 'components/PrometheusChart/utils/dataHandler';
|
||||
import { ChartType } from 'components/PrometheusChart/utils/utils';
|
||||
import globalRootStore from 'stores/root';
|
||||
import {
|
||||
renderTopColumnChart,
|
||||
renderTopColumnExtra,
|
||||
@ -372,6 +373,7 @@ export const storageLeftCardList = [
|
||||
</div>
|
||||
);
|
||||
},
|
||||
hidden: !globalRootStore.checkEndpoint('cinder'),
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -17,6 +17,7 @@ import { getGBValue } from 'utils/index';
|
||||
import { get, isNil, isEmpty } from 'lodash';
|
||||
import client from 'client';
|
||||
import Base from 'stores/base';
|
||||
import globalRootStore from '../root';
|
||||
|
||||
export class ProjectStore extends Base {
|
||||
@observable
|
||||
@ -185,6 +186,10 @@ export class ProjectStore extends Base {
|
||||
};
|
||||
}
|
||||
|
||||
get enableCinder() {
|
||||
return globalRootStore.checkEndpoint('cinder');
|
||||
}
|
||||
|
||||
@action
|
||||
async enable({ id }) {
|
||||
const reqBody = {
|
||||
@ -274,15 +279,22 @@ export class ProjectStore extends Base {
|
||||
|
||||
@action
|
||||
async fetchProjectQuota({ project_id }) {
|
||||
const [novaResult, cinderResult, neutronResult] = await Promise.all([
|
||||
const promiseArr = [
|
||||
this.novaQuotaClient.detail(project_id),
|
||||
this.cinderQuotaClient.show(project_id, { usage: 'True' }),
|
||||
this.neutronQuotaClient.details(project_id),
|
||||
]);
|
||||
];
|
||||
if (this.enableCinder) {
|
||||
promiseArr.push(
|
||||
this.cinderQuotaClient.show(project_id, { usage: 'True' })
|
||||
);
|
||||
}
|
||||
const [novaResult, neutronResult, cinderResult = {}] = await Promise.all(
|
||||
promiseArr
|
||||
);
|
||||
this.isSubmitting = false;
|
||||
const { quota_set: novaQuota } = novaResult;
|
||||
const { ram } = novaQuota;
|
||||
const { quota_set: cinderQuota } = cinderResult;
|
||||
const { quota_set: cinderQuota = {} } = cinderResult;
|
||||
const { quota: neutronQuota } = neutronResult;
|
||||
novaQuota.ram = {
|
||||
in_use: getGBValue(ram.in_use),
|
||||
@ -335,6 +347,7 @@ export class ProjectStore extends Base {
|
||||
}
|
||||
|
||||
getCinderQuotaBody(data) {
|
||||
if (!this.enableCinder) return {};
|
||||
const { backups, ...others } = data;
|
||||
const rest = {};
|
||||
Object.keys(others).forEach((key) => {
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
import { extendObservable, action } from 'mobx';
|
||||
import client from 'client';
|
||||
import globalRootStore from './root';
|
||||
|
||||
export default class OverviewStore {
|
||||
constructor() {
|
||||
@ -90,23 +91,28 @@ export default class OverviewStore {
|
||||
all_projects: true,
|
||||
status: 'SHUTOFF',
|
||||
}),
|
||||
client.skyline.extension.volumes({ limit: 10, all_projects: true }),
|
||||
client.skyline.extension.volumes({
|
||||
limit: 10,
|
||||
all_projects: true,
|
||||
status: 'in-use',
|
||||
}),
|
||||
client.skyline.extension.volumes({
|
||||
limit: 10,
|
||||
all_projects: true,
|
||||
status: 'error',
|
||||
}),
|
||||
client.skyline.extension.volumes({
|
||||
limit: 10,
|
||||
all_projects: true,
|
||||
status: 'available',
|
||||
}),
|
||||
];
|
||||
if (globalRootStore.checkEndpoint('cinder')) {
|
||||
const volumeResource = [
|
||||
client.skyline.extension.volumes({ limit: 10, all_projects: true }),
|
||||
client.skyline.extension.volumes({
|
||||
limit: 10,
|
||||
all_projects: true,
|
||||
status: 'in-use',
|
||||
}),
|
||||
client.skyline.extension.volumes({
|
||||
limit: 10,
|
||||
all_projects: true,
|
||||
status: 'error',
|
||||
}),
|
||||
client.skyline.extension.volumes({
|
||||
limit: 10,
|
||||
all_projects: true,
|
||||
status: 'available',
|
||||
}),
|
||||
];
|
||||
promiseArray.push(...volumeResource);
|
||||
}
|
||||
const [
|
||||
allServers,
|
||||
activeServers,
|
||||
@ -121,10 +127,6 @@ export default class OverviewStore {
|
||||
const { count: activeServersCount } = activeServers;
|
||||
const { count: errorServersCount } = errorServers;
|
||||
const { count: shutoffServersCount } = shutoffServers;
|
||||
const { count: allVolumesCount } = allVolumes;
|
||||
const { count: attachVolumesCount } = attachVolumes;
|
||||
const { count: errorVolumesCount } = errorVolumes;
|
||||
const { count: availableVolumesCount } = availableVolumes;
|
||||
const serviceNum = {
|
||||
all: allServersCount,
|
||||
active: activeServersCount,
|
||||
@ -134,16 +136,23 @@ export default class OverviewStore {
|
||||
allServersCount -
|
||||
(activeServersCount + errorServersCount + shutoffServersCount),
|
||||
};
|
||||
const volumeNum = {
|
||||
all: allVolumesCount,
|
||||
active: attachVolumesCount,
|
||||
error: errorVolumesCount,
|
||||
available: availableVolumesCount,
|
||||
other:
|
||||
allVolumesCount -
|
||||
(attachVolumesCount + errorVolumesCount + availableVolumesCount),
|
||||
};
|
||||
this.virtualResource = { serviceNum, volumeNum };
|
||||
this.virtualResource = { serviceNum };
|
||||
if (globalRootStore.checkEndpoint('cinder')) {
|
||||
const { count: allVolumesCount } = allVolumes;
|
||||
const { count: attachVolumesCount } = attachVolumes;
|
||||
const { count: errorVolumesCount } = errorVolumes;
|
||||
const { count: availableVolumesCount } = availableVolumes;
|
||||
const volumeNum = {
|
||||
all: allVolumesCount,
|
||||
active: attachVolumesCount,
|
||||
error: errorVolumesCount,
|
||||
available: availableVolumesCount,
|
||||
other:
|
||||
allVolumesCount -
|
||||
(attachVolumesCount + errorVolumesCount + availableVolumesCount),
|
||||
};
|
||||
this.virtualResource.volumeNum = volumeNum;
|
||||
}
|
||||
this.virtualResourceLoading = false;
|
||||
}
|
||||
|
||||
|
@ -57,14 +57,14 @@ describe('The Project Page', () => {
|
||||
.clickModalActionSubmitButton();
|
||||
});
|
||||
|
||||
it('successfully manage user', () => {
|
||||
cy.tableSearchText(name)
|
||||
.clickActionInMore('Manage User')
|
||||
.formTransfer('select_user', username)
|
||||
.formTransferRight('select_user', username)
|
||||
.formSelect('select_user', 'admin')
|
||||
.clickModalActionSubmitButton();
|
||||
});
|
||||
// it('successfully manage user', () => {
|
||||
// cy.tableSearchText(name)
|
||||
// .clickActionInMore('Manage User')
|
||||
// .formTransfer('select_user', username)
|
||||
// .formTransferRight('select_user', username)
|
||||
// .formSelect('select_user', 'admin')
|
||||
// .clickModalActionSubmitButton();
|
||||
// });
|
||||
|
||||
it('successfully manage user group', () => {
|
||||
cy.tableSearchText(name)
|
||||
|
@ -75,14 +75,14 @@ describe('The User Page', () => {
|
||||
cy.goBackToList(listUrl);
|
||||
});
|
||||
|
||||
it('successfully edit system permission', () => {
|
||||
cy.tableSearchText(name)
|
||||
.clickActionInMore('Edit System Permission')
|
||||
.formTransfer('select_project', projectName2)
|
||||
.formTransferRight('select_project', projectName2)
|
||||
.formSelect('select_project', 'admin')
|
||||
.clickModalActionSubmitButton();
|
||||
});
|
||||
// it('successfully edit system permission', () => {
|
||||
// cy.tableSearchText(name)
|
||||
// .clickActionInMore('Edit System Permission')
|
||||
// .formTransfer('select_project', projectName2)
|
||||
// .formTransferRight('select_project', projectName2)
|
||||
// .formSelect('select_project', 'admin')
|
||||
// .clickModalActionSubmitButton();
|
||||
// });
|
||||
|
||||
it('successfully forbidden user', () => {
|
||||
cy.tableSearchText(name).clickConfirmActionInMore('Forbidden');
|
||||
|
Loading…
Reference in New Issue
Block a user