feat: Support the use of ports when creating an instance
1. Add port select in network step when creating an instance 2. Show port in confirm step when creating an instance Change-Id: I4adb832d194433f2b201c02c067e72251c6e61e6
This commit is contained in:
parent
6872acf19b
commit
c96180b723
@ -258,7 +258,8 @@ export default class FormItem extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSelectTableValidator = (rule, value) => {
|
getSelectTableValidator = (rule, value) => {
|
||||||
if (!value || value.selectedRowKeys.length === 0) {
|
const { selectedRowKeys = [] } = value || {};
|
||||||
|
if (selectedRowKeys.length === 0) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error(rule.placeholder || `${t('Please select')}${rule.label}!`)
|
new Error(rule.placeholder || `${t('Please select')}${rule.label}!`)
|
||||||
);
|
);
|
||||||
|
@ -1254,6 +1254,7 @@
|
|||||||
"Neutron Agent Detail": "Neutron Agent Detail",
|
"Neutron Agent Detail": "Neutron Agent Detail",
|
||||||
"Neutron Agents": "Neutron Agents",
|
"Neutron Agents": "Neutron Agents",
|
||||||
"Neutron Service": "Neutron Service",
|
"Neutron Service": "Neutron Service",
|
||||||
|
"New": "New",
|
||||||
"New Availability Zone": "New Availability Zone",
|
"New Availability Zone": "New Availability Zone",
|
||||||
"New Caledonia": "New Caledonia",
|
"New Caledonia": "New Caledonia",
|
||||||
"New Zealand": "New Zealand",
|
"New Zealand": "New Zealand",
|
||||||
@ -1451,7 +1452,6 @@
|
|||||||
"Please select your Domain!": "Please select your Domain!",
|
"Please select your Domain!": "Please select your Domain!",
|
||||||
"Please select your Region!": "Please select your Region!",
|
"Please select your Region!": "Please select your Region!",
|
||||||
"Please select {name} first": "Please select {name} first",
|
"Please select {name} first": "Please select {name} first",
|
||||||
"Please select!": "Please select!",
|
|
||||||
"Please set CPU && Ram first.": "Please set CPU && Ram first.",
|
"Please set CPU && Ram first.": "Please set CPU && Ram first.",
|
||||||
"Please set MUNA": "Please set MUNA",
|
"Please set MUNA": "Please set MUNA",
|
||||||
"Please upload files smaller than { size }G on the page. It is recommended to upload files over { size }G using API.": "Please upload files smaller than { size }G on the page. It is recommended to upload files over { size }G using API.",
|
"Please upload files smaller than { size }G on the page. It is recommended to upload files over { size }G using API.": "Please upload files smaller than { size }G on the page. It is recommended to upload files over { size }G using API.",
|
||||||
@ -1482,6 +1482,7 @@
|
|||||||
"Port Security Enabled": "Port Security Enabled",
|
"Port Security Enabled": "Port Security Enabled",
|
||||||
"Port Type": "Port Type",
|
"Port Type": "Port Type",
|
||||||
"Ports": "Ports",
|
"Ports": "Ports",
|
||||||
|
"Ports provide extra communication channels to your instances. You can select ports instead of networks or a mix of both (The port executes its own security group rules by default).": "Ports provide extra communication channels to your instances. You can select ports instead of networks or a mix of both (The port executes its own security group rules by default).",
|
||||||
"Portugal": "Portugal",
|
"Portugal": "Portugal",
|
||||||
"Power Off": "Power Off",
|
"Power Off": "Power Off",
|
||||||
"Power On": "Power On",
|
"Power On": "Power On",
|
||||||
|
@ -1254,6 +1254,7 @@
|
|||||||
"Neutron Agent Detail": "网络服务详情",
|
"Neutron Agent Detail": "网络服务详情",
|
||||||
"Neutron Agents": "网络服务",
|
"Neutron Agents": "网络服务",
|
||||||
"Neutron Service": "网络服务",
|
"Neutron Service": "网络服务",
|
||||||
|
"New": "新建",
|
||||||
"New Availability Zone": "新可用域",
|
"New Availability Zone": "新可用域",
|
||||||
"New Caledonia": "新喀里多尼亚",
|
"New Caledonia": "新喀里多尼亚",
|
||||||
"New Zealand": "新西兰",
|
"New Zealand": "新西兰",
|
||||||
@ -1451,7 +1452,6 @@
|
|||||||
"Please select your Domain!": "请选择Domain!",
|
"Please select your Domain!": "请选择Domain!",
|
||||||
"Please select your Region!": "请选择Region!",
|
"Please select your Region!": "请选择Region!",
|
||||||
"Please select {name} first": "请先选择{name}",
|
"Please select {name} first": "请先选择{name}",
|
||||||
"Please select!": "请选择!",
|
|
||||||
"Please set CPU && Ram first.": "请先设置CPU、内存。",
|
"Please set CPU && Ram first.": "请先设置CPU、内存。",
|
||||||
"Please set MUNA": "请设置NUMA节点",
|
"Please set MUNA": "请设置NUMA节点",
|
||||||
"Please upload files smaller than { size }G on the page. It is recommended to upload files over { size }G using API.": "页面请上传小于{ size }G的文件,超过{ size }G的文件建议使用API上传。",
|
"Please upload files smaller than { size }G on the page. It is recommended to upload files over { size }G using API.": "页面请上传小于{ size }G的文件,超过{ size }G的文件建议使用API上传。",
|
||||||
@ -1482,6 +1482,7 @@
|
|||||||
"Port Security Enabled": "启用端口安全",
|
"Port Security Enabled": "启用端口安全",
|
||||||
"Port Type": "端口方式",
|
"Port Type": "端口方式",
|
||||||
"Ports": "端口",
|
"Ports": "端口",
|
||||||
|
"Ports provide extra communication channels to your instances. You can select ports instead of networks or a mix of both (The port executes its own security group rules by default).": "端口为您的云主机提供了额外的通信渠道。您可以选择已创建的端口而非网络或者二者都选(端口默认执行本身的安全组规则)。",
|
||||||
"Portugal": "葡萄牙",
|
"Portugal": "葡萄牙",
|
||||||
"Power Off": "关机",
|
"Power Off": "关机",
|
||||||
"Power On": "开机",
|
"Power On": "开机",
|
||||||
|
@ -74,7 +74,7 @@ export class ConfirmStep extends Base {
|
|||||||
|
|
||||||
getVirtualLANs() {
|
getVirtualLANs() {
|
||||||
const { context } = this.props;
|
const { context } = this.props;
|
||||||
const { networks } = context;
|
const { networks = [] } = context;
|
||||||
const values = networks.map((it) => {
|
const values = networks.map((it) => {
|
||||||
const { networkOption, subnetOption, ipTypeOption, ip } = it.value;
|
const { networkOption, subnetOption, ipTypeOption, ip } = it.value;
|
||||||
const subnet =
|
const subnet =
|
||||||
@ -86,19 +86,42 @@ export class ConfirmStep extends Base {
|
|||||||
return (
|
return (
|
||||||
<Row>
|
<Row>
|
||||||
{values.map((i) => (
|
{values.map((i) => (
|
||||||
<Col span={24}>{i}</Col>
|
<Col span={24} key={i}>
|
||||||
|
{i}
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPorts() {
|
||||||
|
const { context } = this.props;
|
||||||
|
const { ports: { selectedRows = [] } = {} } = context;
|
||||||
|
const values = selectedRows.map((it) => it.name || it.id);
|
||||||
|
return (
|
||||||
|
<Row>
|
||||||
|
{values.map((i) => (
|
||||||
|
<Col span={24} key={i}>
|
||||||
|
{i}
|
||||||
|
</Col>
|
||||||
))}
|
))}
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
// return values.join(<br />);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSecurityGroups() {
|
getSecurityGroups() {
|
||||||
const { context } = this.props;
|
const { context } = this.props;
|
||||||
const { securityGroup: { selectedRows = [] } = {} } = context;
|
const { securityGroup: { selectedRows = [] } = {} } = context;
|
||||||
const values = selectedRows.map((it) => it.name);
|
const values = selectedRows.map((it) => it.name);
|
||||||
return values;
|
return (
|
||||||
// return values.join(<br />);
|
<Row>
|
||||||
|
{values.map((i) => (
|
||||||
|
<Col span={24} key={i}>
|
||||||
|
{i}
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLoginType() {
|
getLoginType() {
|
||||||
@ -215,10 +238,15 @@ export class ConfirmStep extends Base {
|
|||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: t('Virtual LAN'),
|
label: `${t('Virtual LAN')}(${t('New')})`,
|
||||||
value: this.getVirtualLANs(),
|
value: this.getVirtualLANs(),
|
||||||
span: 1,
|
span: 1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: `${t('Virtual LAN')}(${t('Created')})`,
|
||||||
|
value: this.getPorts(),
|
||||||
|
span: 1,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t('Security Group'),
|
label: t('Security Group'),
|
||||||
value: this.getSecurityGroups(),
|
value: this.getSecurityGroups(),
|
||||||
|
@ -15,12 +15,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { inject, observer } from 'mobx-react';
|
import { inject, observer } from 'mobx-react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Button } from 'antd';
|
|
||||||
import { FormOutlined } from '@ant-design/icons';
|
|
||||||
import { isEmpty, isArray } from 'lodash';
|
import { isEmpty, isArray } from 'lodash';
|
||||||
import { NetworkStore } from 'stores/neutron/network';
|
import { NetworkStore } from 'stores/neutron/network';
|
||||||
import { SubnetStore } from 'stores/neutron/subnet';
|
import { SubnetStore } from 'stores/neutron/subnet';
|
||||||
import { SecurityGroupStore } from 'stores/neutron/security-group';
|
import { SecurityGroupStore } from 'stores/neutron/security-group';
|
||||||
|
import { VirtualAdapterStore } from 'stores/neutron/virtual-adapter';
|
||||||
import { ipValidate } from 'utils/validate';
|
import { ipValidate } from 'utils/validate';
|
||||||
import Base from 'components/Form';
|
import Base from 'components/Form';
|
||||||
import NetworkSelect from 'components/FormItem/NetworkSelect';
|
import NetworkSelect from 'components/FormItem/NetworkSelect';
|
||||||
@ -29,6 +28,7 @@ import {
|
|||||||
securityGroupColumns,
|
securityGroupColumns,
|
||||||
securityGroupFilter,
|
securityGroupFilter,
|
||||||
} from 'resources/security-group';
|
} from 'resources/security-group';
|
||||||
|
import { portColumns, portFilters } from 'resources/port';
|
||||||
|
|
||||||
// import EditYamlModal from 'components/Modals/EditYaml';
|
// import EditYamlModal from 'components/Modals/EditYaml';
|
||||||
const { isIPv4, isIpv6 } = ipValidate;
|
const { isIPv4, isIpv6 } = ipValidate;
|
||||||
@ -38,6 +38,7 @@ export class NetworkStep extends Base {
|
|||||||
this.networkStore = new NetworkStore();
|
this.networkStore = new NetworkStore();
|
||||||
this.subnetStore = new SubnetStore();
|
this.subnetStore = new SubnetStore();
|
||||||
this.securityGroupStore = new SecurityGroupStore();
|
this.securityGroupStore = new SecurityGroupStore();
|
||||||
|
this.portStore = new VirtualAdapterStore();
|
||||||
this.subnetMap = {};
|
this.subnetMap = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,16 +154,43 @@ export class NetworkStep extends Base {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
checkNetworkAndPort = ({ getFieldValue }) => ({
|
||||||
|
validator() {
|
||||||
|
const networkSelect = getFieldValue('networkSelect');
|
||||||
|
const ports = getFieldValue('ports');
|
||||||
|
const { selectedRowKeys: networkSelected = [] } = networkSelect || {};
|
||||||
|
const { selectedRowKeys: portsSelected = [] } = ports || {};
|
||||||
|
if (networkSelected.length === 0 && portsSelected === 0) {
|
||||||
|
return Promise.reject(t('Please select'));
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
onPortChange = (value) => {
|
||||||
|
const { selectedRows = [] } = value || {};
|
||||||
|
this.updateContext({
|
||||||
|
portSelectRows: selectedRows,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
get nameForStateUpdate() {
|
get nameForStateUpdate() {
|
||||||
return ['networkSelect', 'networks'];
|
return ['networkSelect', 'networks', 'ports'];
|
||||||
}
|
}
|
||||||
|
|
||||||
get formItems() {
|
get formItems() {
|
||||||
const { networkSelectRows = [], subnets, initValue = [] } = this.state;
|
const {
|
||||||
|
networkSelectRows = [],
|
||||||
|
subnets,
|
||||||
|
initValue = [],
|
||||||
|
ports = [],
|
||||||
|
} = this.state;
|
||||||
const showNetworks = networkSelectRows.length > 0;
|
const showNetworks = networkSelectRows.length > 0;
|
||||||
const showSecurityGroups =
|
const showSecurityGroups =
|
||||||
networkSelectRows.length &&
|
networkSelectRows.length &&
|
||||||
networkSelectRows.every((it) => it.port_security_enabled);
|
networkSelectRows.every((it) => it.port_security_enabled);
|
||||||
|
const networkRequired = ports.length === 0;
|
||||||
|
const portRequired = networkSelectRows.length === 0;
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: 'networkSelect',
|
name: 'networkSelect',
|
||||||
@ -172,7 +200,9 @@ export class NetworkStep extends Base {
|
|||||||
onChange: this.onNetworkChange,
|
onChange: this.onNetworkChange,
|
||||||
showExternal: true,
|
showExternal: true,
|
||||||
isMulti: true,
|
isMulti: true,
|
||||||
required: true,
|
required: networkRequired,
|
||||||
|
otherRule: this.checkNetworkAndPort,
|
||||||
|
dependencies: ['ports'],
|
||||||
header: (
|
header: (
|
||||||
<div>
|
<div>
|
||||||
{t(
|
{t(
|
||||||
@ -208,20 +238,29 @@ export class NetworkStep extends Base {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'ipv6',
|
name: 'divider1',
|
||||||
label: 'IPv6',
|
type: 'divider',
|
||||||
type: 'label',
|
|
||||||
hidden: true,
|
|
||||||
content: (
|
|
||||||
<span>
|
|
||||||
{t('The selected VPC/ subnet does not have IPv6 enabled.')}{' '}
|
|
||||||
<Button type="link">
|
|
||||||
{t('To open')} <FormOutlined />
|
|
||||||
</Button>{' '}
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: 'ports',
|
||||||
|
type: 'select-table',
|
||||||
|
// type: 'input',
|
||||||
|
label: t('Ports'),
|
||||||
|
extraParams: { project_id: this.currentProjectId, status: 'DOWN' },
|
||||||
|
backendPageStore: this.portStore,
|
||||||
|
isMulti: true,
|
||||||
|
header: t(
|
||||||
|
'Ports provide extra communication channels to your instances. You can select ports instead of networks or a mix of both (The port executes its own security group rules by default).'
|
||||||
|
),
|
||||||
|
filterParams: portFilters,
|
||||||
|
columns: portColumns,
|
||||||
|
dependencies: ['networkSelect'],
|
||||||
|
otherRule: this.checkNetworkAndPort,
|
||||||
|
required: portRequired,
|
||||||
|
onChange: this.onPortChange,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'divider2',
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -409,16 +409,43 @@ export class StepCreate extends StepAction {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNetworkData(values) {
|
||||||
|
const { networks = [], ports = {} } = values;
|
||||||
|
let hasIp = false;
|
||||||
|
const data = [];
|
||||||
|
networks.forEach((it) => {
|
||||||
|
const net = {
|
||||||
|
uuid: it.value.network,
|
||||||
|
};
|
||||||
|
if (it.value.ipType === 1 && it.value.ip) {
|
||||||
|
net.fixed_ip = it.value.ip;
|
||||||
|
hasIp = true;
|
||||||
|
}
|
||||||
|
data.push(net);
|
||||||
|
});
|
||||||
|
const { selectedRowKeys = [] } = ports || {};
|
||||||
|
selectedRowKeys.forEach((it) => {
|
||||||
|
const net = {
|
||||||
|
port: it,
|
||||||
|
};
|
||||||
|
data.push(net);
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
hasIp,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
getSubmitData(values) {
|
getSubmitData(values) {
|
||||||
if (this.status === 'error') {
|
if (this.status === 'error') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { volumes, imageRef } = this.getVolumeAndImageData(values);
|
const { volumes, imageRef } = this.getVolumeAndImageData(values);
|
||||||
|
const { data: networks, hasIp } = this.getNetworkData(values);
|
||||||
const {
|
const {
|
||||||
availableZone,
|
availableZone,
|
||||||
keypair,
|
keypair,
|
||||||
loginType,
|
loginType,
|
||||||
networks,
|
|
||||||
password,
|
password,
|
||||||
physicalNode,
|
physicalNode,
|
||||||
physicalNodeType,
|
physicalNodeType,
|
||||||
@ -429,7 +456,10 @@ export class StepCreate extends StepAction {
|
|||||||
name,
|
name,
|
||||||
count = 1,
|
count = 1,
|
||||||
} = values;
|
} = values;
|
||||||
let hasIp = false;
|
if (hasIp && count > 1) {
|
||||||
|
this.ipBatchError = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const { selectedRows: securityGroupSelectedRows = [] } =
|
const { selectedRows: securityGroupSelectedRows = [] } =
|
||||||
securityGroup || {};
|
securityGroup || {};
|
||||||
const server = {
|
const server = {
|
||||||
@ -440,21 +470,8 @@ export class StepCreate extends StepAction {
|
|||||||
flavorRef: flavor.selectedRowKeys[0],
|
flavorRef: flavor.selectedRowKeys[0],
|
||||||
availability_zone: availableZone.value,
|
availability_zone: availableZone.value,
|
||||||
block_device_mapping_v2: volumes,
|
block_device_mapping_v2: volumes,
|
||||||
networks: networks.map((it) => {
|
networks,
|
||||||
const net = {
|
|
||||||
uuid: it.value.network,
|
|
||||||
};
|
|
||||||
if (it.value.ipType === 1 && it.value.ip) {
|
|
||||||
net.fixed_ip = it.value.ip;
|
|
||||||
hasIp = true;
|
|
||||||
}
|
|
||||||
return net;
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
if (hasIp && count > 1) {
|
|
||||||
this.ipBatchError = true;
|
|
||||||
return Promise.reject();
|
|
||||||
}
|
|
||||||
if (imageRef) {
|
if (imageRef) {
|
||||||
server.imageRef = imageRef;
|
server.imageRef = imageRef;
|
||||||
}
|
}
|
||||||
@ -501,6 +518,10 @@ export class StepCreate extends StepAction {
|
|||||||
const { data } = this.state;
|
const { data } = this.state;
|
||||||
this.values = data;
|
this.values = data;
|
||||||
const submitData = this.getSubmitData(data);
|
const submitData = this.getSubmitData(data);
|
||||||
|
if (!submitData) {
|
||||||
|
Notify.errorWithDetail(null, this.errorText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.onSubmit(submitData).then(
|
this.onSubmit(submitData).then(
|
||||||
() => {
|
() => {
|
||||||
this.routing.push(this.listUrl);
|
this.routing.push(this.listUrl);
|
||||||
|
@ -174,3 +174,54 @@ export function getPortFormItem(device_owner) {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const portColumns = [
|
||||||
|
{
|
||||||
|
title: t('ID/Name'),
|
||||||
|
dataIndex: 'name',
|
||||||
|
sorter: false,
|
||||||
|
render: (value, record) => (
|
||||||
|
<div>
|
||||||
|
<div>{record.id}</div>
|
||||||
|
<div>{value || '-'}</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('Owned Network'),
|
||||||
|
dataIndex: 'network_name',
|
||||||
|
isLink: true,
|
||||||
|
idKey: 'network_id',
|
||||||
|
sorter: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('IPv4 Address'),
|
||||||
|
dataIndex: 'ipv4',
|
||||||
|
render: (value) => value.map((it) => <div key={it}>{it}</div>),
|
||||||
|
sorter: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('IPv6 Address'),
|
||||||
|
dataIndex: 'ipv6',
|
||||||
|
render: (value) => value.map((it) => <div key={it}>{it}</div>),
|
||||||
|
sorter: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('Mac Address'),
|
||||||
|
dataIndex: 'mac_address',
|
||||||
|
sorter: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('Status'),
|
||||||
|
dataIndex: 'status',
|
||||||
|
render: (value) => portStatus[value] || value,
|
||||||
|
sorter: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const portFilters = [
|
||||||
|
{
|
||||||
|
label: t('Name'),
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
Loading…
Reference in New Issue
Block a user