diff --git a/src/locales/en.json b/src/locales/en.json index e96bf061..6885335c 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -915,7 +915,6 @@ "External IP(V4)": "External IP(V4)", "External IP(V6)": "External IP(V6)", "External Network": "External Network", - "External Network ID": "External Network ID", "External Network Info": "External Network Info", "External Networks": "External Networks", "External Port": "External Port", @@ -945,9 +944,7 @@ "Fixed IP Address": "Fixed IP Address", "Fixed IPs": "Fixed IPs", "Fixed Network": "Fixed Network", - "Fixed Network ID": "Fixed Network ID", "Fixed Subnet": "Fixed Subnet", - "Fixed Subnet ID": "Fixed Subnet ID", "Flavor": "Flavor", "Flavor Detail": "Flavor Detail", "Flavor Info": "Flavor Info", @@ -1425,7 +1422,7 @@ "Martinique": "Martinique", "Master Count": "Master Count", "Master Node Addresses": "Master Node Addresses", - "Master Node Flavor ID": "Master Node Flavor ID", + "Master Node Flavor": "Master Node Flavor", "Master Node LB Enabled": "Master Node LB Enabled", "Mauritania": "Mauritania", "Mauritius": "Mauritius", @@ -1565,7 +1562,7 @@ "Node Addresses": "Node Addresses", "Node Count": "Node Count", "Node Driver": "Node Driver", - "Node Flavor ID": "Node Flavor ID", + "Node Flavor": "Node Flavor", "Node ID/Name": "Node ID/Name", "Node Info": "Node Info", "Node Name": "Node Name", @@ -2505,7 +2502,6 @@ "Usage Type": "Usage Type", "Usb Controller": "Usb Controller", "Use Type": "Use Type", - "Use an Existing Network": "Use an Existing Network", "Used": "Used", "Used IPs": "Used IPs", "Used by tunnel(s): {names}. ID(s): {ids}": "Used by tunnel(s): {names}. ID(s): {ids}", diff --git a/src/locales/zh.json b/src/locales/zh.json index 41e8e15e..31d9cdcc 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -915,7 +915,6 @@ "External IP(V4)": "外网IP(V4)", "External IP(V6)": "外网IP(V6)", "External Network": "外部网络", - "External Network ID": "外部网络ID", "External Network Info": "外部网络信息", "External Networks": "外部网络", "External Port": "源端口", @@ -945,9 +944,7 @@ "Fixed IP Address": "内网IP地址", "Fixed IPs": "内网IP", "Fixed Network": "内网", - "Fixed Network ID": "内网ID", "Fixed Subnet": "内网子网", - "Fixed Subnet ID": "内网子网ID", "Flavor": "云主机类型", "Flavor Detail": "云主机类型详情", "Flavor Info": "配置信息", @@ -1425,7 +1422,7 @@ "Martinique": "马提尼克岛", "Master Count": "主数量", "Master Node Addresses": "主节点地址", - "Master Node Flavor ID": "主节点类型ID", + "Master Node Flavor": "主节点类型", "Master Node LB Enabled": "启用主节点负载均衡", "Mauritania": "毛里塔尼亚", "Mauritius": "毛里求斯", @@ -1565,7 +1562,7 @@ "Node Addresses": "节点地址", "Node Count": "节点数量", "Node Driver": "节点驱动", - "Node Flavor ID": "节点类型ID", + "Node Flavor": "节点类型", "Node ID/Name": "节点ID/名称", "Node Info": "节点信息", "Node Name": "节点名称", @@ -2505,7 +2502,6 @@ "Usage Type": "使用类型", "Usb Controller": "USB控制器", "Use Type": "使用类型", - "Use an Existing Network": "使用现有网络", "Used": "已使用", "Used IPs": "已用IP", "Used by tunnel(s): {names}. ID(s): {ids}": "被隧道使用中:{names}。 ID:{ids}", diff --git a/src/pages/container-infra/containers/ClusterTemplates/Detail/BaseDetail.jsx b/src/pages/container-infra/containers/ClusterTemplates/Detail/BaseDetail.jsx index 898dfb60..3ec41a66 100644 --- a/src/pages/container-infra/containers/ClusterTemplates/Detail/BaseDetail.jsx +++ b/src/pages/container-infra/containers/ClusterTemplates/Detail/BaseDetail.jsx @@ -68,17 +68,35 @@ export class BaseDetail extends Base { } get networkCard() { - const { external_network_id, fixed_network } = this.detailData || {}; + const { + external_network_id, + externalNetwork: { name: externalName } = {}, + fixed_network, + fixedNetwork: { name: fixedName } = {}, + fixed_subnet, + fixedSubnet: { name: subName } = {}, + } = this.detailData || {}; const externalNetworkUrl = external_network_id - ? this.getLinkRender('networkDetail', external_network_id, { - id: external_network_id, - }) + ? this.getLinkRender( + 'networkDetail', + externalName || external_network_id, + { + id: external_network_id, + } + ) : '-'; const fixedNetworkUrl = fixed_network - ? this.getLinkRender('networkDetail', fixed_network, { + ? this.getLinkRender('networkDetail', fixedName || fixed_network, { id: fixed_network, }) : '-'; + const subnetUrl = + fixed_network && fixed_subnet + ? this.getLinkRender('subnetDetail', subName || fixed_subnet, { + networkId: fixed_network, + id: fixed_subnet, + }) + : '-'; const options = [ { @@ -98,16 +116,16 @@ export class BaseDetail extends Base { dataIndex: 'no_proxy', }, { - label: t('External Network ID'), + label: t('External Network'), content: externalNetworkUrl, }, { - label: t('Fixed Network ID'), + label: t('Fixed Network'), content: fixedNetworkUrl, }, { - label: t('Fixed Subnet ID'), - dataIndex: 'fixed_subnet', + label: t('Fixed Subnet'), + content: subnetUrl, }, { label: t('DNS'), @@ -132,10 +150,18 @@ export class BaseDetail extends Base { } get specCard() { - const { image_id, keypair_id, flavor_id, master_flavor_id, selfKeypair } = - this.detailData; + const { + image_id, + image: { name: imageName } = {}, + keypair_id, + flavor_id, + flavor: { name: flavorName } = {}, + master_flavor_id, + masterFlavor: { name: masterFlavorName } = {}, + selfKeypair, + } = this.detailData; const imageUrl = image_id - ? this.getLinkRender('imageDetail', image_id, { + ? this.getLinkRender('imageDetail', imageName || image_id, { id: image_id, }) : '-'; @@ -148,20 +174,24 @@ export class BaseDetail extends Base { : keypair_id || '-'; const flavorUrl = flavor_id - ? this.getLinkRender('flavorDetail', flavor_id, { + ? this.getLinkRender('flavorDetail', flavorName || flavor_id, { id: flavor_id, }) : '-'; const masterFlavorUrl = master_flavor_id - ? this.getLinkRender('flavorDetail', master_flavor_id, { - id: master_flavor_id, - }) + ? this.getLinkRender( + 'flavorDetail', + masterFlavorName || master_flavor_id, + { + id: master_flavor_id, + } + ) : '-'; const options = [ { - label: t('Image ID'), + label: t('Image'), content: imageUrl, }, { @@ -170,11 +200,11 @@ export class BaseDetail extends Base { hidden: this.isAdminPage, }, { - label: t('Node Flavor ID'), + label: t('Node Flavor'), content: flavorUrl, }, { - label: t('Master Node Flavor ID'), + label: t('Master Node Flavor'), content: masterFlavorUrl, }, { diff --git a/src/pages/container-infra/containers/ClusterTemplates/actions/StepCreate/StepNetwork/index.jsx b/src/pages/container-infra/containers/ClusterTemplates/actions/StepCreate/StepNetwork/index.jsx index cb5e4cbe..382e14fa 100644 --- a/src/pages/container-infra/containers/ClusterTemplates/actions/StepCreate/StepNetwork/index.jsx +++ b/src/pages/container-infra/containers/ClusterTemplates/actions/StepCreate/StepNetwork/index.jsx @@ -18,7 +18,7 @@ import { inject, observer } from 'mobx-react'; import { NetworkStore } from 'src/stores/neutron/network'; import { SubnetStore } from 'src/stores/neutron/subnet'; import { getLinkRender } from 'utils/route-map'; -import { networkColumns } from 'resources/neutron/network'; +import { networkColumns, subnetColumns } from 'resources/neutron/network'; export class StepNetwork extends Base { async init() { @@ -87,8 +87,11 @@ export class StepNetwork extends Base { https_proxy, no_proxy, external_network_id, + externalNetwork, fixed_network, + fixedNetwork, fixed_subnet, + fixedSubnet, dns_nameserver, master_lb_enabled, floating_ip_enabled, @@ -101,21 +104,22 @@ export class StepNetwork extends Base { no_proxy, externalNetwork: { selectedRowKeys: [external_network_id], + selectedRows: [externalNetwork], }, - fixed_network, - fixed_subnet, dns_nameserver, master_lb_enabled, floating_ip_enabled, }; if (fixed_network) { - values.fixedNetwork = this.props.context.fixedNetwork || { + values.fixedNetwork = { selectedRowKeys: [fixed_network], + selectedRows: [fixedNetwork], }; } if (fixed_subnet) { values.fixedSubnet = { selectedRowKeys: [fixed_subnet], + selectedRows: [fixedSubnet], }; } } @@ -201,7 +205,10 @@ export class StepNetwork extends Base { this.updateContext({ fixedNetwork: value, }); - this.updateFormValue('fixedSubnet', null); + this.updateFormValue('fixedSubnet', { + selectedRowKeys: [], + selectedRows: [], + }); }, }, { @@ -215,29 +222,7 @@ export class StepNetwork extends Base { name: 'name', }, ], - columns: [ - { - title: t('Name'), - dataIndex: 'name', - }, - { - title: t('CIDR'), - dataIndex: 'cidr', - }, - { - title: t('Gateway IP'), - dataIndex: 'gateway_ip', - }, - { - title: t('IP Version'), - dataIndex: 'ip_version', - }, - { - title: t('Created At'), - dataIndex: 'created_at', - valueRender: 'toLocalTime', - }, - ], + columns: subnetColumns, }, { name: 'dns_nameserver', diff --git a/src/pages/container-infra/containers/ClusterTemplates/actions/StepCreate/StepNodeSpec/index.jsx b/src/pages/container-infra/containers/ClusterTemplates/actions/StepCreate/StepNodeSpec/index.jsx index dc224154..76b08078 100644 --- a/src/pages/container-infra/containers/ClusterTemplates/actions/StepCreate/StepNodeSpec/index.jsx +++ b/src/pages/container-infra/containers/ClusterTemplates/actions/StepCreate/StepNodeSpec/index.jsx @@ -12,21 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -import React from 'react'; import { inject, observer } from 'mobx-react'; +import { toJS } from 'mobx'; import Base from 'components/Form'; import { ImageStore } from 'stores/glance/image'; import globalKeypairStore from 'stores/nova/keypair'; -import FlavorSelectTable from 'pages/compute/containers/Instance/components/FlavorSelectTable'; +import { FlavorStore } from 'src/stores/nova/flavor'; import { getImageColumns } from 'resources/glance/image'; import { getKeyPairHeader } from 'resources/nova/keypair'; +import { getBaseSimpleFlavorColumns } from 'resources/magnum/template'; export class StepNodeSpec extends Base { init() { this.imageStore = new ImageStore(); this.keyPairStore = globalKeypairStore; - this.getImageList(); - this.getKeypairs(); + this.flavorStore = new FlavorStore(); + this.masterFlavorStore = new FlavorStore(); + this.getAllInitFunctions(); } get title() { @@ -45,19 +47,44 @@ export class StepNodeSpec extends Base { return !!this.props.extra; } - async getImageList() { - await this.imageStore.fetchList({ all_projects: this.hasAdminRole }); + async getAllInitFunctions() { + await Promise.all([ + this.getImageList(), + this.getKeypairs(), + this.getFlavors(), + this.getMasterFlavors(), + ]); this.updateDefaultValue(); } - async getKeypairs() { - await this.keyPairStore.fetchList(); + getImageList() { + return this.imageStore.fetchList({ all_projects: this.hasAdminRole }); + } + + getKeypairs() { + return this.keyPairStore.fetchList(); } get keypairs() { return this.keyPairStore.list.data || []; } + getFlavors() { + return this.flavorStore.fetchList(); + } + + getMasterFlavors() { + return this.masterFlavorStore.fetchList(); + } + + get flavors() { + return toJS(this.flavorStore.list.data) || []; + } + + get masterFlavors() { + return toJS(this.masterFlavorStore.list.data) || []; + } + get acceptedImageOs() { const { context: { coe = '' } = {} } = this.props; let acceptedOs = []; @@ -93,26 +120,6 @@ export class StepNodeSpec extends Base { return acceptedVolumeDriver; } - getFlavorComponent() { - return ; - } - - onFlavorChange = (value) => { - this.updateContext({ - flavor: value, - }); - }; - - getMasterFlavorComponent() { - return ; - } - - onMasterFlavorChange = (value) => { - this.updateContext({ - masterFlavor: value, - }); - }; - get defaultValue() { let values = {}; @@ -134,10 +141,18 @@ export class StepNodeSpec extends Base { docker_volume_size, }; if (flavor_id) { - values.flavor = { selectedRowKeys: [flavor_id] }; + values.flavor = { + selectedRowKeys: [flavor_id], + selectedRows: this.flavors.filter((it) => it.id === flavor_id), + }; } if (master_flavor_id) { - values.masterFlavor = { selectedRowKeys: [master_flavor_id] }; + values.masterFlavor = { + selectedRowKeys: [master_flavor_id], + selectedRows: this.masterFlavors.filter( + (it) => it.id === master_flavor_id + ), + }; } if (image_id) { values.images = { selectedRowKeys: [image_id] }; @@ -209,13 +224,29 @@ export class StepNodeSpec extends Base { name: 'flavor', label: t('Flavor of Nodes'), type: 'select-table', - component: this.getFlavorComponent(), + data: this.flavors, + columns: getBaseSimpleFlavorColumns(this), + isLoading: this.flavorStore.list.isLoading, + filterParams: [ + { + label: t('Name'), + name: 'name', + }, + ], }, { name: 'masterFlavor', label: t('Flavor of Master Nodes'), type: 'select-table', - component: this.getMasterFlavorComponent(), + data: this.masterFlavors, + columns: getBaseSimpleFlavorColumns(this), + isLoading: this.masterFlavorStore.list.isLoading, + filterParams: [ + { + label: t('Name'), + name: 'name', + }, + ], }, { name: 'volume_driver', diff --git a/src/pages/container-infra/containers/ClusterTemplates/actions/StepCreate/index.jsx b/src/pages/container-infra/containers/ClusterTemplates/actions/StepCreate/index.jsx index f3d5aff5..65e5964f 100644 --- a/src/pages/container-infra/containers/ClusterTemplates/actions/StepCreate/index.jsx +++ b/src/pages/container-infra/containers/ClusterTemplates/actions/StepCreate/index.jsx @@ -117,21 +117,14 @@ export class StepCreate extends StepAction { ...rest, labels: requestLabels, external_network_id: externalNetwork.selectedRowKeys[0], - fixed_network: fixedNetwork ? fixedNetwork.selectedRowKeys[0] : null, - fixed_subnet: fixedSubnet ? fixedSubnet.selectedRowKeys[0] : null, + fixed_network: (fixedNetwork && fixedNetwork.selectedRowKeys[0]) || null, + fixed_subnet: (fixedSubnet && fixedSubnet.selectedRowKeys[0]) || null, + flavor_id: (flavor && flavor.selectedRowKeys[0]) || null, + master_flavor_id: + (masterFlavor && masterFlavor.selectedRowKeys[0]) || null, + image_id: (images && images.selectedRowKeys[0]) || null, + keypair_id: (keypair && keypair.selectedRowKeys[0]) || null, }; - if (flavor) { - body.flavor_id = flavor.selectedRowKeys[0]; - } - if (masterFlavor) { - body.master_flavor_id = masterFlavor.selectedRowKeys[0]; - } - if (images) { - body.image_id = images.selectedRowKeys[0]; - } - if (keypair) { - body.keypair_id = keypair.selectedRowKeys[0]; - } if (this.isEdit) { return this.store.update({ id: this.params.id }, body); } diff --git a/src/pages/container-infra/containers/ClusterTemplates/index.jsx b/src/pages/container-infra/containers/ClusterTemplates/index.jsx index 6f6b0b47..1ade66ec 100644 --- a/src/pages/container-infra/containers/ClusterTemplates/index.jsx +++ b/src/pages/container-infra/containers/ClusterTemplates/index.jsx @@ -19,7 +19,6 @@ import actionConfigs from './actions'; export class ClusterTemplates extends Base { init() { this.store = globalClusterTemplateStore; - this.downloadStore = globalClusterTemplateStore; } get name() { diff --git a/src/pages/container-infra/containers/Clusters/Detail/BaseDetail.jsx b/src/pages/container-infra/containers/Clusters/Detail/BaseDetail.jsx index 049b5242..fce0cf63 100644 --- a/src/pages/container-infra/containers/Clusters/Detail/BaseDetail.jsx +++ b/src/pages/container-infra/containers/Clusters/Detail/BaseDetail.jsx @@ -19,14 +19,14 @@ import React from 'react'; export class BaseDetail extends Base { get leftCards() { - return [this.baseInfoCard, this.miscellaneousCard]; + return [this.templateCard, this.networkCard, this.miscellaneousCard]; } get rightCards() { return [this.nodesCard, this.labelCard, this.stackCard]; } - get baseInfoCard() { + get templateCard() { const { template = {} } = this.detailData; const templateUrl = template ? this.getLinkRender( @@ -65,19 +65,45 @@ export class BaseDetail extends Base { }; } - get miscellaneousCard() { - const { master_flavor_id, flavor_id, keypair } = this.detailData; - const masterFlavorUrl = master_flavor_id - ? this.getLinkRender('flavorDetail', master_flavor_id, { - id: master_flavor_id, + get networkCard() { + const { + fixed_network, + fixedNetwork: { name: fixedName } = {}, + fixed_subnet, + fixedSubnet: { name: subName } = {}, + } = this.detailData || {}; + const fixedNetworkUrl = fixed_network + ? this.getLinkRender('networkDetail', fixedName || fixed_network, { + id: fixed_network, }) : '-'; + const subnetUrl = + fixed_network && fixed_subnet + ? this.getLinkRender('subnetDetail', subName || fixed_subnet, { + networkId: fixed_network, + id: fixed_subnet, + }) + : '-'; - const flavorUrl = flavor_id - ? this.getLinkRender('flavorDetail', flavor_id, { - id: flavor_id, - }) - : '-'; + const options = [ + { + label: t('Fixed Network'), + content: fixedNetworkUrl, + }, + { + label: t('Fixed Subnet'), + content: subnetUrl, + }, + ]; + + return { + title: t('Network'), + options, + }; + } + + get miscellaneousCard() { + const { keypair } = this.detailData; const keypairUrl = keypair ? this.getLinkRender('keypairDetail', keypair, { @@ -111,14 +137,6 @@ export class BaseDetail extends Base { label: t('Docker Volume Size (GiB)'), dataIndex: 'docker_volume_size', }, - { - label: t('Master Node Flavor ID'), - content: masterFlavorUrl, - }, - { - label: t('Node Flavor ID'), - content: flavorUrl, - }, { label: t('COE Version'), dataIndex: 'coe_version', @@ -132,15 +150,48 @@ export class BaseDetail extends Base { return { title: t('Miscellaneous'), options, + labelCol: 12, + contentCol: 12, }; } get nodesCard() { + const { + master_flavor_id, + masterFlavor: { name: masterFlavorName } = {}, + flavor_id, + flavor: { name: flavorName } = {}, + } = this.detailData; + + const masterFlavorUrl = master_flavor_id + ? this.getLinkRender( + 'flavorDetail', + masterFlavorName || master_flavor_id, + { + id: master_flavor_id, + } + ) + : '-'; + + const flavorUrl = flavor_id + ? this.getLinkRender('flavorDetail', flavorName || flavor_id, { + id: flavor_id, + }) + : '-'; + const options = [ + { + label: t('Master Node Flavor'), + content: masterFlavorUrl, + }, { label: t('Master Count'), dataIndex: 'master_count', }, + { + label: t('Node Flavor'), + content: flavorUrl, + }, { label: t('Node Count'), dataIndex: 'node_count', diff --git a/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepNetworks/index.jsx b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepNetworks/index.jsx index 66e0ccaf..3bb5566d 100644 --- a/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepNetworks/index.jsx +++ b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepNetworks/index.jsx @@ -12,11 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +import React from 'react'; import Base from 'components/Form'; +import { toJS } from 'mobx'; import { inject, observer } from 'mobx-react'; import { defaultTip } from 'resources/magnum/cluster'; +import { NetworkStore } from 'stores/neutron/network'; +import { SubnetStore } from 'src/stores/neutron/subnet'; +import { networkColumns, subnetColumns } from 'resources/neutron/network'; +import { getLinkRender } from 'utils/route-map'; export class StepNetworks extends Base { + init() { + this.networkStore = new NetworkStore(); + this.subnetStore = new SubnetStore(); + this.getAllInitFunctions(); + } + get title() { return t('Cluster Network'); } @@ -27,9 +39,62 @@ export class StepNetworks extends Base { allowed = () => Promise.resolve(); + async getAllInitFunctions() { + const { + context: { clusterTemplate = {} }, + } = this.props; + const { selectedRows = [] } = clusterTemplate; + const { fixed_network, fixed_subnet } = selectedRows[0] || {}; + await Promise.all([ + fixed_network + ? this.networkStore.fetchDetail({ id: fixed_network }) + : null, + fixed_subnet ? this.subnetStore.fetchDetail({ id: fixed_subnet }) : null, + this.subnetStore.fetchList(), + ]); + this.updateDefaultValue(); + } + + get network() { + return toJS(this.networkStore.detail) || {}; + } + + get subnet() { + return toJS(this.subnetStore.detail) || {}; + } + + get subnetList() { + const { + context: { + clusterTemplate: { selectedRows: templateRows = [] } = {}, + fixedNetwork: { selectedRowKeys: contextKeys = [] } = {}, + }, + } = this.props; + const { fixed_network } = templateRows[0] || {}; + const key = contextKeys[0] || fixed_network; + + return (this.subnetStore.list.data || []).filter( + (it) => key === it.network_id + ); + } + get defaultValue() { + const { + context: { clusterTemplate = {}, fixedNetwork, fixedSubnet } = {}, + } = this.props; + const { selectedRows = [] } = clusterTemplate; + const { fixed_network, fixed_subnet } = selectedRows[0] || {}; + return { newNetwork: true, + fixedNetwork: fixedNetwork || { + selectedRowKeys: fixed_network ? [fixed_network] : [], + selectedRows: fixed_network ? [this.network] : [], + }, + fixedSubnet: fixedSubnet || { + selectedRowKeys: fixed_subnet ? [fixed_subnet] : [], + selectedRows: fixed_subnet ? [this.subnet] : [], + }, }; } @@ -40,6 +105,21 @@ export class StepNetworks extends Base { get formItems() { const { newNetwork } = this.state; + const { + context: { clusterTemplate = {}, fixedNetwork, fixedSubnet } = {}, + } = this.props; + const { selectedRows = [] } = clusterTemplate; + const { fixed_network, fixed_subnet } = selectedRows[0] || {}; + + const initFixedNetwork = fixedNetwork || { + selectedRowKeys: fixed_network ? [fixed_network] : [], + selectedRows: fixed_network ? [this.network] : [], + }; + const initSubnet = fixedSubnet || { + selectedRowKeys: fixed_subnet ? [fixed_subnet] : [], + selectedRows: fixed_subnet ? [this.subnet] : [], + }; + return [ { name: 'master_lb_enabled', @@ -55,10 +135,59 @@ export class StepNetworks extends Base { content: t('Create New Network'), }, { - name: 'fixed_network', - label: t('Use an Existing Network'), - type: 'network-select-table', + name: 'fixedNetwork', + label: t('Fixed Network'), + type: 'select-table', hidden: newNetwork, + backendPageStore: this.networkStore, + extraParams: { + 'router:external': false, + project_id: this.currentProjectId, + }, + loading: this.networkStore.list.isLoading, + header: ( +
+ {t(' You can go to the console to ')} + {getLinkRender({ + key: 'network', + value: `${t('create a new network/subnet')} > `, + })} +
+ ), + filterParams: [ + { + label: t('Name'), + name: 'name', + }, + ], + columns: networkColumns(this), + onChange: (value) => { + this.updateContext({ + fixedNetwork: value, + }); + this.updateContext({ + fixedSubnet: { + selectedRowKeys: [], + selectedRows: [], + }, + }); + }, + initValue: initFixedNetwork, + }, + { + name: 'fixedSubnet', + label: t('Fixed Subnet'), + type: 'select-table', + hidden: newNetwork, + data: this.subnetList, + filterParams: [ + { + label: t('Name'), + name: 'name', + }, + ], + columns: subnetColumns, + initValue: initSubnet, }, { type: 'divider', diff --git a/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepNodeSpec/index.jsx b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepNodeSpec/index.jsx index 6f189437..aab20223 100644 --- a/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepNodeSpec/index.jsx +++ b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepNodeSpec/index.jsx @@ -12,18 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -import React from 'react'; import { inject, observer } from 'mobx-react'; +import { toJS } from 'mobx'; import Base from 'components/Form'; -import FlavorSelectTable from 'src/pages/compute/containers/Instance/components/FlavorSelectTable'; import globalKeypairStore from 'stores/nova/keypair'; +import { FlavorStore } from 'src/stores/nova/flavor'; import { defaultTip } from 'resources/magnum/cluster'; import { getKeyPairHeader } from 'resources/nova/keypair'; +import { getBaseSimpleFlavorColumns } from 'resources/magnum/template'; export class StepNodeSpec extends Base { init() { this.keyPairStore = globalKeypairStore; - this.getKeypairs(); + this.flavorStore = new FlavorStore(); + this.masterFlavorStore = new FlavorStore(); + this.getAllInitFunctions(); } get title() { @@ -36,58 +39,90 @@ export class StepNodeSpec extends Base { allowed = () => Promise.resolve(); - async getKeypairs() { - await this.keyPairStore.fetchList(); + async getAllInitFunctions() { + await Promise.all([ + this.getKeypairs(), + this.getFlavors(), + this.getMasterFlavors(), + ]); + this.updateDefaultValue(); + } + + getKeypairs() { + return this.keyPairStore.fetchList(); } get keypairs() { return this.keyPairStore.list.data || []; } - getFlavorComponent() { - return ; + getFlavors() { + return this.flavorStore.fetchList(); } - onFlavorChange = (value) => { - this.updateContext({ - flavor: value, - }); - }; - - getMasterFlavorComponent() { - return ; + getMasterFlavors() { + return this.masterFlavorStore.fetchList(); } - onMasterFlavorChange = (value) => { - this.updateContext({ - masterFlavor: value, - }); - }; + get flavors() { + return toJS(this.flavorStore.list.data) || []; + } + + get masterFlavors() { + return toJS(this.masterFlavorStore.list.data) || []; + } get defaultValue() { - const { context: { clusterTemplate = {} } = {} } = this.props; + const { + context: { clusterTemplate = {}, keypair, masterFlavor, flavor } = {}, + } = this.props; const { selectedRows = [] } = clusterTemplate; - const { master_flavor_id, flavor_id } = selectedRows[0] || {}; + const { master_flavor_id, flavor_id, keypair_id, selfKeypair } = + selectedRows[0] || {}; return { master_count: 1, node_count: 1, - masterFlavor: { - selectedRowKeys: [master_flavor_id], + masterFlavor: masterFlavor || { + selectedRowKeys: master_flavor_id ? [master_flavor_id] : [], + selectedRows: this.masterFlavors.filter( + (it) => it.id === master_flavor_id + ), + }, + flavor: flavor || { + selectedRowKeys: flavor_id ? [flavor_id] : [], + selectedRows: this.flavors.filter((it) => it.id === flavor_id), + }, + keypair: keypair || { + selectedRowKeys: keypair_id && selfKeypair ? [keypair_id] : [], + selectedRows: this.keypairs.filter((it) => it.id === keypair_id), }, - flavor: { selectedRowKeys: [flavor_id] }, }; } get formItems() { - const { context: { clusterTemplate = {}, keypair } = {} } = this.props; + const { + context: { clusterTemplate = {}, keypair, masterFlavor, flavor } = {}, + } = this.props; const { selectedRows = [] } = clusterTemplate; const { master_flavor_id, flavor_id, keypair_id, selfKeypair } = selectedRows[0] || {}; const { initKeyPair = keypair } = this.state; const templateHasSelfKeypair = keypair_id && selfKeypair; const templateInitKeypair = { - selectedRowKeys: [keypair_id], + selectedRowKeys: keypair_id && selfKeypair ? [keypair_id] : [], + selectedRows: this.keypairs.filter((it) => it.id === keypair_id), + }; + + const initFlavor = flavor || { + selectedRowKeys: flavor_id ? [flavor_id] : [], + selectedRows: this.flavors.filter((it) => it.id === flavor_id), + }; + const initMasterFlavor = masterFlavor || { + selectedRowKeys: master_flavor_id ? [master_flavor_id] : [], + selectedRows: this.masterFlavors.filter( + (it) => it.id === master_flavor_id + ), }; return [ @@ -95,7 +130,7 @@ export class StepNodeSpec extends Base { name: 'keypair', label: t('Keypair'), type: 'select-table', - required: !templateHasSelfKeypair, + required: true, data: this.keypairs, initValue: initKeyPair || (templateHasSelfKeypair && templateInitKeypair), @@ -137,9 +172,23 @@ export class StepNodeSpec extends Base { name: 'masterFlavor', label: t('Flavor of Master Nodes'), type: 'select-table', - component: this.getMasterFlavorComponent(), - required: !master_flavor_id, + required: true, tip: defaultTip, + data: this.masterFlavors, + initValue: initMasterFlavor, + columns: getBaseSimpleFlavorColumns(this), + isLoading: this.masterFlavorStore.list.isLoading, + filterParams: [ + { + label: t('Name'), + name: 'name', + }, + ], + onChange: (value) => { + this.updateContext({ + masterFlavor: value, + }); + }, }, { type: 'divider', @@ -160,9 +209,23 @@ export class StepNodeSpec extends Base { name: 'flavor', label: t('Flavor of Nodes'), type: 'select-table', - component: this.getFlavorComponent(), - required: !flavor_id, + required: true, tip: defaultTip, + data: this.flavors, + initValue: initFlavor, + columns: getBaseSimpleFlavorColumns(this), + isLoading: this.flavorStore.list.isLoading, + filterParams: [ + { + label: t('Name'), + name: 'name', + }, + ], + onChange: (value) => { + this.updateContext({ + flavor: value, + }); + }, }, ]; } diff --git a/src/pages/container-infra/containers/Clusters/actions/StepCreate/index.jsx b/src/pages/container-infra/containers/Clusters/actions/StepCreate/index.jsx index 590e27ea..85543260 100644 --- a/src/pages/container-infra/containers/Clusters/actions/StepCreate/index.jsx +++ b/src/pages/container-infra/containers/Clusters/actions/StepCreate/index.jsx @@ -171,6 +171,19 @@ export class StepCreate extends StepAction { return quotaInfo; } + checkClusterQuota() { + const { quotaLoading } = this.state; + if (quotaLoading) { + return ''; + } + const { magnum_cluster = {} } = toJS(this.projectStore.magnumQuota) || {}; + const { left = 0 } = magnum_cluster; + if (left === 0) { + return this.getQuotaMessage(1, magnum_cluster, t('Clusters')); + } + return ''; + } + checkInstanceQuota() { const { quotaLoading } = this.state; if (quotaLoading) { @@ -235,9 +248,10 @@ export class StepCreate extends StepAction { } checkQuotaInput() { + const clusterMsg = this.checkClusterQuota(); const instanceMsg = this.checkInstanceQuota(); const flavorMsg = this.checkFlavorQuota(); - const error = instanceMsg || flavorMsg; + const error = clusterMsg || instanceMsg || flavorMsg; if (!error) { this.status = 'success'; this.errorMsg = ''; @@ -273,7 +287,8 @@ export class StepCreate extends StepAction { auto_healing_enabled, auto_scaling_enabled, newNetwork, - fixed_network, + fixedNetwork, + fixedSubnet, flavor, masterFlavor, ...rest @@ -311,11 +326,16 @@ export class StepCreate extends StepAction { data.flavor_id = flavor.selectedRowKeys[0]; } - if (!newNetwork && fixed_network) { - const { selectedRowKeys = [] } = fixed_network; + if (!newNetwork && fixedNetwork) { + const { selectedRowKeys = [] } = fixedNetwork; data.fixed_network = selectedRowKeys[0]; } + if (!newNetwork && fixedSubnet) { + const { selectedRowKeys = [] } = fixedSubnet; + data.fixed_subnet = selectedRowKeys[0]; + } + return this.store.create(data); }; } diff --git a/src/pages/container-infra/containers/Clusters/index.jsx b/src/pages/container-infra/containers/Clusters/index.jsx index ef0511f9..aef0f85b 100644 --- a/src/pages/container-infra/containers/Clusters/index.jsx +++ b/src/pages/container-infra/containers/Clusters/index.jsx @@ -22,7 +22,6 @@ import actionConfigs from './actions'; export class Clusters extends Base { init() { this.store = globalClustersStore; - this.downloadStore = globalClustersStore; } get name() { diff --git a/src/resources/magnum/template.js b/src/resources/magnum/template.js index 60788d4d..d63d629c 100644 --- a/src/resources/magnum/template.js +++ b/src/resources/magnum/template.js @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { flavorArchitectures, flavorCategoryList } from 'resources/nova/flavor'; +import { formatSize } from 'utils'; + export const getBaseTemplateColumns = (self) => [ { title: t('ID/Name'), @@ -41,3 +44,32 @@ export const getBaseTemplateColumns = (self) => [ }, }, ]; + +export const getBaseSimpleFlavorColumns = (self) => [ + { + title: t('ID/Name'), + dataIndex: 'name', + routeName: self ? self.getRouteName('flavorDetail') : '', + }, + { + title: t('Architecture'), + dataIndex: 'architecture', + valueMap: flavorArchitectures, + }, + { + title: t('Category'), + dataIndex: 'category', + render: (value) => flavorCategoryList[value] || value || '-', + }, + { + title: t('CPU'), + dataIndex: 'vcpus', + isHideable: true, + }, + { + title: t('Memory'), + dataIndex: 'ram', + isHideable: true, + render: (ram) => formatSize(ram, 2), + }, +]; diff --git a/src/resources/neutron/network.jsx b/src/resources/neutron/network.jsx index 51b04a37..a85330b8 100644 --- a/src/resources/neutron/network.jsx +++ b/src/resources/neutron/network.jsx @@ -111,3 +111,27 @@ export const isExternalNetwork = (network) => !!network['router:external']; export const subnetIpv6Tip = t( 'Default is slaac, for details, see https://docs.openstack.org/neutron/latest/admin/config-ipv6.html' ); + +export const subnetColumns = [ + { + title: t('Name'), + dataIndex: 'name', + }, + { + title: t('CIDR'), + dataIndex: 'cidr', + }, + { + title: t('Gateway IP'), + dataIndex: 'gateway_ip', + }, + { + title: t('IP Version'), + dataIndex: 'ip_version', + }, + { + title: t('Created At'), + dataIndex: 'created_at', + valueRender: 'toLocalTime', + }, +]; diff --git a/src/stores/magnum/clusterTemplates.js b/src/stores/magnum/clusterTemplates.js index 700923a8..08487631 100644 --- a/src/stores/magnum/clusterTemplates.js +++ b/src/stores/magnum/clusterTemplates.js @@ -22,6 +22,22 @@ export class ClusterTemplatesStore extends Base { return client.magnum.clusterTemplates; } + get flavorClient() { + return client.nova.flavors; + } + + get networkClient() { + return client.neutron.networks; + } + + get subnetClient() { + return client.neutron.subnets; + } + + get imageClient() { + return client.glance.images; + } + @action async create(newbody) { return this.submitting(this.client.create(newbody)); @@ -75,11 +91,48 @@ export class ClusterTemplatesStore extends Base { } async detailDidFetch(item) { - const { keypairs = [] } = (await client.nova.keypairs.list()) || {}; + const [kp = {}, fr = {}, mfr = {}, ext = {}, fx = {}, sub = {}, img] = + await Promise.all([ + client.nova.keypairs.list(), + item.flavor_id ? this.flavorClient.show(item.flavor_id) : {}, + item.master_flavor_id + ? this.flavorClient.show(item.master_flavor_id) + : {}, + item.external_network_id + ? this.networkClient.show(item.external_network_id) + : {}, + item.fixed_network ? this.networkClient.show(item.fixed_network) : {}, + item.fixed_subnet ? this.subnetClient.show(item.fixed_subnet) : {}, + item.image_id ? this.imageClient.show(item.image_id) : {}, + ]); + const { keypairs = [] } = kp; + const { flavor } = fr; + const { flavor: masterFlavor } = mfr; + const { network } = ext; + const { network: fixedNetwork } = fx; + const { subnet: fixedSubnet } = sub; const keypair = keypairs.find((k) => k?.keypair?.name === item.keypair_id); if (keypair) { item.selfKeypair = true; } + if (flavor) { + item.flavor = flavor; + } + if (masterFlavor) { + item.masterFlavor = masterFlavor; + } + if (network) { + item.externalNetwork = network; + } + if (fixedNetwork) { + item.fixedNetwork = fixedNetwork; + } + if (fixedSubnet) { + item.fixedSubnet = fixedSubnet; + } + if (img) { + item.image = img; + } return item; } } diff --git a/src/stores/magnum/clusters.js b/src/stores/magnum/clusters.js index aed93580..b3f0838b 100644 --- a/src/stores/magnum/clusters.js +++ b/src/stores/magnum/clusters.js @@ -25,6 +25,18 @@ export class ClustersStore extends Base { return client.magnum.clusterTemplates; } + get flavorClient() { + return client.nova.flavors; + } + + get networkClient() { + return client.neutron.networks; + } + + get subnetClient() { + return client.neutron.subnets; + } + get listWithDetail() { return true; } @@ -45,9 +57,41 @@ export class ClustersStore extends Base { } async detailDidFetch(item) { - const { cluster_template_id } = item || {}; - const template = await this.templateClient.show(cluster_template_id); + const template = + (await this.templateClient.show(item.cluster_template_id)) || {}; item.template = template; + const { + flavor_id: templateFlavorId, + master_flavor_id: templateMasterFlavorId, + fixed_network: templateFixedNetworkId, + fixed_subnet: templateSubnetId, + } = template; + const flavorId = item.flavor_id || templateFlavorId; + const masterFlavorId = item.master_flavor_id || templateMasterFlavorId; + const fixedNetworkId = item.fixed_network || templateFixedNetworkId; + const fixedSubnetId = item.fixed_subnet || templateSubnetId; + const [fr = {}, mfr = {}, fx = {}, sub = {}] = await Promise.all([ + flavorId ? this.flavorClient.show(flavorId) : {}, + masterFlavorId ? this.flavorClient.show(masterFlavorId) : {}, + fixedNetworkId ? this.networkClient.show(fixedNetworkId) : {}, + fixedSubnetId ? this.subnetClient.show(fixedSubnetId) : {}, + ]); + const { flavor } = fr; + const { flavor: masterFlavor } = mfr; + const { network: fixedNetwork } = fx; + const { subnet: fixedSubnet } = sub; + if (flavor) { + item.flavor = flavor; + } + if (masterFlavor) { + item.masterFlavor = masterFlavor; + } + if (fixedNetwork) { + item.fixedNetwork = fixedNetwork; + } + if (fixedSubnet) { + item.fixedSubnet = fixedSubnet; + } return item; }