| <template> |
| <b-container fluid="xl"> |
| <page-title :description="$t('pageNetwork.pageDescription')" /> |
| <page-section :section-title="$t('pageNetwork.interface')"> |
| <b-row> |
| <b-col lg="3"> |
| <b-form-group |
| label-for="interface-select" |
| :label="$t('pageNetwork.form.networkInterface')" |
| > |
| <b-form-select |
| id="interface-select" |
| v-model="selectedInterfaceIndex" |
| :disabled="loading" |
| data-test-id="network-select-interface" |
| :options="interfaceSelectOptions" |
| @change="selectInterface" |
| > |
| </b-form-select> |
| </b-form-group> |
| </b-col> |
| </b-row> |
| </page-section> |
| <b-form novalidate @submit.prevent="submitForm"> |
| <b-form-group :disabled="loading"> |
| <page-section :section-title="$t('pageNetwork.system')"> |
| <b-row> |
| <b-col lg="3"> |
| <b-form-group |
| :label="$t('pageNetwork.form.defaultGateway')" |
| label-for="default-gateway" |
| > |
| <b-form-input |
| id="default-gateway" |
| v-model.trim="form.gateway" |
| data-test-id="network-input-gateway" |
| type="text" |
| :state="getValidationState($v.form.gateway)" |
| @change="$v.form.gateway.$touch()" |
| /> |
| <b-form-invalid-feedback role="alert"> |
| <div v-if="!$v.form.gateway.required"> |
| {{ $t('global.form.fieldRequired') }} |
| </div> |
| <div v-if="!$v.form.gateway.ipAddress"> |
| {{ $t('global.form.invalidFormat') }} |
| </div> |
| </b-form-invalid-feedback> |
| </b-form-group> |
| </b-col> |
| <b-col lg="3"> |
| <b-form-group |
| :label="$t('pageNetwork.form.hostname')" |
| label-for="hostname-field" |
| > |
| <b-form-input |
| id="hostname-field" |
| v-model.trim="form.hostname" |
| data-test-id="network-input-hostname" |
| type="text" |
| :state="getValidationState($v.form.hostname)" |
| @change="$v.form.hostname.$touch()" |
| /> |
| <b-form-invalid-feedback role="alert"> |
| <div v-if="!$v.form.hostname.required"> |
| {{ $t('global.form.fieldRequired') }} |
| </div> |
| <div v-if="!$v.form.hostname.validateHostname"> |
| {{ |
| $t('global.form.lengthMustBeBetween', { min: 1, max: 64 }) |
| }} |
| </div> |
| </b-form-invalid-feedback> |
| </b-form-group> |
| </b-col> |
| <b-col lg="3"> |
| <b-form-group |
| :label="$t('pageNetwork.form.macAddress')" |
| label-for="mac-address" |
| > |
| <b-form-input |
| id="mac-address" |
| v-model.trim="form.macAddress" |
| data-test-id="network-input-macAddress" |
| type="text" |
| :state="getValidationState($v.form.macAddress)" |
| @change="$v.form.macAddress.$touch()" |
| /> |
| <b-form-invalid-feedback role="alert"> |
| <div v-if="!$v.form.macAddress.required"> |
| {{ $t('global.form.fieldRequired') }} |
| </div> |
| <div v-if="!$v.form.macAddress.macAddress"> |
| {{ $t('global.form.invalidFormat') }} |
| </div> |
| </b-form-invalid-feedback> |
| </b-form-group> |
| </b-col> |
| </b-row> |
| </page-section> |
| <page-section :section-title="$t('pageNetwork.ipv4')"> |
| <b-form-group :label="$t('pageNetwork.ipv4Configuration')"> |
| <b-form-text id="enable-secure-help-block"> |
| {{ $t('pageNetwork.ipv4Helper') }} |
| </b-form-text> |
| <b-form-radio |
| v-model="form.dhcpEnabled" |
| name="dhcp-radio" |
| :value="true" |
| @change="onChangeIpv4Config" |
| > |
| {{ $t('pageNetwork.dhcp') }} |
| </b-form-radio> |
| <b-form-radio |
| v-model="form.dhcpEnabled" |
| name="static-radio" |
| :value="false" |
| @change="onChangeIpv4Config" |
| > |
| {{ $t('pageNetwork.static') }} |
| </b-form-radio> |
| </b-form-group> |
| <b-row> |
| <b-col lg="9" class="mb-3"> |
| <h3 class="h4"> |
| {{ $t('pageNetwork.dhcp') }} |
| </h3> |
| <b-table |
| responsive="md" |
| hover |
| :fields="ipv4DhcpTableFields" |
| :items="form.ipv4DhcpTableItems" |
| :empty-text="$t('global.table.emptyMessage')" |
| class="mb-0" |
| show-empty |
| > |
| <template #cell(Address)="{ item, index }"> |
| <b-form-input |
| v-model.trim="item.Address" |
| :data-test-id="`network-input-dhcpIpv4-${index}`" |
| :aria-label=" |
| $t('pageNetwork.table.dhcpIpv4AddressRow') + |
| ' ' + |
| (index + 1) |
| " |
| readonly |
| /> |
| </template> |
| <template #cell(SubnetMask)="{ item, index }"> |
| <b-form-input |
| v-model.trim="item.SubnetMask" |
| :data-test-id="`network-input-subnetMask-${index}`" |
| :aria-label=" |
| $t('pageNetwork.table.dhcpIpv4SubnetRow') + |
| ' ' + |
| (index + 1) |
| " |
| readonly |
| /> |
| </template> |
| <template #cell(actions)="{ item, index }"> |
| <table-row-action |
| v-for="(action, actionIndex) in item.actions" |
| :key="actionIndex" |
| :value="action.value" |
| :title="action.title" |
| :enabled="false" |
| @click-table-action=" |
| onDeleteIpv4StaticTableRow($event, index) |
| " |
| > |
| <template #icon> |
| <icon-trashcan v-if="action.value === 'delete'" /> |
| </template> |
| </table-row-action> |
| </template> |
| </b-table> |
| </b-col> |
| <b-col lg="9" class="mb-3"> |
| <h3 class="h4"> |
| {{ $t('pageNetwork.static') }} |
| </h3> |
| <b-table |
| responsive="md" |
| hover |
| :fields="ipv4StaticTableFields" |
| :items="form.ipv4StaticTableItems" |
| :empty-text="$t('global.table.emptyMessage')" |
| class="mb-0" |
| show-empty |
| > |
| <template #cell(Address)="{ item, index }"> |
| <b-form-input |
| v-model.trim="item.Address" |
| :data-test-id="`network-input-staticIpv4-${index}`" |
| :aria-label=" |
| $t('pageNetwork.table.staticIpv4AddressRow') + |
| ' ' + |
| (index + 1) |
| " |
| :state=" |
| getValidationState( |
| $v.form.ipv4StaticTableItems.$each.$iter[index].Address |
| ) |
| " |
| @change=" |
| $v.form.ipv4StaticTableItems.$each.$iter[ |
| index |
| ].Address.$touch() |
| " |
| /> |
| <b-form-invalid-feedback role="alert"> |
| <div |
| v-if=" |
| !$v.form.ipv4StaticTableItems.$each.$iter[index].Address |
| .required |
| " |
| > |
| {{ $t('global.form.fieldRequired') }} |
| </div> |
| <div |
| v-if=" |
| !$v.form.ipv4StaticTableItems.$each.$iter[index].Address |
| .ipAddress |
| " |
| > |
| {{ $t('global.form.invalidFormat') }} |
| </div> |
| </b-form-invalid-feedback> |
| </template> |
| <template #cell(SubnetMask)="{ item, index }"> |
| <b-form-input |
| v-model.trim="item.SubnetMask" |
| :data-test-id="`network-input-subnetMask-${index}`" |
| :aria-label=" |
| $t('pageNetwork.table.staticIpv4SubnetRow') + |
| ' ' + |
| (index + 1) |
| " |
| :state=" |
| getValidationState( |
| $v.form.ipv4StaticTableItems.$each.$iter[index] |
| .SubnetMask |
| ) |
| " |
| @change=" |
| $v.form.ipv4StaticTableItems.$each.$iter[ |
| index |
| ].SubnetMask.$touch() |
| " |
| /> |
| <b-form-invalid-feedback role="alert"> |
| <div |
| v-if=" |
| !$v.form.ipv4StaticTableItems.$each.$iter[index] |
| .SubnetMask.required |
| " |
| > |
| {{ $t('global.form.fieldRequired') }} |
| </div> |
| <div |
| v-if=" |
| !$v.form.ipv4StaticTableItems.$each.$iter[index] |
| .SubnetMask.ipAddress |
| " |
| > |
| {{ $t('global.form.invalidFormat') }} |
| </div> |
| </b-form-invalid-feedback> |
| </template> |
| <template #cell(actions)="{ item, index }"> |
| <table-row-action |
| v-for="(action, actionIndex) in item.actions" |
| :key="actionIndex" |
| :value="action.value" |
| :title="action.title" |
| @click-table-action=" |
| onDeleteIpv4StaticTableRow($event, index) |
| " |
| > |
| <template #icon> |
| <icon-trashcan v-if="action.value === 'delete'" /> |
| </template> |
| </table-row-action> |
| </template> |
| </b-table> |
| <b-button variant="link" @click="addIpv4StaticTableRow"> |
| <icon-add /> |
| {{ $t('pageNetwork.table.addStaticIpv4Address') }} |
| </b-button> |
| </b-col> |
| </b-row> |
| </page-section> |
| <page-section :section-title="$t('pageNetwork.staticDns')"> |
| <b-row> |
| <b-col lg="4" class="mb-3"> |
| <b-table |
| responsive |
| hover |
| :fields="dnsTableFields" |
| :items="form.dnsStaticTableItems" |
| :empty-text="$t('global.table.emptyMessage')" |
| class="mb-0" |
| show-empty |
| > |
| <template #cell(address)="{ item, index }"> |
| <b-form-input |
| v-model.trim="item.address" |
| :data-test-id="`network-input-dnsAddress-${index}`" |
| :aria-label=" |
| $t('pageNetwork.table.staticDnsRow') + ' ' + (index + 1) |
| " |
| :state=" |
| getValidationState( |
| $v.form.dnsStaticTableItems.$each.$iter[index].address |
| ) |
| " |
| @change=" |
| $v.form.dnsStaticTableItems.$each.$iter[ |
| index |
| ].address.$touch() |
| " |
| /> |
| <b-form-invalid-feedback role="alert"> |
| <div |
| v-if=" |
| !$v.form.dnsStaticTableItems.$each.$iter[index].address |
| .required |
| " |
| > |
| {{ $t('global.form.fieldRequired') }} |
| </div> |
| <div |
| v-if=" |
| !$v.form.dnsStaticTableItems.$each.$iter[index].address |
| .ipAddress |
| " |
| > |
| {{ $t('global.form.invalidFormat') }} |
| </div> |
| </b-form-invalid-feedback> |
| </template> |
| <template #cell(actions)="{ item, index }"> |
| <table-row-action |
| v-for="(action, actionIndex) in item.actions" |
| :key="actionIndex" |
| :value="action.value" |
| :title="action.title" |
| @click-table-action="onDeleteDnsTableRow($event, index)" |
| > |
| <template #icon> |
| <icon-trashcan v-if="action.value === 'delete'" /> |
| </template> |
| </table-row-action> |
| </template> |
| </b-table> |
| <b-button variant="link" @click="addDnsTableRow"> |
| <icon-add /> {{ $t('pageNetwork.table.addDns') }} |
| </b-button> |
| </b-col> |
| </b-row> |
| </page-section> |
| <b-button |
| variant="primary" |
| type="submit" |
| data-test-id="network-button-saveNetworkSettings" |
| > |
| {{ $t('global.action.saveSettings') }} |
| </b-button> |
| </b-form-group> |
| </b-form> |
| </b-container> |
| </template> |
| |
| <script> |
| import IconTrashcan from '@carbon/icons-vue/es/trash-can/20'; |
| import IconAdd from '@carbon/icons-vue/es/add--alt/20'; |
| import BVToastMixin from '@/components/Mixins/BVToastMixin'; |
| import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin'; |
| import PageSection from '@/components/Global/PageSection'; |
| import PageTitle from '@/components/Global/PageTitle'; |
| import TableRowAction from '@/components/Global/TableRowAction'; |
| import VuelidateMixin from '@/components/Mixins/VuelidateMixin'; |
| import { mapState } from 'vuex'; |
| import { |
| required, |
| helpers, |
| ipAddress, |
| macAddress, |
| } from 'vuelidate/lib/validators'; |
| |
| // Hostname pattern |
| const validateHostname = helpers.regex('validateHostname', /^\S{0,64}$/); |
| |
| export default { |
| name: 'Network', |
| components: { |
| PageTitle, |
| PageSection, |
| TableRowAction, |
| IconTrashcan, |
| IconAdd, |
| }, |
| mixins: [BVToastMixin, VuelidateMixin, LoadingBarMixin], |
| beforeRouteLeave(to, from, next) { |
| this.hideLoader(); |
| next(); |
| }, |
| data() { |
| return { |
| ipv4DhcpTableFields: [ |
| { |
| key: 'Address', |
| label: this.$t('pageNetwork.table.ipAddress'), |
| }, |
| { |
| key: 'SubnetMask', |
| label: this.$t('pageNetwork.table.subnet'), |
| }, |
| { key: 'actions', label: '', tdClass: 'text-right' }, |
| ], |
| ipv4StaticTableFields: [ |
| { |
| key: 'Address', |
| label: this.$t('pageNetwork.table.ipAddress'), |
| }, |
| { |
| key: 'SubnetMask', |
| label: this.$t('pageNetwork.table.subnet'), |
| }, |
| { key: 'actions', label: '', tdClass: 'text-right' }, |
| ], |
| dnsTableFields: [ |
| { |
| key: 'address', |
| label: this.$t('pageNetwork.table.ipAddress'), |
| }, |
| { key: 'actions', label: '', tdClass: 'text-right' }, |
| ], |
| selectedInterfaceIndex: 0, |
| selectedInterface: {}, |
| form: { |
| dhcpEnabled: null, |
| gateway: '', |
| hostname: '', |
| macAddress: '', |
| ipv4StaticTableItems: [], |
| ipv4DhcpTableItems: [], |
| dnsStaticTableItems: [], |
| }, |
| loading, |
| }; |
| }, |
| validations() { |
| return { |
| form: { |
| gateway: { required, ipAddress }, |
| hostname: { required, validateHostname }, |
| ipv4StaticTableItems: { |
| $each: { |
| Address: { |
| required, |
| ipAddress, |
| }, |
| SubnetMask: { |
| required, |
| ipAddress, |
| }, |
| }, |
| }, |
| macAddress: { required, macAddress: macAddress() }, |
| dnsStaticTableItems: { |
| $each: { |
| address: { |
| required, |
| ipAddress, |
| }, |
| }, |
| }, |
| }, |
| }; |
| }, |
| computed: { |
| ...mapState('network', [ |
| 'ethernetData', |
| 'interfaceOptions', |
| 'defaultGateway', |
| ]), |
| interfaceSelectOptions() { |
| return this.interfaceOptions.map((option, index) => { |
| return { |
| text: option, |
| value: index, |
| }; |
| }); |
| }, |
| }, |
| watch: { |
| ethernetData: function () { |
| this.selectInterface(); |
| }, |
| }, |
| created() { |
| this.startLoader(); |
| this.$store |
| .dispatch('network/getEthernetData') |
| .finally(() => this.endLoader()); |
| }, |
| methods: { |
| selectInterface() { |
| this.selectedInterface = this.ethernetData[this.selectedInterfaceIndex]; |
| this.getIpv4DhcpTableItems(); |
| this.getIpv4StaticTableItems(); |
| this.getDnsStaticTableItems(); |
| this.getInterfaceSettings(); |
| }, |
| getInterfaceSettings() { |
| this.form.gateway = this.defaultGateway; |
| this.form.hostname = this.selectedInterface.HostName; |
| this.form.macAddress = this.selectedInterface.MACAddress; |
| this.form.dhcpEnabled = this.selectedInterface.DHCPv4.DHCPEnabled; |
| }, |
| onChangeIpv4Config(value) { |
| this.form.dhcpEnabled = value; |
| }, |
| getDnsStaticTableItems() { |
| const dns = this.selectedInterface.StaticNameServers || []; |
| this.form.dnsStaticTableItems = dns.map((server) => { |
| return { |
| address: server, |
| actions: [ |
| { |
| value: 'delete', |
| enabled: this.form.dhcpEnabled, |
| title: this.$t('pageNetwork.table.deleteDns'), |
| }, |
| ], |
| }; |
| }); |
| }, |
| addDnsTableRow() { |
| this.$v.form.dnsStaticTableItems.$touch(); |
| this.form.dnsStaticTableItems.push({ |
| address: '', |
| actions: [ |
| { |
| value: 'delete', |
| enabled: this.form.dhcpEnabled, |
| title: this.$t('pageNetwork.table.deleteDns'), |
| }, |
| ], |
| }); |
| }, |
| deleteDnsTableRow(index) { |
| this.$v.form.dnsStaticTableItems.$touch(); |
| this.form.dnsStaticTableItems.splice(index, 1); |
| }, |
| onDeleteDnsTableRow(action, row) { |
| this.deleteDnsTableRow(row); |
| }, |
| getIpv4DhcpTableItems() { |
| const addresses = this.selectedInterface.IPv4Addresses || []; |
| this.form.ipv4DhcpTableItems = addresses |
| .filter((ipv4) => ipv4.AddressOrigin === 'DHCP') |
| .map((ipv4) => { |
| return { |
| Address: ipv4.Address, |
| SubnetMask: ipv4.SubnetMask, |
| actions: [ |
| { |
| value: 'delete', |
| enabled: false, |
| title: this.$t('pageNetwork.table.deleteDhcpIpv4'), |
| }, |
| ], |
| }; |
| }); |
| }, |
| getIpv4StaticTableItems() { |
| const addresses = this.selectedInterface.IPv4StaticAddresses || []; |
| this.form.ipv4StaticTableItems = addresses.map((ipv4) => { |
| return { |
| Address: ipv4.Address, |
| SubnetMask: ipv4.SubnetMask, |
| actions: [ |
| { |
| value: 'delete', |
| enabled: this.form.dhcpEnabled, |
| title: this.$t('pageNetwork.table.deleteStaticIpv4'), |
| }, |
| ], |
| }; |
| }); |
| }, |
| addIpv4StaticTableRow() { |
| this.$v.form.ipv4StaticTableItems.$touch(); |
| this.form.ipv4StaticTableItems.push({ |
| Address: '', |
| SubnetMask: '', |
| actions: [ |
| { |
| value: 'delete', |
| enabled: this.form.dhcpEnabled, |
| title: this.$t('pageNetwork.table.deleteStaticIpv4'), |
| }, |
| ], |
| }); |
| }, |
| deleteIpv4StaticTableRow(index) { |
| this.$v.form.ipv4StaticTableItems.$touch(); |
| this.form.ipv4StaticTableItems.splice(index, 1); |
| }, |
| onDeleteIpv4StaticTableRow(action, row) { |
| this.deleteIpv4StaticTableRow(row); |
| }, |
| submitForm() { |
| this.$v.$touch(); |
| if (this.$v.$invalid) return; |
| this.startLoader(); |
| let networkInterfaceSelected = this.selectedInterface; |
| let selectedInterfaceIndex = this.selectedInterfaceIndex; |
| let interfaceId = networkInterfaceSelected.Id; |
| let isDhcpEnabled = this.form.dhcpEnabled; |
| let macAddress = this.form.macAddress; |
| let hostname = this.form.hostname; |
| let networkSettingsForm = { |
| interfaceId, |
| hostname, |
| macAddress, |
| selectedInterfaceIndex, |
| }; |
| // Enabling DHCP without any available IP addresses will bring network down |
| if (this.form.ipv4DhcpTableItems.length) { |
| networkSettingsForm.isDhcpEnabled = isDhcpEnabled; |
| } else { |
| networkSettingsForm.isDhcpEnabled = false; |
| this.errorToast(this.$t('pageNetwork.toast.errorSaveDhcpSettings')); |
| } |
| networkSettingsForm.staticIpv4 = this.form.ipv4StaticTableItems.map( |
| (updateIpv4) => { |
| delete updateIpv4.actions; |
| updateIpv4.Gateway = this.form.gateway; |
| return updateIpv4; |
| } |
| ); |
| networkSettingsForm.staticNameServers = this.form.dnsStaticTableItems.map( |
| (updateDns) => { |
| return updateDns.address; |
| } |
| ); |
| this.$store |
| .dispatch('network/updateInterfaceSettings', networkSettingsForm) |
| .then((success) => { |
| this.successToast(success); |
| }) |
| .catch(({ message }) => this.errorToast(message)) |
| .finally(() => { |
| this.$v.form.$reset(); |
| this.endLoader(); |
| }); |
| }, |
| }, |
| }; |
| </script> |