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:
xusongfu 2022-02-14 09:48:49 +08:00
parent 36c5a63196
commit 7ac40b2650
32 changed files with 371 additions and 154 deletions

View File

@ -131,7 +131,8 @@
"skipWordIfMatch": [], "skipWordIfMatch": [],
"minLength": 3 "minLength": 3
} }
] ],
"linebreak-style": ["error", "unix"]
}, },
"globals": { "globals": {
"t": true, "t": true,

15
releasenotes/README.rst Normal file
View 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.

View 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

View File

@ -13,13 +13,17 @@
// limitations under the License. // limitations under the License.
import Base from '../client/base'; import Base from '../client/base';
import { cinderBase } from '../client/constants'; import { cinderBase, cinderEndpoint } from '../client/constants';
class CinderClient extends Base { class CinderClient extends Base {
get baseUrl() { get baseUrl() {
return cinderBase(); return cinderBase();
} }
get enable() {
return !!cinderEndpoint();
}
get projectInUrl() { get projectInUrl() {
return true; return true;
} }

View File

@ -28,7 +28,22 @@ export default class BaseClient {
return url ? `${this.baseUrl}/${url}` : `${this.baseUrl}`; return url ? `${this.baseUrl}/${url}` : `${this.baseUrl}`;
}; };
get enable() {
return true;
}
get request() { 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; const request = this.originRequest;
return { return {
get: (url, params, conf) => request.get(this.getUrl(url), params, conf), get: (url, params, conf) => request.get(this.getUrl(url), params, conf),

View File

@ -71,6 +71,7 @@ export const vpnEndpoint = () => getOriginEndpoint('neutron_vpn');
export const lbEndpoint = () => getOriginEndpoint('octavia'); export const lbEndpoint = () => getOriginEndpoint('octavia');
export const qosEndpoint = () => getOriginEndpoint('neutron_qos'); export const qosEndpoint = () => getOriginEndpoint('neutron_qos');
export const swiftEndpoint = () => getOriginEndpoint('swift'); export const swiftEndpoint = () => getOriginEndpoint('swift');
export const cinderEndpoint = () => getOriginEndpoint('cinder');
export const apiVersionMaps = { export const apiVersionMaps = {
nova: { nova: {

View File

@ -186,6 +186,9 @@ export class HttpRequest {
}; };
} }
}); });
this.request.empty = () => {
return {};
};
}; };
} }

View File

