blob: 3260dffb669471616bcc62d3ea0fe8fb0d7514ee [file] [log] [blame]
<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>