From 2de43cee9c93fde40849fd40933e4c7b245e2f65 Mon Sep 17 00:00:00 2001 From: rawagner Date: Tue, 2 Apr 2019 13:46:43 +0200 Subject: [PATCH] Add binding type to NICs (#298) --- .../CreateDeviceRow/CreateDeviceRow.js | 2 +- src/components/CreateNicRow/CreateNicRow.js | 75 ++++++++++++++----- .../__snapshots__/CreateNicRow.test.js.snap | 52 +++++++++++-- src/components/Form/FormFactory.js | 4 +- .../Table/EditableDraggableTable.js | 2 + src/components/Table/TableFactory.js | 4 +- .../Wizard/CreateVmWizard/CreateVmWizard.js | 2 + .../Wizard/CreateVmWizard/NetworksTab.js | 46 +++++++++--- .../Wizard/CreateVmWizard/constants.js | 3 + .../Wizard/CreateVmWizard/strings.js | 2 + .../CreateVmWizard/tests/NetworksTab.test.js | 4 +- .../__snapshots__/NetworksTab.test.js.snap | 18 ++++- src/k8s/vmBuilder.js | 25 +++++++ src/selectors/vm/selectors.js | 18 +++++ src/utils/patches.js | 5 +- src/utils/utils.js | 28 +++++++ 16 files changed, 246 insertions(+), 44 deletions(-) diff --git a/src/components/CreateDeviceRow/CreateDeviceRow.js b/src/components/CreateDeviceRow/CreateDeviceRow.js index c09c6d5c5..eb5a1b6e2 100644 --- a/src/components/CreateDeviceRow/CreateDeviceRow.js +++ b/src/components/CreateDeviceRow/CreateDeviceRow.js @@ -46,5 +46,5 @@ CreateDeviceRow.propTypes = { onCancel: PropTypes.func.isRequired, LoadingComponent: PropTypes.func.isRequired, deviceFields: PropTypes.object.isRequired, - columnSizes: PropTypes.object.isRequired, + columnSizes: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired, }; diff --git a/src/components/CreateNicRow/CreateNicRow.js b/src/components/CreateNicRow/CreateNicRow.js index 90416f285..1a07f2bf1 100644 --- a/src/components/CreateNicRow/CreateNicRow.js +++ b/src/components/CreateNicRow/CreateNicRow.js @@ -5,20 +5,36 @@ import { get } from 'lodash'; import { CreateDeviceRow } from '../CreateDeviceRow'; import { getName, getNetworks, getInterfaces } from '../../selectors'; import { validateDNS1123SubdomainValue } from '../../utils/validations'; +import { getNetworkBindings, getDefaultNetworkBinding } from '../../utils/utils'; import { POD_NETWORK } from '../../constants'; -import { HEADER_NAME, HEADER_MAC, SELECT_NETWORK } from '../Wizard/CreateVmWizard/strings'; +import { + HEADER_NAME, + HEADER_MAC, + SELECT_NETWORK, + HEADER_BINDING_METHOD, + SELECT_BINDING, +} from '../Wizard/CreateVmWizard/strings'; import { NETWORK_TYPE_POD, NETWORK_TYPE_MULTUS, NAME_KEY } from '../Wizard/CreateVmWizard/constants'; import { Loading } from '../Loading'; import { settingsValue } from '../../k8s/selectors'; import { DROPDOWN, CUSTOM, LABEL } from '../Form'; -const columnSizes = { +const mainColumnSize = { lg: 3, md: 3, sm: 3, xs: 3, }; +const otherColumnSize = { + lg: 2, + md: 2, + sm: 2, + xs: 2, +}; + +const columnSizes = [mainColumnSize, mainColumnSize, otherColumnSize, otherColumnSize, otherColumnSize]; + const getUsedNetworks = vm => { const interfaces = getInterfaces(vm); const networks = getNetworks(vm); @@ -61,6 +77,7 @@ const getNetworkChoices = (vm, networks) => { }; const getNicColumns = (nic, networks, LoadingComponent) => { + const bindingChoices = getNetworkBindings(get(settingsValue(nic, 'network'), 'networkType')); let network; if (networks) { const networkChoices = getNetworkChoices(nic.vm, networks); @@ -71,6 +88,7 @@ const getNicColumns = (nic, networks, LoadingComponent) => { choices: networkChoices, disabled: nic.creating || networkChoices.length === 0, required: true, + title: 'Network', }; } else { network = { @@ -94,6 +112,15 @@ const getNicColumns = (nic, networks, LoadingComponent) => { type: LABEL, }, network, + binding: { + id: 'binding', + type: DROPDOWN, + defaultValue: SELECT_BINDING, + title: HEADER_BINDING_METHOD, + required: true, + disabled: nic.creating, + choices: bindingChoices, + }, mac: { id: 'mac-address', title: HEADER_MAC, @@ -102,25 +129,39 @@ const getNicColumns = (nic, networks, LoadingComponent) => { }; }; -const onFormChange = (newValue, key, onChange) => { - if (key === 'network' && get(newValue, 'value.networkType') === NETWORK_TYPE_POD) { - // reset mac value - onChange({ value: '' }, 'mac'); +const onFormChange = (nic, newValue, key, onChange) => { + if (key === 'network' && get(settingsValue(nic, 'network'), 'networkType') !== get(newValue, 'value.networkType')) { + switch (get(newValue, 'value.networkType')) { + case NETWORK_TYPE_POD: + onChange({ value: getDefaultNetworkBinding(NETWORK_TYPE_POD) }, 'binding'); + // reset mac value + onChange({ value: '' }, 'mac'); + break; + case NETWORK_TYPE_MULTUS: + onChange({ value: getDefaultNetworkBinding(NETWORK_TYPE_MULTUS) }, 'binding'); + break; + default: + break; + } } + onChange(newValue, key); }; -export const CreateNicRow = ({ nic, onAccept, onCancel, onChange, networks, LoadingComponent }) => ( - onFormChange(newValue, key, onChange)} - device={nic} - LoadingComponent={LoadingComponent} - columnSizes={columnSizes} - deviceFields={getNicColumns(nic, networks, LoadingComponent)} - /> -); +export const CreateNicRow = ({ nic, onAccept, onCancel, onChange, networks, LoadingComponent }) => { + const fields = getNicColumns(nic, networks, LoadingComponent); + return ( + onFormChange(nic, newValue, key, onChange)} + device={nic} + LoadingComponent={LoadingComponent} + columnSizes={columnSizes} + deviceFields={fields} + /> + ); +}; CreateNicRow.propTypes = { nic: PropTypes.object.isRequired, diff --git a/src/components/CreateNicRow/tests/__snapshots__/CreateNicRow.test.js.snap b/src/components/CreateNicRow/tests/__snapshots__/CreateNicRow.test.js.snap index e38fc4813..0978aea3c 100644 --- a/src/components/CreateNicRow/tests/__snapshots__/CreateNicRow.test.js.snap +++ b/src/components/CreateNicRow/tests/__snapshots__/CreateNicRow.test.js.snap @@ -4,16 +4,55 @@ exports[` renders correctly 1`] = ` renders correctly 1`] = ` "disabled": false, "id": "network-type", "required": true, + "title": "Network", "type": "dropdown", }, } diff --git a/src/components/Form/FormFactory.js b/src/components/Form/FormFactory.js index 376901854..7a92643f0 100644 --- a/src/components/Form/FormFactory.js +++ b/src/components/Form/FormFactory.js @@ -270,7 +270,7 @@ export const ListFormFactory = ({ fields, fieldsValues, onFormChange, actions, c const form = formGroups.map((formGroup, index) => ( { +const onChange = (rows, { editing, type, id, key, newValue }, onRowUpdate, onRowDeleteOrMove, onRowActivate) => { switch (type) { case ON_ACTIVATE: onRowActivate(rows); @@ -13,7 +13,7 @@ const onChange = (rows, { editing, type, id }, onRowUpdate, onRowDeleteOrMove, o case ON_CONFIRM: case ON_CANCEL: case ON_CHANGE: - onRowUpdate(rows, id, editing); + onRowUpdate(rows, id, editing, key, newValue); break; case ON_DELETE: case ON_MOVE: diff --git a/src/components/Wizard/CreateVmWizard/CreateVmWizard.js b/src/components/Wizard/CreateVmWizard/CreateVmWizard.js index 6f4844d6a..67245a0ed 100644 --- a/src/components/Wizard/CreateVmWizard/CreateVmWizard.js +++ b/src/components/Wizard/CreateVmWizard/CreateVmWizard.js @@ -35,6 +35,7 @@ import { NAMESPACE_KEY, PROVIDER_VMWARE_VM_KEY, INTERMEDIARY_STORAGE_TAB_KEY, + NETWORK_BINDING_MASQUERADE, } from './constants'; import { @@ -264,6 +265,7 @@ const podNetwork = { editable: true, edit: false, networkType: NETWORK_TYPE_POD, + binding: NETWORK_BINDING_MASQUERADE, }; export class CreateVmWizard extends React.Component { diff --git a/src/components/Wizard/CreateVmWizard/NetworksTab.js b/src/components/Wizard/CreateVmWizard/NetworksTab.js index 9321c5cfb..a24b637a7 100644 --- a/src/components/Wizard/CreateVmWizard/NetworksTab.js +++ b/src/components/Wizard/CreateVmWizard/NetworksTab.js @@ -6,6 +6,7 @@ import { TableFactory } from '../../Table/TableFactory'; import { ACTIONS_TYPE, DELETE_ACTION } from '../../Table/constants'; import { POD_NETWORK, PROVISION_SOURCE_PXE } from '../../../constants'; import { getValidationObject } from '../../../utils/validations'; +import { getNetworkBindings, getDefaultNetworkBinding } from '../../../utils/utils'; import { NETWORK_TYPE_POD, NETWORK_TYPE_MULTUS } from './constants'; import { @@ -23,7 +24,10 @@ import { HEADER_MAC, HEADER_NAME, HEADER_NETWORK, + HEADER_BINDING_METHOD, + SELECT_BINDING, } from './strings'; +import { getInterfaceBinding } from '../../../selectors'; const validateNetwork = network => { const errors = Array(4).fill(null); @@ -106,6 +110,7 @@ const resolveInitialNetworks = (networks, networkConfigs, namespace, sourceType) editable: true, edit: false, networkType, + binding: getInterfaceBinding(templateNetwork.interface), }; } return { @@ -138,7 +143,7 @@ export class NetworksTab extends React.Component { publishResults = rows => { let valid = this.props.sourceType === PROVISION_SOURCE_PXE ? rows.some(row => row.isBootable) : true; const nics = rows.map( - ({ templateNetwork, rootNetwork, id, isBootable, name, mac, network, errors, networkType }) => { + ({ templateNetwork, rootNetwork, id, isBootable, name, mac, network, errors, networkType, binding }) => { const result = { id, isBootable, @@ -148,6 +153,7 @@ export class NetworksTab extends React.Component { errors, networkType, rootNetwork, + binding, }; if (templateNetwork) { @@ -173,15 +179,19 @@ export class NetworksTab extends React.Component { this.setState({ rows, editing: true }); }; - onRowUpdate = (rows, updatedRowId, editing) => { + onRowUpdate = (rows, updatedRowId, editing, property, newValue) => { const updatedRow = rows.find(r => r.id === updatedRowId); if (updatedRow.isBootable && updatedRow.network === POD_NETWORK) { updatedRow.isBootable = false; } - if (updatedRow.network === POD_NETWORK) { - updatedRow.networkType = NETWORK_TYPE_POD; - } else { - updatedRow.networkType = NETWORK_TYPE_MULTUS; + if (property === 'network') { + if (newValue === POD_NETWORK && updatedRow.networkType !== NETWORK_TYPE_POD) { + updatedRow.networkType = NETWORK_TYPE_POD; + updatedRow.binding = getDefaultNetworkBinding(NETWORK_TYPE_POD); + } else if (updatedRow.networkType !== NETWORK_TYPE_MULTUS) { + updatedRow.networkType = NETWORK_TYPE_MULTUS; + updatedRow.binding = getDefaultNetworkBinding(NETWORK_TYPE_MULTUS); + } } updatedRow.errors = validateNetwork(updatedRow); this.rowsChanged(rows, editing); @@ -206,6 +216,7 @@ export class NetworksTab extends React.Component { name: `nic${state.nextId - 1}`, mac: '', network: '', + binding: '', }, ], })); @@ -218,7 +229,7 @@ export class NetworksTab extends React.Component { label: HEADER_NAME, props: { style: { - width: '32%', + width: '24%', }, }, }, @@ -233,7 +244,7 @@ export class NetworksTab extends React.Component { label: HEADER_MAC, props: { style: { - width: '32%', + width: '24%', }, }, }, @@ -251,7 +262,7 @@ export class NetworksTab extends React.Component { label: HEADER_NETWORK, props: { style: { - width: '32%', + width: '24%', }, }, }, @@ -267,6 +278,23 @@ export class NetworksTab extends React.Component { initialValue: SELECT_NETWORK, }), }, + { + header: { + label: HEADER_BINDING_METHOD, + props: { + style: { + width: '24%', + }, + }, + }, + property: 'binding', + renderConfig: nic => ({ + id: 'binding-edit', + type: DROPDOWN, + choices: getNetworkBindings(nic.networkType), + initialValue: SELECT_BINDING, + }), + }, ]; if (!this.props.isCreateRemoveDisabled) { diff --git a/src/components/Wizard/CreateVmWizard/constants.js b/src/components/Wizard/CreateVmWizard/constants.js index 0c3941781..db82e2901 100644 --- a/src/components/Wizard/CreateVmWizard/constants.js +++ b/src/components/Wizard/CreateVmWizard/constants.js @@ -30,6 +30,9 @@ export const BATCH_CHANGES_KEY = 'internalBatchChanges'; // NetworksTab export const NETWORK_TYPE_MULTUS = 'multus'; export const NETWORK_TYPE_POD = 'pod'; +export const NETWORK_BINDING_MASQUERADE = 'masquerade'; +export const NETWORK_BINDING_BRIDGE = 'bridge'; +export const NETWORK_BINDING_SRIOV = 'sriov'; // StorageTab export const STORAGE_TYPE_PVC = 'pvc'; diff --git a/src/components/Wizard/CreateVmWizard/strings.js b/src/components/Wizard/CreateVmWizard/strings.js index 30651a5c4..87e6fd0d9 100644 --- a/src/components/Wizard/CreateVmWizard/strings.js +++ b/src/components/Wizard/CreateVmWizard/strings.js @@ -43,6 +43,7 @@ export const getVmwareOsString = osName => `Select matching for: ${osName}`; // NetworksTab export const SELECT_NETWORK = '--- Select Network Definition ---'; +export const SELECT_BINDING = '--- Select binding ---'; export const REMOVE_NIC_BUTTON = 'Remove NIC'; export const CREATE_NIC_BUTTON = 'Create NIC'; export const PXE_INFO = 'Pod network is not PXE bootable'; @@ -53,6 +54,7 @@ export const HEADER_MAC = 'MAC Address'; export const HEADER_NETWORK = 'Network Configuration'; export const ERROR_NETWORK_NOT_FOUND = 'Network config not found'; export const ERROR_NETWORK_NOT_SELECTED = 'Network config must be selected'; +export const HEADER_BINDING_METHOD = 'Binding method'; // StorageTab export const ERROR_NO_BOOTABLE_DISK = 'A bootable disk could not be found'; diff --git a/src/components/Wizard/CreateVmWizard/tests/NetworksTab.test.js b/src/components/Wizard/CreateVmWizard/tests/NetworksTab.test.js index 87e30fc4b..318ae5c3e 100644 --- a/src/components/Wizard/CreateVmWizard/tests/NetworksTab.test.js +++ b/src/components/Wizard/CreateVmWizard/tests/NetworksTab.test.js @@ -292,7 +292,7 @@ describe('', () => { const udpatedRows = cloneDeep(rows); udpatedRows[0].network = POD_NETWORK; - component.instance().onRowUpdate(udpatedRows, 0, true); + component.instance().onRowUpdate(udpatedRows, 0, true, 'network', POD_NETWORK); expect(component.state().rows[0].isBootable).toBeFalsy(); expect(component.state().rows[0].networkType).toEqual(NETWORK_TYPE_POD); @@ -301,7 +301,7 @@ describe('', () => { udpatedRows[1].network = NetworksTabFixture.props.networkConfigs[0].metadata.name; - component.instance().onRowUpdate(udpatedRows, 1, true); + component.instance().onRowUpdate(udpatedRows, 1, true, 'network', udpatedRows[1].network); expect(component.state().rows[0].isBootable).toBeFalsy(); expect(component.state().rows[0].networkType).toEqual(NETWORK_TYPE_POD); diff --git a/src/components/Wizard/CreateVmWizard/tests/__snapshots__/NetworksTab.test.js.snap b/src/components/Wizard/CreateVmWizard/tests/__snapshots__/NetworksTab.test.js.snap index 2895f8697..7b9bc780c 100644 --- a/src/components/Wizard/CreateVmWizard/tests/__snapshots__/NetworksTab.test.js.snap +++ b/src/components/Wizard/CreateVmWizard/tests/__snapshots__/NetworksTab.test.js.snap @@ -21,7 +21,7 @@ exports[` renders correctly 1`] = ` "label": "Name", "props": Object { "style": Object { - "width": "32%", + "width": "24%", }, }, }, @@ -33,7 +33,7 @@ exports[` renders correctly 1`] = ` "label": "MAC Address", "props": Object { "style": Object { - "width": "32%", + "width": "24%", }, }, }, @@ -45,13 +45,25 @@ exports[` renders correctly 1`] = ` "label": "Network Configuration", "props": Object { "style": Object { - "width": "32%", + "width": "24%", }, }, }, "property": "network", "renderConfig": [Function], }, + Object { + "header": Object { + "label": "Binding method", + "props": Object { + "style": Object { + "width": "24%", + }, + }, + }, + "property": "binding", + "renderConfig": [Function], + }, Object { "header": Object { "props": Object { diff --git a/src/k8s/vmBuilder.js b/src/k8s/vmBuilder.js index b18b9de32..7ac14a8a9 100644 --- a/src/k8s/vmBuilder.js +++ b/src/k8s/vmBuilder.js @@ -16,6 +16,9 @@ import { NETWORK_TYPE_POD, DATA_VOLUME_SOURCE_URL, DATA_VOLUME_SOURCE_BLANK, + NETWORK_BINDING_BRIDGE, + NETWORK_BINDING_MASQUERADE, + NETWORK_BINDING_SRIOV, } from '../components/Wizard/CreateVmWizard/constants'; import { getCloudInitVolume } from '../selectors'; @@ -95,6 +98,7 @@ export const addInterface = (vm, defaultInterface, network) => { ...(network.templateNetwork ? network.templateNetwork.interface : defaultInterface), name: network.name, }; + if (network.mac) { interfaceSpec.macAddress = network.mac; } @@ -104,10 +108,31 @@ export const addInterface = (vm, defaultInterface, network) => { interfaceSpec.bootOrder = interfaceSpec.bootOrder ? interfaceSpec.bootOrder : assignBootOrderIndex(vm); } + addBindingToInterface(interfaceSpec, network.binding); + const interfaces = getInterfaces(vm); interfaces.push(interfaceSpec); }; +export const addBindingToInterface = (interfaceSpec, binding) => { + delete interfaceSpec.bridge; + delete interfaceSpec.masquerade; + delete interfaceSpec.sriov; + + switch (binding) { + case NETWORK_BINDING_MASQUERADE: + interfaceSpec.masquerade = {}; + break; + case NETWORK_BINDING_SRIOV: + interfaceSpec.sriov = {}; + break; + case NETWORK_BINDING_BRIDGE: + default: + interfaceSpec.bridge = {}; + break; + } +}; + export const addNetwork = (vm, network) => { const networkSpec = { ...(network.templateNetwork ? network.templateNetwork.network : {}), diff --git a/src/selectors/vm/selectors.js b/src/selectors/vm/selectors.js index 7f71aed8f..b26bc9b7d 100644 --- a/src/selectors/vm/selectors.js +++ b/src/selectors/vm/selectors.js @@ -12,6 +12,11 @@ import { TEMPLATE_OS_NAME_ANNOTATION, DASHES, } from '../../constants'; +import { + NETWORK_BINDING_BRIDGE, + NETWORK_BINDING_SRIOV, + NETWORK_BINDING_MASQUERADE, +} from '../../components/Wizard/CreateVmWizard/constants'; export const getDisks = vm => get(vm, 'spec.template.spec.domain.devices.disks', []); export const getInterfaces = vm => get(vm, 'spec.template.spec.domain.devices.interfaces', []); @@ -71,3 +76,16 @@ export const getFlavorDescription = vm => { export const isVmRunning = vm => get(vm, 'spec.running', false); export const isVmReady = vm => get(vm, 'status.ready', false); export const isVmCreated = vm => get(vm, 'status.created', false); + +export const getInterfaceBinding = intface => { + if (intface.bridge) { + return NETWORK_BINDING_BRIDGE; + } + if (intface.sriov) { + return NETWORK_BINDING_SRIOV; + } + if (intface.masquerade) { + return NETWORK_BINDING_MASQUERADE; + } + return null; +}; diff --git a/src/utils/patches.js b/src/utils/patches.js index e6f49baff..15940af74 100644 --- a/src/utils/patches.js +++ b/src/utils/patches.js @@ -12,7 +12,7 @@ import { DEVICE_TYPE_INTERFACE, } from '../constants'; import { NETWORK_TYPE_POD } from '../components/Wizard/CreateVmWizard/constants'; -import { assignBootOrderIndex, getBootableDevicesInOrder, getDevices } from '../k8s/vmBuilder'; +import { assignBootOrderIndex, getBootableDevicesInOrder, getDevices, addBindingToInterface } from '../k8s/vmBuilder'; export const getPxeBootPatch = vm => { const patches = []; @@ -315,13 +315,14 @@ export const getAddNicPatch = (vm, nic) => { const i = { name: nic.name, model: nic.model, - bridge: {}, bootOrder: assignBootOrderIndex(vm), }; if (nic.mac) { i.macAddress = nic.mac; } + addBindingToInterface(i, nic.binding); + const network = { name: nic.name, }; diff --git a/src/utils/utils.js b/src/utils/utils.js index 0364ef0d3..2d4ae6452 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -1,5 +1,13 @@ import { NamespaceModel, ProjectModel } from '../models'; +import { + NETWORK_TYPE_POD, + NETWORK_TYPE_MULTUS, + NETWORK_BINDING_BRIDGE, + NETWORK_BINDING_SRIOV, + NETWORK_BINDING_MASQUERADE, +} from '../components/Wizard/CreateVmWizard/constants'; + export function prefixedId(idPrefix, id) { return idPrefix && id ? `${idPrefix}-${id}` : null; } @@ -92,3 +100,23 @@ export const formatNetTraffic = (bytesPerSecond, preferredUnit, fixed = 2) => { formatted.unit = `${formatted.unit}ps`; return formatted; }; + +export const getNetworkBindings = networkType => { + switch (networkType) { + case NETWORK_TYPE_MULTUS: + return [NETWORK_BINDING_BRIDGE, NETWORK_BINDING_SRIOV]; + case NETWORK_TYPE_POD: + default: + return [NETWORK_BINDING_MASQUERADE, NETWORK_BINDING_BRIDGE, NETWORK_BINDING_SRIOV]; + } +}; + +export const getDefaultNetworkBinding = networkType => { + switch (networkType) { + case NETWORK_TYPE_MULTUS: + return NETWORK_BINDING_BRIDGE; + case NETWORK_TYPE_POD: + default: + return NETWORK_BINDING_MASQUERADE; + } +};