@ -32,6 +32,9 @@ const Charts = (props) => {
return ( return (
<Row gutter={[16, 16]} style={{ width: '100%' }}> <Row gutter={[16, 16]} style={{ width: '100%' }}>
{topCardList.map((chartProps) => { {topCardList.map((chartProps) => {
if (chartProps.hidden) {
return null;
}
const config = merge({}, baseTopCardProps, chartProps); const config = merge({}, baseTopCardProps, chartProps);
const { span, fetchDataParams = {}, ...rest } = config; const { span, fetchDataParams = {}, ...rest } = config;
const colProps = { const colProps = {

View File

@ -119,14 +119,19 @@ export class BaseLayout extends Component {
checkLicenseKey = (key) => this.rootStore.checkLicense(key); checkLicenseKey = (key) => this.rootStore.checkLicense(key);
checkItemEndpoints = (key) => this.rootStore.checkEndpoint(key);
updateMenuItemByAllowed = (menuItem) => { updateMenuItemByAllowed = (menuItem) => {
const { licenseKey, policy, children = [], ...rest } = menuItem; const { licenseKey, policy, endpoints, children = [], ...rest } = menuItem;
if (licenseKey && !this.checkLicenseKey(licenseKey)) { if (licenseKey && !this.checkLicenseKey(licenseKey)) {
return null; return null;
} }
if (policy && !checkItemPolicy({ policy })) { if (policy && !checkItemPolicy({ policy })) {
return null; return null;
} }
if (endpoints && !this.checkItemEndpoints(endpoints)) {
return null;
}
if (children.length === 0) { if (children.length === 0) {
return menuItem; return menuItem;
} }

View File

@ -179,6 +179,7 @@ const renderMenu = (t) => {
name: t('Volume'), name: t('Volume'),
key: 'volumeAdmin', key: 'volumeAdmin',
level: 1, level: 1,
endpoints: 'cinder',
children: [ children: [
{ {
path: /^\/storage\/volume-admin\/detail\/.[^/]+$/, path: /^\/storage\/volume-admin\/detail\/.[^/]+$/,
@ -194,6 +195,7 @@ const renderMenu = (t) => {
name: t('Backups'), name: t('Backups'),
key: 'backupAdmin', key: 'backupAdmin',
level: 1, level: 1,
endpoints: 'cinder',
children: [ children: [
{ {
path: /^\/storage\/backup-admin\/detail\/.[^/]+$/, path: /^\/storage\/backup-admin\/detail\/.[^/]+$/,
@ -209,6 +211,7 @@ const renderMenu = (t) => {
name: t('Volume Snapshot'), name: t('Volume Snapshot'),
key: 'snapshotAdmin', key: 'snapshotAdmin',
level: 1, level: 1,
endpoints: 'cinder',
children: [ children: [
{ {
path: /^\/storage\/snapshot-admin\/detail\/.[^/]+$/, path: /^\/storage\/snapshot-admin\/detail\/.[^/]+$/,
@ -224,6 +227,7 @@ const renderMenu = (t) => {
name: t('Volume Type'), name: t('Volume Type'),
key: 'volumeTypeAdmin', key: 'volumeTypeAdmin',
level: 1, level: 1,
endpoints: 'cinder',
children: [ children: [
{ {
path: /^\/storage\/volume-type-admin\/detail\/.[^/]+$/, path: /^\/storage\/volume-type-admin\/detail\/.[^/]+$/,
@ -246,6 +250,7 @@ const renderMenu = (t) => {
name: t('Storage Backend'), name: t('Storage Backend'),
key: 'storageBackendAdmin', key: 'storageBackendAdmin',
level: 1, level: 1,
endpoints: 'cinder',
children: [], children: [],
}, },
], ],

View File

@ -148,6 +148,7 @@ const renderMenu = (t) => {
name: t('Volume'), name: t('Volume'),
key: 'volume', key: 'volume',
level: 1, level: 1,
endpoints: 'cinder',
children: [ children: [
{ {
path: '/storage/volume/create', path: '/storage/volume/create',
@ -169,6 +170,7 @@ const renderMenu = (t) => {
name: t('Backups'), name: t('Backups'),
key: 'backup', key: 'backup',
level: 1, level: 1,
endpoints: 'cinder',
children: [ children: [
{ {
path: /^\/storage\/backup\/detail\/.[^/]+$/, path: /^\/storage\/backup\/detail\/.[^/]+$/,
@ -184,6 +186,7 @@ const renderMenu = (t) => {
name: t('Volume Snapshot'), name: t('Volume Snapshot'),
key: 'snapshot', key: 'snapshot',
level: 1, level: 1,
endpoints: 'cinder',
children: [ children: [
{ {
path: /^\/storage\/snapshot\/detail\/.[^/]+$/, path: /^\/storage\/snapshot\/detail\/.[^/]+$/,

View File

@ -87,7 +87,11 @@ export class virtualResourceInfo extends Component {
} }
get card() { 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() { get smallCard() {

View File

@ -17,6 +17,7 @@ import { Badge, Card, Col, List, Progress, Row, Spin, Tooltip } from 'antd';
import { inject, observer } from 'mobx-react'; import { inject, observer } from 'mobx-react';
import globalVolumeTypeStore from 'stores/cinder/volume-type'; import globalVolumeTypeStore from 'stores/cinder/volume-type';
import globalProjectStore from 'stores/keystone/project'; import globalProjectStore from 'stores/keystone/project';
import globalRootStore from 'stores/root';
import { isNumber } from 'lodash'; import { isNumber } from 'lodash';
import styles from '../style.less'; import styles from '../style.less';
@ -128,16 +129,23 @@ export class QuotaOverview extends Component {
} else { } else {
const { user } = this.props.rootStore; const { user } = this.props.rootStore;
const { project: { id: projectId = '' } = {} } = user; const { project: { id: projectId = '' } = {} } = user;
await Promise.all([ const promiseArr = [
this.projectStore.fetchProjectQuota({ project_id: projectId }), this.projectStore.fetchProjectQuota({ project_id: projectId }),
this.volumeTypeStore.fetchList(), ];
]); if (this.enableCinder) {
promiseArr.push(this.volumeTypeStore.fetchList());
}
await Promise.all(promiseArr);
} }
this.setState({ this.setState({
isLoading: false, isLoading: false,
}); });
} }
get enableCinder() {
return globalRootStore.checkEndpoint('cinder');
}
get volumeTypeData() { get volumeTypeData() {
const { volumeTypeData } = this.props; const { volumeTypeData } = this.props;
return volumeTypeData || this.volumeTypeStore.list.data; return volumeTypeData || this.volumeTypeStore.list.data;
@ -148,7 +156,11 @@ export class QuotaOverview extends Component {
} }
get quotaCardList() { 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() { get quotaAction() {
@ -208,7 +220,12 @@ export class QuotaOverview extends Component {
</Card> </Card>
</Col> </Col>
))} ))}
<Col className={styles.card} span={24} key={this.volumeTypesQuota.type}> {this.enableCinder ? (
<Col
className={styles.card}
span={24}
key={this.volumeTypesQuota.type}
>
<Card <Card
title={this.volumeTypesQuota.text} title={this.volumeTypesQuota.text}
bordered={false} bordered={false}
@ -217,6 +234,7 @@ export class QuotaOverview extends Component {
{this.renderVolumeTypes()} {this.renderVolumeTypes()}
</Card> </Card>
</Col> </Col>
) : null}
</Row> </Row>
); );
}; };

View File

@ -20,6 +20,7 @@ import overviewNetwork from 'asset/image/overview-network.svg';
import overviewRouter from 'asset/image/overview-router.svg'; import overviewRouter from 'asset/image/overview-router.svg';
import overviewVolume from 'asset/image/overview-volume.svg'; import overviewVolume from 'asset/image/overview-volume.svg';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import globalRootStore from 'stores/root';
import styles from './style.less'; import styles from './style.less';
import QuotaOverview from './components/QuotaOverview'; import QuotaOverview from './components/QuotaOverview';
import ProjectInfo from './components/ProjectInfo'; import ProjectInfo from './components/ProjectInfo';
@ -52,6 +53,13 @@ const actions = [
]; ];
export class Overview extends Component { export class Overview extends Component {
get filterActions() {
if (!globalRootStore.checkEndpoint('cinder')) {
return actions.filter((it) => it.key !== 'volume');
}
return actions;
}
renderAction = (item) => ( renderAction = (item) => (
<Row className={styles.actionButton} gutter={[8]}> <Row className={styles.actionButton} gutter={[8]}>
<Col span={8} className={styles.main_icon}> <Col span={8} className={styles.main_icon}>
@ -64,7 +72,7 @@ export class Overview extends Component {
); );
renderActions() { renderActions() {
return actions.map((item) => ( return this.filterActions.map((item) => (
<Col span={6} key={item.key}> <Col span={6} key={item.key}>
<Link to={item.to}>{this.renderAction(item)}</Link> <Link to={item.to}>{this.renderAction(item)}</Link>
</Col> </Col>

View File

@ -15,6 +15,7 @@
import { inject, observer } from 'mobx-react'; import { inject, observer } from 'mobx-react';
import { ModalAction } from 'containers/Action'; import { ModalAction } from 'containers/Action';
import globalVolumeStore from 'stores/cinder/volume'; import globalVolumeStore from 'stores/cinder/volume';
import globalRootStore from 'stores/root';
export class CreateVolume extends ModalAction { export class CreateVolume extends ModalAction {
static id = 'create'; static id = 'create';
@ -30,7 +31,9 @@ export class CreateVolume extends ModalAction {
static allowed = (_, containerProps) => { static allowed = (_, containerProps) => {
const { isAdminPage } = containerProps; const { isAdminPage } = containerProps;
return Promise.resolve(!isAdminPage); return Promise.resolve(
globalRootStore.checkEndpoint('cinder') && !isAdminPage
);
}; };
getVolumeTypes() { getVolumeTypes() {

View File

@ -38,6 +38,10 @@ class Edit extends ModalAction {
return t('edit image'); return t('edit image');
} }
get enableCinder() {
return this.props.rootStore.checkEndpoint('cinder');
}
get labelCol() { get labelCol() {
return { return {
xs: { span: 8 }, xs: { span: 8 },
@ -127,6 +131,7 @@ class Edit extends ModalAction {
type: 'input-int', type: 'input-int',
min: 0, min: 0,
max: 500, max: 500,
display: this.enableCinder,
}, },
{ {
name: 'min_ram', name: 'min_ram',

View File

@ -328,6 +328,7 @@ export class BaseDetail extends Base {
} }
renderVolumeRow() { renderVolumeRow() {
if (!this.props.rootStore.checkEndpoint('cinder')) return null;
const { const {
match: { url }, match: { url },
} = this.props; } = this.props;

View File

@ -35,6 +35,10 @@ export class InstanceDetail extends Base {
return t('instance'); return t('instance');
} }
get enableCinder() {
return this.props.rootStore.checkEndpoint('cinder');
}
get policy() { get policy() {
return 'os_compute_api:servers:show'; return 'os_compute_api:servers:show';
} }
@ -119,11 +123,6 @@ export class InstanceDetail extends Base {
key: 'BaseDetail', key: 'BaseDetail',
component: BaseDetail, component: BaseDetail,
}, },
{
title: t('Volume'),
key: 'volumes',
component: Volumes,
},
{ {
title: t('Interface'), title: t('Interface'),
key: 'interface', key: 'interface',
@ -145,6 +144,13 @@ export class InstanceDetail extends Base {
component: ActionLog, component: ActionLog,
}, },
]; ];
if (this.enableCinder) {
tabs.splice(1, 0, {
title: t('Volume'),
key: 'volumes',
component: Volumes,
});
}
if (isIronicInstance(this.detailData)) { if (isIronicInstance(this.detailData)) {
return tabs.filter( return tabs.filter(
(it) => (it) =>

View File

@ -14,6 +14,7 @@
import { inject, observer } from 'mobx-react'; import { inject, observer } from 'mobx-react';
import globalServerStore from 'stores/nova/instance'; import globalServerStore from 'stores/nova/instance';
import globalRootStore from 'stores/root';
import { ModalAction } from 'containers/Action'; import { ModalAction } from 'containers/Action';
import { allowAttachVolumeInstance } from 'resources/instance'; import { allowAttachVolumeInstance } from 'resources/instance';
import { multiTip } from 'resources/volume'; import { multiTip } from 'resources/volume';
@ -56,7 +57,11 @@ export class AttachVolume extends ModalAction {
static allowed = (item, containerProps) => { static allowed = (item, containerProps) => {
const { isAdminPage } = containerProps; const { isAdminPage } = containerProps;
return Promise.resolve(!isAdminPage && allowAttachVolumeInstance(item)); return Promise.resolve(
globalRootStore.checkEndpoint('cinder') &&
!isAdminPage &&
allowAttachVolumeInstance(item)
);
}; };
get formItems() { get formItems() {

View File

@ -15,6 +15,7 @@
import { inject, observer } from 'mobx-react'; import { inject, observer } from 'mobx-react';
import { VolumeStore } from 'stores/cinder/volume'; import { VolumeStore } from 'stores/cinder/volume';
import globalServerStore from 'stores/nova/instance'; import globalServerStore from 'stores/nova/instance';
import globalRootStore from 'stores/root';
import { ModalAction } from 'containers/Action'; import { ModalAction } from 'containers/Action';
import { volumeStatus, isOsDisk } from 'resources/volume'; import { volumeStatus, isOsDisk } from 'resources/volume';
import { allowAttachVolumeInstance } from 'resources/instance'; import { allowAttachVolumeInstance } from 'resources/instance';
@ -63,7 +64,11 @@ export class DetachVolume extends ModalAction {
static allowed = (item, containerProps) => { static allowed = (item, containerProps) => {
const { isAdminPage } = containerProps; const { isAdminPage } = containerProps;
return Promise.resolve(!isAdminPage && allowAttachVolumeInstance(item)); return Promise.resolve(
globalRootStore.checkEndpoint('cinder') &&
!isAdminPage &&
allowAttachVolumeInstance(item)
);
}; };
get formItems() { get formItems() {

View File

@ -101,6 +101,10 @@ export class BaseStep extends Base {
})); }));
} }
get enableCinder() {
return this.props.rootStore.checkEndpoint('cinder');
}
get volumeTypes() { get volumeTypes() {
return (this.volumeTypeStore.list.data || []).map((it) => ({ return (this.volumeTypeStore.list.data || []).map((it) => ({
label: it.name, label: it.name,
@ -134,14 +138,15 @@ export class BaseStep extends Base {
get sourceTypes() { get sourceTypes() {
const { image, volume } = this.locationParams; const { image, volume } = this.locationParams;
return [ const types = [{ label: t('Image'), value: 'image', disabled: volume }];
{ label: t('Image'), value: 'image', disabled: volume }, if (this.enableCinder) {
{ types.push({
label: t('Bootable Volume'), label: t('Bootable Volume'),
value: 'bootableVolume', value: 'bootableVolume',
disabled: image, disabled: image,
}, });
]; }
return types;
} }
get imageSourceType() { get imageSourceType() {
@ -149,7 +154,9 @@ export class BaseStep extends Base {
} }
get volumeSourceType() { get volumeSourceType() {
return this.sourceTypes.find((it) => it.value === 'bootableVolume'); return this.enableCinder
? this.sourceTypes.find((it) => it.value === 'bootableVolume')
: {};
} }
allowed = () => Promise.resolve(); allowed = () => Promise.resolve();
@ -180,14 +187,19 @@ export class BaseStep extends Base {
} }
async getVolumeTypes() { async getVolumeTypes() {
if (this.enableCinder) {
await this.volumeTypeStore.fetchList(); await this.volumeTypeStore.fetchList();
} }
}
async getVolumes() { async getVolumes() {
const { image, volume } = this.locationParams; const { image, volume } = this.locationParams;
if (image) { if (image) {
return; return;
} }
if (!this.enableCinder) {
return;
}
if (volume) { if (volume) {
await this.volumeStore.fetchDetail({ await this.volumeStore.fetchDetail({
id: volume, id: volume,
@ -327,7 +339,7 @@ export class BaseStep extends Base {
} }
get showSystemDisk() { get showSystemDisk() {
return this.sourceTypeIsImage; return this.enableCinder && this.sourceTypeIsImage;
} }
getFlavorComponent() { getFlavorComponent() {
@ -420,7 +432,7 @@ export class BaseStep extends Base {
isLoading: this.volumeStore.list.isLoading, isLoading: this.volumeStore.list.isLoading,
required: this.sourceTypeIsVolume, required: this.sourceTypeIsVolume,
isMulti: false, isMulti: false,
display: this.sourceTypeIsVolume, display: this.sourceTypeIsVolume && this.enableCinder,
onChange: this.onBootableVolumeChange, onChange: this.onBootableVolumeChange,
filterParams: [ 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.' '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, onChange: this.onDataDiskChange,
display: this.enableCinder,
}, },
]; ];
} }

View File

@ -29,6 +29,10 @@ export class ConfirmStep extends Base {
return 'ConfirmStep'; return 'ConfirmStep';
} }
get enableCinder() {
return this.props.rootStore.checkEndpoint('cinder');
}
allowed = () => Promise.resolve(); allowed = () => Promise.resolve();
getDisk(diskInfo) { getDisk(diskInfo) {
@ -43,6 +47,7 @@ export class ConfirmStep extends Base {
} }
getSystemDisk() { getSystemDisk() {
if (!this.enableCinder) return null;
const { context } = this.props; const { context } = this.props;
const { systemDisk, source } = context; const { systemDisk, source } = context;
return source.value === 'bootableVolume' return source.value === 'bootableVolume'
@ -51,6 +56,7 @@ export class ConfirmStep extends Base {
} }
getDataDisk() { getDataDisk() {
if (!this.enableCinder) return null;
const { context } = this.props; const { context } = this.props;
const { dataDisk = [] } = context; const { dataDisk = [] } = context;
return dataDisk.map((it) => this.getDisk(it.value)); return dataDisk.map((it) => this.getDisk(it.value));
@ -175,22 +181,7 @@ export class ConfirmStep extends Base {
get formItems() { get formItems() {
const { context } = this.props; const { context } = this.props;
return [ let baseItems = [
{
name: 'confirm-count',
label: t('Count'),
type: 'label',
content: context.count || 1,
},
{
name: 'confirm-config',
label: t('Config Overview'),
type: 'descriptions',
title: t('Base Config'),
onClick: () => {
this.goStep(0);
},
items: [
// { // {
// label: t('Resource Pool'), // label: t('Resource Pool'),
// value: context.resource, // value: context.resource,
@ -223,7 +214,28 @@ export class ConfirmStep extends Base {
label: t('Flavor'), label: t('Flavor'),
value: this.getFlavor(), value: this.getFlavor(),
}, },
], ];
if (!this.enableCinder) {
baseItems = baseItems.filter(
(it) => ![t('System Disk'), t('Data Disk')].includes(it.label)
);
}
return [
{
name: 'confirm-count',
label: t('Count'),
type: 'label',
content: context.count || 1,
},
{
name: 'confirm-config',
label: t('Config Overview'),
type: 'descriptions',
title: t('Base Config'),
onClick: () => {
this.goStep(0);
},
items: baseItems,
}, },
{ {
type: 'short-divider', type: 'short-divider',

View File

@ -82,6 +82,10 @@ export class StepCreate extends StepAction {
return t('Create instance'); return t('Create instance');
} }
get enableCinder() {
return this.props.rootStore.checkEndpoint('cinder');
}
get listUrl() { get listUrl() {
const { image, volume, servergroup } = this.locationParams; const { image, volume, servergroup } = this.locationParams;
if (image) { if (image) {
@ -242,6 +246,7 @@ export class StepCreate extends StepAction {
} }
checkVolumeQuota() { checkVolumeQuota() {
if (!this.enableCinder) return '';
let msg = ''; let msg = '';
const { totalNewCount, totalNewSize, newCountMap, newSizeMap } = const { totalNewCount, totalNewSize, newCountMap, newSizeMap } =
this.getVolumeInputMap(); this.getVolumeInputMap();
@ -351,15 +356,21 @@ export class StepCreate extends StepAction {
source, source,
systemDisk, systemDisk,
} = values; } = values;
let imageRef = null;
let rootVolume = {};
const { value: sourceValue } = source; const { value: sourceValue } = source;
if (sourceValue !== 'bootableVolume') { const imageRef =
const { deleteType, type, size } = systemDisk; sourceValue === 'bootableVolume'
imageRef = ? null
sourceValue === 'image' : sourceValue === 'image'
? image.selectedRowKeys[0] ? image.selectedRowKeys[0]
: instanceSnapshot.selectedRowKeys[0]; : instanceSnapshot.selectedRowKeys[0];
if (!this.enableCinder) {
return {
imageRef,
};
}
let rootVolume = {};
if (sourceValue !== 'bootableVolume') {
const { deleteType, type, size } = systemDisk;
rootVolume = { rootVolume = {
boot_index: 0, boot_index: 0,
uuid: imageRef, uuid: imageRef,
@ -469,9 +480,11 @@ export class StepCreate extends StepAction {
name, name,
flavorRef: flavor.selectedRowKeys[0], flavorRef: flavor.selectedRowKeys[0],
availability_zone: availableZone.value, availability_zone: availableZone.value,
block_device_mapping_v2: volumes,
networks, networks,
}; };
if (this.enableCinder) {
server.block_device_mapping_v2 = volumes;
}
if (imageRef) { if (imageRef) {
server.imageRef = imageRef; server.imageRef = imageRef;
} }

View File

@ -23,6 +23,10 @@ import HeatService from './HeatService';
@inject('rootStore') @inject('rootStore')
@observer @observer
export default class Service extends Base { export default class Service extends Base {
get enableCinder() {
return this.props.rootStore.checkEndpoint('cinder');
}
get tabs() { get tabs() {
const tabs = [ const tabs = [
{ {
@ -35,11 +39,6 @@ export default class Service extends Base {
key: 'computeServices', key: 'computeServices',
component: ComputeService, component: ComputeService,
}, },
{
title: t('Block Storage Services'),
key: 'cinderService',
component: CinderService,
},
{ {
title: t('Neutron Agents'), title: t('Neutron Agents'),
key: 'neutronAgent', key: 'neutronAgent',
@ -51,6 +50,13 @@ export default class Service extends Base {
component: HeatService, component: HeatService,
}, },
]; ];
if (this.enableCinder) {
tabs.splice(3, 0, {
title: t('Block Storage Services'),
key: 'cinderService',
component: CinderService,
});
}
return tabs; return tabs;
} }
} }

View File

@ -26,18 +26,26 @@ export class Quota extends Component {
this.volumeTypeStore = new VolumeTypeStore(); this.volumeTypeStore = new VolumeTypeStore();
} }
get enableCinder() {
return this.props.rootStore.checkEndpoint('cinder');
}
get volumeTypeData() { get volumeTypeData() {
if (!this.enableCinder) return [];
return this.volumeTypeStore.projectVolumeTypes; return this.volumeTypeStore.projectVolumeTypes;
} }
getData = async () => { getData = async () => {
const { id: project_id } = this.props.match.params; const { id: project_id } = this.props.match.params;
return Promise.all([ const promiseArr = [
this.projectStore.fetchProjectQuota({ this.projectStore.fetchProjectQuota({
project_id, project_id,
}), }),
this.volumeTypeStore.fetchProjectVolumeTypes(project_id), ];
]); if (this.enableCinder) {
promiseArr.push(this.volumeTypeStore.fetchProjectVolumeTypes(project_id));
}
return Promise.all(promiseArr);
}; };
render() { render() {

View File

@ -38,14 +38,21 @@ export class QuotaManager extends ModalAction {
return t('Edit quota'); return t('Edit quota');
} }
get enableCinder() {
return this.props.rootStore.checkEndpoint('cinder');
}
async getData() { async getData() {
const { id: project_id } = this.item; const { id: project_id } = this.item;
await Promise.all([ const promiseArr = [
this.projectStore.fetchProjectQuota({ this.projectStore.fetchProjectQuota({
project_id, project_id,
}), }),
this.volumeTypeStore.fetchProjectVolumeTypes(project_id), ];
]); if (this.enableCinder) {
promiseArr.push(this.volumeTypeStore.fetchProjectVolumeTypes(project_id));
}
await Promise.all(promiseArr);
this.updateDefaultValue(); this.updateDefaultValue();
} }
@ -164,20 +171,21 @@ export class QuotaManager extends ModalAction {
get formItems() { get formItems() {
const computeFormItems = this.getComputeFormItems(); const computeFormItems = this.getComputeFormItems();
const cinderFormItems = this.getFormItemsByCards('storage');
const networkFormItems = this.getFormItemsByCards('networks'); const networkFormItems = this.getFormItemsByCards('networks');
const form = [...computeFormItems, ...networkFormItems];
if (this.enableCinder) {
const cinderFormItems = this.getFormItemsByCards('storage');
const volumeTypeFormItems = this.getVolumeTypeFormItems(); const volumeTypeFormItems = this.getVolumeTypeFormItems();
const form = [ form.splice(7, 0, ...cinderFormItems);
...computeFormItems, form.push(
...cinderFormItems,
...networkFormItems,
{ {
name: 'more', name: 'more',
label: t('Advanced Options'), label: t('Advanced Options'),
type: 'more', type: 'more',
}, },
...volumeTypeFormItems, ...volumeTypeFormItems
]; );
}
return form; return form;
} }

View File

@ -17,6 +17,7 @@ import { observer } from 'mobx-react';
import { OpenstackServiceStore } from 'stores/prometheus/openstack-service'; import { OpenstackServiceStore } from 'stores/prometheus/openstack-service';
import { SyncOutlined } from '@ant-design/icons'; import { SyncOutlined } from '@ant-design/icons';
import { Button } from 'antd'; import { Button } from 'antd';
import globalRootStore from 'stores/root';
import Services from './Services'; import Services from './Services';
import styles from './index.less'; import styles from './index.less';
@ -32,6 +33,10 @@ class OpenstackService extends Component {
this.getData(); this.getData();
} }
get enableCinder() {
return globalRootStore.checkEndpoint('cinder');
}
getData = async () => { getData = async () => {
// await this.store.getNodes(); // await this.store.getNodes();
await this.store.getChartData(); await this.store.getChartData();
@ -55,17 +60,19 @@ class OpenstackService extends Component {
title: t('Neutron Service'), title: t('Neutron Service'),
...network_service, ...network_service,
}, },
{
key: 'cinder_service',
title: t('Cinder Service'),
...cinder_service,
},
{ {
key: 'other_service', key: 'other_service',
title: t('Other Service'), title: t('Other Service'),
...other_service, ...other_service,
}, },
]; ];
if (this.enableCinder) {
serviceMap.splice(2, 0, {
key: 'cinder_service',
title: t('Cinder Service'),
...cinder_service,
});
}
return ( return (
<div className={styles.container}> <div className={styles.container}>

View File

@ -10,6 +10,7 @@ import {
import CircleChart from 'components/PrometheusChart/CircleWithRightLegend'; import CircleChart from 'components/PrometheusChart/CircleWithRightLegend';
import { handleResponses } from 'components/PrometheusChart/utils/dataHandler'; import { handleResponses } from 'components/PrometheusChart/utils/dataHandler';
import { ChartType } from 'components/PrometheusChart/utils/utils'; import { ChartType } from 'components/PrometheusChart/utils/utils';
import globalRootStore from 'stores/root';
import { import {
renderTopColumnChart, renderTopColumnChart,
renderTopColumnExtra, renderTopColumnExtra,
@ -372,6 +373,7 @@ export const storageLeftCardList = [
</div> </div>
); );
}, },
hidden: !globalRootStore.checkEndpoint('cinder'),
}, },
]; ];

View File

@ -17,6 +17,7 @@ import { getGBValue } from 'utils/index';
import { get, isNil, isEmpty } from 'lodash'; import { get, isNil, isEmpty } from 'lodash';
import client from 'client'; import client from 'client';
import Base from 'stores/base'; import Base from 'stores/base';
import globalRootStore from '../root';
export class ProjectStore extends Base { export class ProjectStore extends Base {
@observable @observable
@ -185,6 +186,10 @@ export class ProjectStore extends Base {
}; };
} }
get enableCinder() {
return globalRootStore.checkEndpoint('cinder');
}
@action @action
async enable({ id }) { async enable({ id }) {
const reqBody = { const reqBody = {
@ -274,15 +279,22 @@ export class ProjectStore extends Base {
@action @action
async fetchProjectQuota({ project_id }) { async fetchProjectQuota({ project_id }) {
const [novaResult, cinderResult, neutronResult] = await Promise.all([ const promiseArr = [
this.novaQuotaClient.detail(project_id), this.novaQuotaClient.detail(project_id),
this.cinderQuotaClient.show(project_id, { usage: 'True' }),
this.neutronQuotaClient.details(project_id), 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; this.isSubmitting = false;
const { quota_set: novaQuota } = novaResult; const { quota_set: novaQuota } = novaResult;
const { ram } = novaQuota; const { ram } = novaQuota;
const { quota_set: cinderQuota } = cinderResult; const { quota_set: cinderQuota = {} } = cinderResult;
const { quota: neutronQuota } = neutronResult; const { quota: neutronQuota } = neutronResult;
novaQuota.ram = { novaQuota.ram = {
in_use: getGBValue(ram.in_use), in_use: getGBValue(ram.in_use),
@ -335,6 +347,7 @@ export class ProjectStore extends Base {
} }
getCinderQuotaBody(data) { getCinderQuotaBody(data) {
if (!this.enableCinder) return {};
const { backups, ...others } = data; const { backups, ...others } = data;
const rest = {}; const rest = {};
Object.keys(others).forEach((key) => { Object.keys(others).forEach((key) => {

View File

@ -14,6 +14,7 @@
import { extendObservable, action } from 'mobx'; import { extendObservable, action } from 'mobx';
import client from 'client'; import client from 'client';
import globalRootStore from './root';
export default class OverviewStore { export default class OverviewStore {
constructor() { constructor() {
@ -90,6 +91,9 @@ export default class OverviewStore {
all_projects: true, all_projects: true,
status: 'SHUTOFF', status: 'SHUTOFF',
}), }),
];
if (globalRootStore.checkEndpoint('cinder')) {
const volumeResource = [
client.skyline.extension.volumes({ limit: 10, all_projects: true }), client.skyline.extension.volumes({ limit: 10, all_projects: true }),
client.skyline.extension.volumes({ client.skyline.extension.volumes({
limit: 10, limit: 10,
@ -107,6 +111,8 @@ export default class OverviewStore {
status: 'available', status: 'available',
}), }),
]; ];
promiseArray.push(...volumeResource);
}
const [ const [
allServers, allServers,
activeServers, activeServers,
@ -121,10 +127,6 @@ export default class OverviewStore {
const { count: activeServersCount } = activeServers; const { count: activeServersCount } = activeServers;
const { count: errorServersCount } = errorServers; const { count: errorServersCount } = errorServers;
const { count: shutoffServersCount } = shutoffServers; const { count: shutoffServersCount } = shutoffServers;
const { count: allVolumesCount } = allVolumes;
const { count: attachVolumesCount } = attachVolumes;
const { count: errorVolumesCount } = errorVolumes;
const { count: availableVolumesCount } = availableVolumes;
const serviceNum = { const serviceNum = {
all: allServersCount, all: allServersCount,
active: activeServersCount, active: activeServersCount,
@ -134,6 +136,12 @@ export default class OverviewStore {
allServersCount - allServersCount -
(activeServersCount + errorServersCount + shutoffServersCount), (activeServersCount + errorServersCount + shutoffServersCount),
}; };
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 = { const volumeNum = {
all: allVolumesCount, all: allVolumesCount,
active: attachVolumesCount, active: attachVolumesCount,
@ -143,7 +151,8 @@ export default class OverviewStore {
allVolumesCount - allVolumesCount -
(attachVolumesCount + errorVolumesCount + availableVolumesCount), (attachVolumesCount + errorVolumesCount + availableVolumesCount),
}; };
this.virtualResource = { serviceNum, volumeNum }; this.virtualResource.volumeNum = volumeNum;
}
this.virtualResourceLoading = false; this.virtualResourceLoading = false;
} }

View File

@ -57,14 +57,14 @@ describe('The Project Page', () => {
.clickModalActionSubmitButton(); .clickModalActionSubmitButton();
}); });
it('successfully manage user', () => { // it('successfully manage user', () => {
cy.tableSearchText(name) // cy.tableSearchText(name)
.clickActionInMore('Manage User') // .clickActionInMore('Manage User')
.formTransfer('select_user', username) // .formTransfer('select_user', username)
.formTransferRight('select_user', username) // .formTransferRight('select_user', username)
.formSelect('select_user', 'admin') // .formSelect('select_user', 'admin')
.clickModalActionSubmitButton(); // .clickModalActionSubmitButton();
}); // });
it('successfully manage user group', () => { it('successfully manage user group', () => {
cy.tableSearchText(name) cy.tableSearchText(name)

View File

@ -75,14 +75,14 @@ describe('The User Page', () => {
cy.goBackToList(listUrl); cy.goBackToList(listUrl);
}); });
it('successfully edit system permission', () => { // it('successfully edit system permission', () => {
cy.tableSearchText(name) // cy.tableSearchText(name)
.clickActionInMore('Edit System Permission') // .clickActionInMore('Edit System Permission')
.formTransfer('select_project', projectName2) // .formTransfer('select_project', projectName2)
.formTransferRight('select_project', projectName2) // .formTransferRight('select_project', projectName2)
.formSelect('select_project', 'admin') // .formSelect('select_project', 'admin')
.clickModalActionSubmitButton(); // .clickModalActionSubmitButton();
}); // });
it('successfully forbidden user', () => { it('successfully forbidden user', () => {
cy.tableSearchText(name).clickConfirmActionInMore('Forbidden'); cy.tableSearchText(name).clickConfirmActionInMore('Forbidden');