| <template> |
| <b-container fluid="xl"> |
| <page-title :description="$t('pageNetworkSettings.pageDescription')" /> |
| <page-section :section-title="$t('pageNetworkSettings.interface')"> |
| <b-row> |
| <b-col lg="3"> |
| <b-form-group |
| label-for="interface-select" |
| :label="$t('pageNetworkSettings.form.networkInterface')" |
| > |
| <b-form-select |
| id="interface-select" |
| v-model="selectedInterfaceIndex" |
| :disabled="loading" |
| data-test-id="networkSettings-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('pageNetworkSettings.system')"> |
| <b-row> |
| <b-col lg="3"> |
| <b-form-group |
| :label="$t('pageNetworkSettings.form.defaultGateway')" |
| label-for="default-gateway" |
| > |
| <b-form-input |
| id="default-gateway" |
| v-model.trim="form.gateway" |
| data-test-id="networkSettings-input-gateway" |
| type="text" |
| :readonly="dhcpEnabled" |
| :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.validateAddress"> |
| {{ $t('global.form.invalidFormat') }} |
| </div> |
| </b-form-invalid-feedback> |
| </b-form-group> |
| </b-col> |
| <b-col lg="3"> |
| <b-form-group |
| :label="$t('pageNetworkSettings.form.hostname')" |
| label-for="hostname-field" |
| > |
| <b-form-input |
| id="hostname-field" |
| v-model.trim="form.hostname" |
| data-test-id="networkSettings-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('pageNetworkSettings.form.macAddress')" |
| label-for="mac-address" |
| > |
| <b-form-input |
| id="mac-address" |
| v-model.trim="form.macAddress" |
| data-test-id="networkSettings-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.validateMacAddress"> |
| {{ $t('global.form.invalidFormat') }} |
| </div> |
| </b-form-invalid-feedback> |
| </b-form-group> |
| </b-col> |
| </b-row> |
| </page-section> |
| <page-section :section-title="$t('pageNetworkSettings.staticIpv4')"> |
| <b-row> |
| <b-col lg="9" class="mb-3"> |
| <b-table |
| responsive="md" |
| hover |
| :fields="ipv4StaticTableFields" |
| :items="form.ipv4StaticTableItems" |
| class="mb-0" |
| > |
| <template #cell(Address)="{ item, index }"> |
| <b-form-input |
| v-model.trim="item.Address" |
| :data-test-id="`networkSettings-input-staticIpv4-${index}`" |
| :aria-label=" |
| $t('pageNetworkSettings.ariaLabel.staticIpv4AddressRow') + |
| ' ' + |
| (index + 1) |
| " |
| :readonly="dhcpEnabled" |
| :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 |
| .validateAddress |
| " |
| > |
| {{ $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="`networkSettings-input-subnetMask-${index}`" |
| :aria-label=" |
| $t('pageNetworkSettings.ariaLabel.staticIpv4SubnetRow') + |
| ' ' + |
| (index + 1) |
| " |
| :readonly="dhcpEnabled" |
| :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.validateAddress |
| " |
| > |
| {{ $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('pageNetworkSettings.table.addStaticIpv4Address') }} |
| </b-button> |
| </b-col> |
| </b-row> |
| </page-section> |
| <page-section :section-title="$t('pageNetworkSettings.staticDns')"> |
| <b-row> |
| <b-col lg="4" class="mb-3"> |
| <b-table |
| responsive |
| hover |
| :fields="dnsTableFields" |
| :items="form.dnsStaticTableItems" |
| class="mb-0" |
| > |
| <template #cell(address)="{ item, index }"> |
| <b-form-input |
| v-model.trim="item.address" |
| :data-test-id="`networkSettings-input-dnsAddress-${index}`" |
| :aria-label=" |
| $t('pageNetworkSettings.ariaLabel.staticDnsRow') + |
| ' ' + |
| (index + 1) |
| " |
| :readonly="dhcpEnabled" |
| :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 |
| .validateAddress |
| " |
| > |
| {{ $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('pageNetworkSettings.table.addDns') }} |
| </b-button> |
| </b-col> |
| </b-row> |
| </page-section> |
| <b-button |
| variant="primary" |
| type="submit" |
| data-test-id="networkSettings-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 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 } from 'vuelidate/lib/validators'; |
| |
| // IP address, gateway and subnet pattern |
| const validateAddress = helpers.regex( |
| 'validateAddress', |
| /^(?=.*[^.]$)((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.?){4}$/ |
| ); |
| // Hostname pattern |
| const validateHostname = helpers.regex('validateHostname', /^\S{0,64}$/); |
| // MAC address pattern |
| const validateMacAddress = helpers.regex( |
| 'validateMacAddress', |
| /^(?:[0-9A-Fa-f]{2}([:-]?)[0-9A-Fa-f]{2})(?:(?:\1|\.)(?:[0-9A-Fa-f]{2}([:-]?)[0-9A-Fa-f]{2})){2}$/ |
| ); |
| |
| export default { |
| name: 'NetworkSettings', |
| components: { |
| PageTitle, |
| PageSection, |
| TableRowAction, |
| IconTrashcan, |
| IconAdd, |
| }, |
| mixins: [BVToastMixin, VuelidateMixin, LoadingBarMixin], |
| beforeRouteLeave(to, from, next) { |
| this.hideLoader(); |
| next(); |
| }, |
| data() { |
| return { |
| dhcpEnabled: null, |
| ipv4Configuration: '', |
| ipv4StaticTableFields: [ |
| { |
| key: 'Address', |
| label: this.$t('pageNetworkSettings.table.ipAddress'), |
| }, |
| { |
| key: 'SubnetMask', |
| label: this.$t('pageNetworkSettings.table.subnet'), |
| }, |
| { key: 'actions', label: '', tdClass: 'text-right' }, |
| ], |
| dnsTableFields: [ |
| { |
| key: 'address', |
| label: this.$t('pageNetworkSettings.table.ipAddress'), |
| }, |
| { key: 'actions', label: '', tdClass: 'text-right' }, |
| ], |
| selectedInterfaceIndex: 0, |
| selectedInterface: {}, |
| form: { |
| gateway: '', |
| hostname: '', |
| macAddress: '', |
| ipv4StaticTableItems: [], |
| dnsStaticTableItems: [], |
| }, |
| }; |
| }, |
| validations() { |
| return { |
| form: { |
| gateway: { required, validateAddress }, |
| hostname: { required, validateHostname }, |
| ipv4StaticTableItems: { |
| $each: { |
| Address: { |
| required, |
| validateAddress, |
| }, |
| SubnetMask: { |
| required, |
| validateAddress, |
| }, |
| }, |
| }, |
| macAddress: { required, validateMacAddress }, |
| dnsStaticTableItems: { |
| $each: { |
| address: { |
| required, |
| validateAddress, |
| }, |
| }, |
| }, |
| }, |
| }; |
| }, |
| computed: { |
| ...mapState('networkSettings', [ |
| '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('networkSettings/getEthernetData') |
| .finally(() => this.endLoader()); |
| }, |
| methods: { |
| selectInterface() { |
| this.selectedInterface = this.ethernetData[this.selectedInterfaceIndex]; |
| 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.dhcpEnabled = this.selectedInterface.DHCPv4.DHCPEnabled; |
| }, |
| getDnsStaticTableItems() { |
| const dns = this.selectedInterface.StaticNameServers || []; |
| this.form.dnsStaticTableItems = dns.map((server) => { |
| return { |
| address: server, |
| actions: [ |
| { |
| value: 'delete', |
| enabled: this.dhcpEnabled, |
| title: this.$t('pageNetworkSettings.table.deleteDns'), |
| }, |
| ], |
| }; |
| }); |
| }, |
| addDnsTableRow() { |
| this.$v.form.dnsStaticTableItems.$touch(); |
| this.form.dnsStaticTableItems.push({ |
| address: '', |
| actions: [ |
| { |
| value: 'delete', |
| enabled: this.dhcpEnabled, |
| title: this.$t('pageNetworkSettings.table.deleteDns'), |
| }, |
| ], |
| }); |
| }, |
| deleteDnsTableRow(index) { |
| this.$v.form.dnsStaticTableItems.$touch(); |
| this.form.dnsStaticTableItems.splice(index, 1); |
| }, |
| onDeleteDnsTableRow(action, row) { |
| this.deleteDnsTableRow(row); |
| }, |
| getIpv4StaticTableItems() { |
| const addresses = this.selectedInterface.IPv4StaticAddresses || []; |
| this.form.ipv4StaticTableItems = addresses.map((ipv4) => { |
| return { |
| Address: ipv4.Address, |
| SubnetMask: ipv4.SubnetMask, |
| actions: [ |
| { |
| value: 'delete', |
| enabled: this.dhcpEnabled, |
| title: this.$t('pageNetworkSettings.table.deleteStaticIpv4'), |
| }, |
| ], |
| }; |
| }); |
| }, |
| addIpv4StaticTableRow() { |
| this.$v.form.ipv4StaticTableItems.$touch(); |
| this.form.ipv4StaticTableItems.push({ |
| Address: '', |
| SubnetMask: '', |
| actions: [ |
| { |
| value: 'delete', |
| enabled: this.dhcpEnabled, |
| title: this.$t('pageNetworkSettings.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 = networkInterfaceSelected.DHCPv4.DHCPEnabled; |
| let macAddress = this.form.macAddress; |
| let hostname = this.form.hostname; |
| let networkSettingsForm = { |
| interfaceId, |
| hostname, |
| macAddress, |
| selectedInterfaceIndex, |
| isDhcpEnabled, |
| }; |
| 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( |
| 'networkSettings/updateInterfaceSettings', |
| networkSettingsForm |
| ) |
| .then((success) => { |
| this.successToast(success); |
| }) |
| .catch(({ message }) => this.errorToast(message)) |
| .finally(() => { |
| this.$v.form.$reset(); |
| this.endLoader(); |
| }); |
| }, |
| }, |
| }; |
| </script> |