From 317a81acc44b6e509c5352acb4a4a9b7256d8583 Mon Sep 17 00:00:00 2001 From: xusongfu Date: Tue, 10 Jan 2023 19:10:26 +0800 Subject: [PATCH] feature: support search filters and fix image in zun ui 1. support search filters to capsules 2. support search filters to containers 3. support search filters to hosts 4. support search filters to services 5. because the glance image's name can be repeated, change params from name to id if use glance image Change-Id: If3ceed8d0027f230afd72784c48161048fe61faf --- src/locales/en.json | 2 +- src/locales/zh.json | 2 +- .../containers/Capsules/index.jsx | 15 +++++ .../Containers/Detail/BaseDetail.jsx | 18 +++++- .../actions/StepCreate/StepInfo/index.jsx | 60 ++++++++----------- .../actions/StepCreate/StepSpec/index.jsx | 41 +++++-------- .../Containers/actions/StepCreate/index.jsx | 15 +++-- .../containers/Containers/index.jsx | 37 ++++++++++-- .../containers/Hosts/index.jsx | 9 +++ .../containers/Services/index.jsx | 15 +++++ src/resources/zun/container.js | 14 ++++- src/stores/zun/containers.js | 11 +++- 12 files changed, 161 insertions(+), 78 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 0b68de03..66b5f738 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -2352,7 +2352,7 @@ "The name of the physical network to which a port is connected": "The name of the physical network to which a port is connected", "The name should contain letter or number, the length is 1 to 16, characters can only contain \"0-9, a-z, A-Z, -, _.\"": "The name should contain letter or number, the length is 1 to 16, characters can only contain \"0-9, a-z, A-Z, -, _.\"", "The name should contain letter or number, the length is 2 to 64, characters can only contain \"0-9, a-z, A-Z, -, _.\"": "The name should contain letter or number, the length is 2 to 64, characters can only contain \"0-9, a-z, A-Z, -, _.\"", - "The name should start with letter or number, characters can only contain \"0-9, a-z, A-Z, -, _, .\"": "The name should start with letter or number, characters can only contain \"0-9, a-z, A-Z, -, _, .\"", + "The name should start with letter or number, and be a string of 2 to 255, characters can only contain \"0-9, a-z, A-Z, -, _, .\"": "The name should start with letter or number, and be a string of 2 to 255, characters can only contain \"0-9, a-z, A-Z, -, _, .\"", "The name should start with upper letter or lower letter, and be a string of 1 to 128, characters can only contain \"0-9, a-z, A-Z, \"-'_()[].:^\".": "The name should start with upper letter or lower letter, and be a string of 1 to 128, characters can only contain \"0-9, a-z, A-Z, \"-'_()[].:^\".", "The name should start with upper letter or lower letter, characters can only contain \"0-9, a-z, A-Z, -, _, .\"": "The name should start with upper letter or lower letter, characters can only contain \"0-9, a-z, A-Z, -, _, .\"", "The name should start with upper letter, lower letter or chinese, and be a string of 1 to 128, characters can only contain \"0-9, a-z, A-Z, \"-'_()[].\".": "The name should start with upper letter, lower letter or chinese, and be a string of 1 to 128, characters can only contain \"0-9, a-z, A-Z, \"-'_()[].\".", diff --git a/src/locales/zh.json b/src/locales/zh.json index d704eb60..c8f72544 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -2352,7 +2352,7 @@ "The name of the physical network to which a port is connected": "端口连接到的物理网络的名称", "The name should contain letter or number, the length is 1 to 16, characters can only contain \"0-9, a-z, A-Z, -, _.\"": "名称应包含字母或数字,长度为 1 到 16,且字符只能包含“0-9、a-z、A-Z、-、_”。", "The name should contain letter or number, the length is 2 to 64, characters can only contain \"0-9, a-z, A-Z, -, _.\"": "名称应包含字母或数字,长度为 2 到 64,且字符只能包含“0-9、a-z、A-Z、-、_”。", - "The name should start with letter or number, characters can only contain \"0-9, a-z, A-Z, -, _, .\"": "名称应以字母或数字开头,且只包含“0-9, a-z, A-Z, -, _, .”。", + "The name should start with letter or number, and be a string of 2 to 255, characters can only contain \"0-9, a-z, A-Z, -, _, .\"": "名称应以字母或数字开头,长度为 2 到 255,且只包含“0-9, a-z, A-Z, -, _, .”。", "The name should start with upper letter or lower letter, and be a string of 1 to 128, characters can only contain \"0-9, a-z, A-Z, \"-'_()[].:^\".": "名称应以大写字母或小写字母开头,最长为128字符,且只包含“0-9, a-z, A-Z, \"'-_()[].:^”。", "The name should start with upper letter or lower letter, characters can only contain \"0-9, a-z, A-Z, -, _, .\"": "名称应以大写字母或小写字母开头,且字符只能包含“0-9、a-z、A-Z、-、_、.”。", "The name should start with upper letter, lower letter or chinese, and be a string of 1 to 128, characters can only contain \"0-9, a-z, A-Z, \"-'_()[].\".": "名称应以大写字母,小写字母或中文开头,最长为128字符,且只包含“0-9, a-z, A-Z, \"'-_()[].”。", diff --git a/src/pages/container-service/containers/Capsules/index.jsx b/src/pages/container-service/containers/Capsules/index.jsx index 029d3afb..663a60c1 100644 --- a/src/pages/container-service/containers/Capsules/index.jsx +++ b/src/pages/container-service/containers/Capsules/index.jsx @@ -16,6 +16,7 @@ import Base from 'containers/List'; import { inject, observer } from 'mobx-react'; import globalCapsulesStore from 'stores/zun/capsules'; import { capsuleStatus } from 'resources/zun/capsule'; +import { getOptions } from 'utils'; import actionConfigs from './actions'; export class Capsules extends Base { @@ -63,6 +64,20 @@ export class Capsules extends Base { }, ]; } + + get searchFilters() { + return [ + { + label: t('Name'), + name: 'name', + }, + { + label: t('Status'), + name: 'status', + options: getOptions(capsuleStatus), + }, + ]; + } } export default inject('rootStore')(observer(Capsules)); diff --git a/src/pages/container-service/containers/Containers/Detail/BaseDetail.jsx b/src/pages/container-service/containers/Containers/Detail/BaseDetail.jsx index 524ce49d..8ecf7f00 100644 --- a/src/pages/container-service/containers/Containers/Detail/BaseDetail.jsx +++ b/src/pages/container-service/containers/Containers/Detail/BaseDetail.jsx @@ -15,7 +15,11 @@ import Base from 'containers/BaseDetail'; import React from 'react'; import { inject, observer } from 'mobx-react'; -import { containerStatus } from 'resources/zun/container'; +import { + containerStatus, + imageDrivers, + exitPolicies, +} from 'resources/zun/container'; import { stringifyContent } from 'utils/content'; import { isEmpty } from 'lodash'; @@ -34,14 +38,22 @@ export class BaseDetail extends Base { } get baseInfoCard() { + const { image, imageInfo } = this.detailData || {}; + const imageUrl = imageInfo + ? this.getLinkRender('imageDetail', imageInfo.name, { + id: imageInfo.id, + }) + : image; + const options = [ { label: t('Image'), - dataIndex: 'image', + content: imageUrl, }, { label: t('Image Driver'), dataIndex: 'image_driver', + valueMap: imageDrivers, }, { label: t('Status Detail'), @@ -135,7 +147,7 @@ export class BaseDetail extends Base { return (

- {t('Name')}: {Name} + {t('Name')}: {exitPolicies[Name]}

{t('Max Retry')}: {MaximumRetryCount} diff --git a/src/pages/container-service/containers/Containers/actions/StepCreate/StepInfo/index.jsx b/src/pages/container-service/containers/Containers/actions/StepCreate/StepInfo/index.jsx index 973f5e19..0bb652cf 100644 --- a/src/pages/container-service/containers/Containers/actions/StepCreate/StepInfo/index.jsx +++ b/src/pages/container-service/containers/Containers/actions/StepCreate/StepInfo/index.jsx @@ -12,13 +12,13 @@ import Base from 'components/Form'; import { inject, observer } from 'mobx-react'; -import globalImageStore from 'src/stores/glance/image'; +import { ImageStore } from 'stores/glance/image'; import { getImageColumns } from 'resources/glance/image'; -import { toJS } from 'mobx'; +import { imageDrivers } from 'resources/zun/container'; export class StepInfo extends Base { init() { - this.getImageList(); + this.imageStore = new ImageStore(); } get title() { @@ -29,24 +29,21 @@ export class StepInfo extends Base { return t('Info'); } - async getImageList() { - await globalImageStore.fetchList(); - } - - get imageList() { - return toJS(globalImageStore.list.data || []).filter( - (it) => it.container_format === 'docker' - ); - } - get imageColumns() { return getImageColumns(this).filter( (it) => !['project_name', 'owner'].includes(it.dataIndex) ); } + get imageDriverOptions() { + return Object.entries(imageDrivers).map(([k, v]) => ({ + label: v, + value: k, + })); + } + get formItems() { - const { imageDriver } = this.state; + const { context: { image_driver } = {} } = this.props; return [ { @@ -61,7 +58,7 @@ export class StepInfo extends Base { return Promise.reject( value ? t( - 'The name should start with letter or number, characters can only contain "0-9, a-z, A-Z, -, _, ."' + 'The name should start with letter or number, and be a string of 2 to 255, characters can only contain "0-9, a-z, A-Z, -, _, ."' ) : '' ); @@ -74,38 +71,29 @@ export class StepInfo extends Base { label: t('Image Driver'), placeholder: t('Please select image driver'), type: 'select', - options: [ - { - label: t('Docker Hub'), - value: 'docker', - }, - { - label: t('Glance Image'), - value: 'glance', - }, - ], - onChange: (value) => { - this.setState({ - imageDriver: value, - }); - }, + options: this.imageDriverOptions, + onChange: (value) => + this.updateContext({ + image_driver: value, + }), required: true, }, { - name: 'image', + name: 'imageDocker', label: t('Image'), type: 'input', placeholder: t('Please input image'), required: true, - display: imageDriver === 'docker', + display: image_driver === 'docker', }, { - name: 'image', + name: 'imageGlance', label: t('Image'), type: 'select-table', - data: this.imageList, required: true, - isLoading: globalImageStore.list.isLoading, + backendPageStore: this.imageStore, + extraParams: { container_format: 'docker' }, + isLoading: this.imageStore.list.isLoading, filterParams: [ { label: t('Name'), @@ -113,7 +101,7 @@ export class StepInfo extends Base { }, ], columns: this.imageColumns, - display: imageDriver === 'glance', + display: image_driver === 'glance', }, ]; } diff --git a/src/pages/container-service/containers/Containers/actions/StepCreate/StepSpec/index.jsx b/src/pages/container-service/containers/Containers/actions/StepCreate/StepSpec/index.jsx index d707674e..8e73c9fe 100644 --- a/src/pages/container-service/containers/Containers/actions/StepCreate/StepSpec/index.jsx +++ b/src/pages/container-service/containers/Containers/actions/StepCreate/StepSpec/index.jsx @@ -13,6 +13,7 @@ import Base from 'components/Form'; import { inject, observer } from 'mobx-react'; import globalAvailabilityZoneStore from 'stores/nova/zone'; +import { exitPolicies } from 'resources/zun/container'; import ExposedPorts from '../../../components/ExposedPorts'; export class StepSpec extends Base { @@ -42,6 +43,13 @@ export class StepSpec extends Base { })); } + get exitPoliciesOptions() { + return Object.entries(exitPolicies).map(([k, v]) => ({ + label: v, + value: k, + })); + } + exposedPortValidator = (rule, value) => { const ifHaveEmpty = (value || []).some((it) => { const { value: innerValue } = it; @@ -57,7 +65,8 @@ export class StepSpec extends Base { }; get formItems() { - const { disableRetry, healthcheck } = this.state; + const { context: { exitPolicy, healthcheck } = {} } = this.props; + const disableRetry = exitPolicy !== 'on-failure'; return [ { @@ -103,27 +112,10 @@ export class StepSpec extends Base { name: 'exitPolicy', label: t('Exit Policy'), type: 'select', - options: [ - { - label: t('No'), - value: 'no', - }, - { - label: t('On failure'), - value: 'on-failure', - }, - { - label: t('Always'), - value: 'always', - }, - { - label: t('Unless Stopped'), - value: 'unless-stopped', - }, - ], + options: this.exitPoliciesOptions, onChange: (value) => - this.setState({ - disableRetry: value !== 'on-failure', + this.updateContext({ + exitPolicy: value, }), }, { @@ -153,11 +145,10 @@ export class StepSpec extends Base { name: 'healthcheck', label: t('Enable Health Check'), type: 'check', - onChange: (value) => { - this.setState({ + onChange: (value) => + this.updateContext({ healthcheck: value, - }); - }, + }), }, { name: 'healthcheck_cmd', diff --git a/src/pages/container-service/containers/Containers/actions/StepCreate/index.jsx b/src/pages/container-service/containers/Containers/actions/StepCreate/index.jsx index 0b1f0519..b7726aa6 100644 --- a/src/pages/container-service/containers/Containers/actions/StepCreate/index.jsx +++ b/src/pages/container-service/containers/Containers/actions/StepCreate/index.jsx @@ -15,7 +15,7 @@ import { message as $message } from 'antd'; import { StepAction } from 'src/containers/Action'; import globalContainersStore from 'stores/zun/containers'; import globalProjectStore from 'stores/keystone/project'; -import { isEmpty, isObject } from 'lodash'; +import { isEmpty } from 'lodash'; import StepInfo from './StepInfo'; import StepSpec from './StepSpec'; import StepVolumes from './StepVolumes'; @@ -227,7 +227,9 @@ export class StepCreate extends StepAction { environmentVariables, labels, mounts, - image, + image_driver, + imageDocker, + imageGlance, exitPolicy, maxRetry, networks, @@ -245,6 +247,7 @@ export class StepCreate extends StepAction { } = values; const body = { + image_driver, ...rest, }; @@ -359,8 +362,12 @@ export class StepCreate extends StepAction { body.entrypoint = [entrypoint]; } - if (image) { - body.image = isObject(image) ? (image.selectedRows[0] || {}).name : image; + if (imageDocker && image_driver === 'docker') { + body.image = imageDocker; + } + + if (imageGlance && image_driver === 'glance') { + body.image = imageGlance.selectedRowKeys[0]; } if (exitPolicy) { diff --git a/src/pages/container-service/containers/Containers/index.jsx b/src/pages/container-service/containers/Containers/index.jsx index f5c51f38..23badd3d 100644 --- a/src/pages/container-service/containers/Containers/index.jsx +++ b/src/pages/container-service/containers/Containers/index.jsx @@ -15,7 +15,12 @@ import Base from 'containers/List'; import { inject, observer } from 'mobx-react'; import globalContainersStore from 'src/stores/zun/containers'; -import { containerStatus, containerTaskStatus } from 'resources/zun/container'; +import { + containerStatus, + containerTaskStatus, + imageDrivers, +} from 'resources/zun/container'; +import { getOptions } from 'utils'; import actionConfigs from './actions'; export class Containers extends Base { @@ -48,17 +53,18 @@ export class Containers extends Base { routeName: this.getRouteName('zunContainerDetail'), idKey: 'uuid', }, + { + title: t('Image Driver'), + isHideable: true, + dataIndex: 'image_driver', + valueMap: imageDrivers, + }, { title: t('Status'), isHideable: true, dataIndex: 'status', valueMap: containerStatus, }, - { - title: t('Image'), - isHideable: true, - dataIndex: 'image', - }, { title: t('Task State'), isHideable: true, @@ -67,6 +73,25 @@ export class Containers extends Base { }, ]; } + + get searchFilters() { + return [ + { + label: t('Name'), + name: 'name', + }, + { + label: t('Status'), + name: 'status', + options: getOptions(containerStatus), + }, + { + label: t('Task State'), + name: 'task_state', + options: getOptions(containerTaskStatus), + }, + ]; + } } export default inject('rootStore')(observer(Containers)); diff --git a/src/pages/container-service/containers/Hosts/index.jsx b/src/pages/container-service/containers/Hosts/index.jsx index dc83982c..8b83e579 100644 --- a/src/pages/container-service/containers/Hosts/index.jsx +++ b/src/pages/container-service/containers/Hosts/index.jsx @@ -96,6 +96,15 @@ export class Hosts extends Base { }, ]; } + + get searchFilters() { + return [ + { + label: t('Name'), + name: 'name', + }, + ]; + } } export default inject('rootStore')(observer(Hosts)); diff --git a/src/pages/container-service/containers/Services/index.jsx b/src/pages/container-service/containers/Services/index.jsx index 92a9cc9d..ea27f9dd 100644 --- a/src/pages/container-service/containers/Services/index.jsx +++ b/src/pages/container-service/containers/Services/index.jsx @@ -17,6 +17,7 @@ import Base from 'containers/List'; import { inject, observer } from 'mobx-react'; import globalServicesStore from 'src/stores/zun/services'; import { serviceState } from 'resources/nova/service'; +import { getOptions } from 'utils'; export class Services extends Base { init() { @@ -78,6 +79,20 @@ export class Services extends Base { }, ]; } + + get searchFilters() { + return [ + { + label: t('Name'), + name: 'name', + }, + { + label: t('Service State'), + name: 'state', + options: getOptions(serviceState), + }, + ]; + } } export default inject('rootStore')(observer(Services)); diff --git a/src/resources/zun/container.js b/src/resources/zun/container.js index 6b64550e..67d56552 100644 --- a/src/resources/zun/container.js +++ b/src/resources/zun/container.js @@ -26,7 +26,7 @@ export const containerStatus = { }; export const containerTaskStatus = { - null: t('No Task'), + free: t('No Task'), container_creating: t('Container Creating'), container_starting: t('Container Starting'), container_stopping: t('Container Stopping'), @@ -102,3 +102,15 @@ export const checkItemAction = (item, actionName) => { const { status } = item; return validStates[actionName].includes(status); }; + +export const imageDrivers = { + docker: t('Docker Hub'), + glance: t('Glance Image'), +}; + +export const exitPolicies = { + no: t('No'), + 'on-failure': t('On failure'), + always: t('Always'), + 'unless-stopped': t('Unless Stopped'), +}; diff --git a/src/stores/zun/containers.js b/src/stores/zun/containers.js index 8039b4ac..b75251cf 100644 --- a/src/stores/zun/containers.js +++ b/src/stores/zun/containers.js @@ -22,10 +22,15 @@ export class ContainersStore extends Base { return client.zun.containers; } + get imageClient() { + return client.glance.images; + } + get mapper() { return (data) => ({ ...data, id: data.uuid, + task_state: data.task_state === null ? 'free' : data.task_state, }); } @@ -85,7 +90,7 @@ export class ContainersStore extends Base { } async detailDidFetch(item) { - const { uuid, status, addresses = {} } = item; + const { uuid, status, addresses = {}, image_driver, image } = item; let stats = {}; if (status === 'Running') { stats = (await this.client.stats.list(uuid)) || {}; @@ -100,6 +105,10 @@ export class ContainersStore extends Base { }, []) .map((it) => it.port); } + if (image_driver === 'glance') { + const info = await this.imageClient.show(image); + item.imageInfo = info; + } return { ...item, stats, networks, ports }; }