summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/components/AppNavigation/AppNavigation.vue2
-rw-r--r--src/router/index.js8
-rw-r--r--src/store/modules/Configuration/NetworkSettingsStore.js71
-rw-r--r--src/views/Configuration/NetworkSettings/NetworkSettings.vue492
-rw-r--r--src/views/Configuration/NetworkSettings/index.js2
5 files changed, 571 insertions, 4 deletions
diff --git a/src/components/AppNavigation/AppNavigation.vue b/src/components/AppNavigation/AppNavigation.vue
index b7a3e071..8103558e 100644
--- a/src/components/AppNavigation/AppNavigation.vue
+++ b/src/components/AppNavigation/AppNavigation.vue
@@ -59,7 +59,7 @@
<b-nav-item href="javascript:void(0)">
{{ $t('appNavigation.firmware') }}
</b-nav-item>
- <b-nav-item href="javascript:void(0)">
+ <b-nav-item to="/configuration/network-settings">
{{ $t('appNavigation.networkSettings') }}
</b-nav-item>
<b-nav-item href="javascript:void(0)">
diff --git a/src/router/index.js b/src/router/index.js
index 30532a5f..e35e0f59 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -72,6 +72,14 @@ const routes = [
}
},
{
+ path: '/configuration/network-settings',
+ name: 'network-settings',
+ component: () => import('@/views/Configuration/NetworkSettings'),
+ meta: {
+ title: 'appPageTitle.networkSettings'
+ }
+ },
+ {
path: '/control/reboot-bmc',
name: 'reboot-bmc',
component: () => import('@/views/Control/RebootBmc'),
diff --git a/src/store/modules/Configuration/NetworkSettingsStore.js b/src/store/modules/Configuration/NetworkSettingsStore.js
index f6912c87..524ad342 100644
--- a/src/store/modules/Configuration/NetworkSettingsStore.js
+++ b/src/store/modules/Configuration/NetworkSettingsStore.js
@@ -1,16 +1,25 @@
import api from '../../api';
+import { find, remove } from 'lodash';
const NetworkSettingsStore = {
namespaced: true,
state: {
- ethernetData: []
+ defaultGateway: '',
+ ethernetData: [],
+ interfaceOptions: []
},
getters: {
- ethernetData: state => state.ethernetData
+ defaultGateway: state => state.defaultGateway,
+ ethernetData: state => state.ethernetData,
+ interfaceOptions: state => state.interfaceOptions
},
mutations: {
+ setDefaultGateway: (state, defaultGateway) =>
+ (state.defaultGateway = defaultGateway),
setEthernetData: (state, ethernetData) =>
- (state.ethernetData = ethernetData)
+ (state.ethernetData = ethernetData),
+ setInterfaceOptions: (state, interfaceOptions) =>
+ (state.interfaceOptions = interfaceOptions)
},
actions: {
async getEthernetData({ commit }) {
@@ -32,11 +41,67 @@ const NetworkSettingsStore = {
const ethernetData = ethernetInterfaces.map(
ethernetInterface => ethernetInterface.data
);
+ const interfaceOptions = ethernetInterfaces.map(
+ ethernetName => ethernetName.data.Id
+ );
+ const addresses = ethernetData[0].IPv4StaticAddresses;
+
+ // Default gateway manually set to first gateway saved on the first interface. Default gateway property is WIP on backend
+ const defaultGateway = addresses.map(ipv4 => {
+ return ipv4.Gateway;
+ });
+
+ commit('setDefaultGateway', defaultGateway[0]);
commit('setEthernetData', ethernetData);
+ commit('setInterfaceOptions', interfaceOptions);
})
.catch(error => {
console.log('Network Data:', error);
});
+ },
+
+ async updateInterfaceSettings({ dispatch, state }, networkSettingsForm) {
+ const updatedAddresses = networkSettingsForm.staticIpv4;
+ const originalAddresses =
+ state.ethernetData[networkSettingsForm.selectedInterfaceIndex]
+ .IPv4StaticAddresses;
+
+ const addressArray = originalAddresses.map(item => {
+ const address = item.Address;
+ if (find(updatedAddresses, { Address: address })) {
+ remove(updatedAddresses, item => {
+ return item.Address === address;
+ });
+ return {};
+ } else {
+ return null;
+ }
+ });
+
+ const data = {
+ HostName: networkSettingsForm.hostname,
+ MACAddress: networkSettingsForm.macAddress
+ };
+
+ // If DHCP disabled, update static DNS or static ipv4
+ if (!networkSettingsForm.isDhcpEnabled) {
+ data.IPv4StaticAddresses = [...addressArray, ...updatedAddresses];
+ data.StaticNameServers = networkSettingsForm.staticNameServers;
+ }
+
+ return await api
+ .patch(
+ `/redfish/v1/Managers/bmc/EthernetInterfaces/${networkSettingsForm.interfaceId}`,
+ data
+ )
+ .then(() => dispatch('getEthernetData'))
+ .then(() => {
+ return 'Successfully configured network settings.';
+ })
+ .catch(error => {
+ console.log(error);
+ throw new Error('Error configuring network settings.');
+ });
}
}
};
diff --git a/src/views/Configuration/NetworkSettings/NetworkSettings.vue b/src/views/Configuration/NetworkSettings/NetworkSettings.vue
new file mode 100644
index 00000000..18e73a04
--- /dev/null
+++ b/src/views/Configuration/NetworkSettings/NetworkSettings.vue
@@ -0,0 +1,492 @@
+<template>
+ <b-container fluid="xl">
+ <page-title
+ description="Configure network settings for the BMC and the Virtualization management interface"
+ />
+ <page-section section-title="Interface">
+ <b-row>
+ <b-col lg="3">
+ <b-form-group label-for="interface-select" label="Network interface">
+ <b-form-select
+ id="interface-select"
+ v-model="selectedInterfaceIndex"
+ :options="interfaceSelectOptions"
+ @change="selectInterface"
+ >
+ </b-form-select>
+ </b-form-group>
+ </b-col>
+ </b-row>
+ </page-section>
+ <b-form novalidate @submit.prevent="submitForm">
+ <page-section section-title="System">
+ <b-row>
+ <b-col lg="3">
+ <b-form-group label="Default gateway" label-for="default-gateway">
+ <b-form-input
+ id="default-gateway"
+ v-model.trim="form.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">Field required</div>
+ <div v-if="!$v.form.gateway.validateAddress">Invalid</div>
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </b-col>
+ <b-col lg="3">
+ <b-form-group label="Hostname" label-for="hostname-field">
+ <b-form-input
+ id="hostname-field"
+ v-model.trim="form.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">Field required</div>
+ <div v-if="!$v.form.hostname.validateHostname">
+ Must be less than 64 characters
+ </div>
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </b-col>
+ <b-col lg="3">
+ <b-form-group label="MAC address" label-for="mac-address">
+ <b-form-input
+ id="mac-address"
+ v-model.trim="form.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">Field required</div>
+ <div v-if="!$v.form.macAddress.validateMacAddress">Invalid</div>
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </b-col>
+ </b-row>
+ </page-section>
+ <page-section section-title="Static IPv4">
+ <b-row>
+ <b-col lg="9" class="mb-3">
+ <b-table
+ :fields="ipv4StaticTableFields"
+ :items="form.ipv4StaticTableItems"
+ class="mb-0"
+ >
+ <template v-slot:cell(Address)="{ item, index }">
+ <b-form-input
+ v-model.trim="item.Address"
+ :aria-label="'Static IPV4 address ' + (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
+ "
+ >
+ Field required
+ </div>
+ <div
+ v-if="
+ !$v.form.ipv4StaticTableItems.$each.$iter[index].Address
+ .validateAddress
+ "
+ >
+ Invalid
+ </div>
+ </b-form-invalid-feedback>
+ </template>
+ <template v-slot:cell(SubnetMask)="{ item, index }">
+ <b-form-input
+ v-model.trim="item.SubnetMask"
+ :aria-label="'Static IPV4 Subnet mask ' + (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
+ "
+ >
+ Field required
+ </div>
+ <div
+ v-if="
+ !$v.form.ipv4StaticTableItems.$each.$iter[index]
+ .SubnetMask.validateAddress
+ "
+ >
+ Invalid
+ </div>
+ </b-form-invalid-feedback>
+ </template>
+ <template v-slot:cell(actions)="{ item, index }">
+ <table-row-action
+ v-for="(action, actionIndex) in item.actions"
+ :key="actionIndex"
+ :value="action.value"
+ :title="action.title"
+ @click:tableAction="onDeleteIpv4StaticTableRow($event, index)"
+ >
+ <template v-slot:icon>
+ <icon-trashcan v-if="action.value === 'delete'" />
+ </template>
+ </table-row-action>
+ </template>
+ </b-table>
+ <b-button variant="link" @click="addIpv4StaticTableRow">
+ <icon-add /> Add static IP
+ </b-button>
+ </b-col>
+ </b-row>
+ </page-section>
+ <page-section section-title="Static DNS">
+ <b-row>
+ <b-col lg="4" class="mb-3">
+ <b-table
+ :fields="dnsTableFields"
+ :items="form.dnsStaticTableItems"
+ class="mb-0"
+ >
+ <template v-slot:cell(address)="{ item, index }">
+ <b-form-input
+ v-model.trim="item.address"
+ :aria-label="'Static DNS ' + (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
+ "
+ >
+ Field required
+ </div>
+ <div
+ v-if="
+ !$v.form.dnsStaticTableItems.$each.$iter[index].address
+ .validateAddress
+ "
+ >
+ Invalid
+ </div>
+ </b-form-invalid-feedback>
+ </template>
+ <template v-slot:cell(actions)="{ item, index }">
+ <table-row-action
+ v-for="(action, actionIndex) in item.actions"
+ :key="actionIndex"
+ :value="action.value"
+ :title="action.title"
+ @click:tableAction="onDeleteDnsTableRow($event, index)"
+ >
+ <template v-slot:icon>
+ <icon-trashcan v-if="action.value === 'delete'" />
+ </template>
+ </table-row-action>
+ </template>
+ </b-table>
+ <b-button variant="link" @click="addDnsTableRow">
+ <icon-add /> Add DNS server
+ </b-button>
+ </b-col>
+ </b-row>
+ </page-section>
+ <b-button
+ variant="primary"
+ type="submit"
+ :disabled="!$v.form.$anyDirty || $v.form.$invalid"
+ >
+ Save settings
+ </b-button>
+ </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],
+ data() {
+ return {
+ dhcpEnabled: null,
+ ipv4Configuration: '',
+ ipv4StaticTableFields: [
+ { key: 'Address', label: 'IP address' },
+ { key: 'SubnetMask', label: 'Subnet mask' },
+ { key: 'actions', label: '', tdClass: 'text-right' }
+ ],
+ dnsTableFields: [
+ { key: 'address', label: 'IP address' },
+ { 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());
+ },
+ beforeRouteLeave(to, from, next) {
+ this.hideLoader();
+ next();
+ },
+ 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: 'delete static dns row'
+ }
+ ]
+ };
+ });
+ },
+ addDnsTableRow() {
+ this.$v.form.dnsStaticTableItems.$touch();
+ this.form.dnsStaticTableItems.push({
+ address: '',
+ actions: [
+ {
+ value: 'delete',
+ enabled: this.dhcpEnabled,
+ title: 'delete static dns row'
+ }
+ ]
+ });
+ },
+ 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: 'delete static ipv4 row'
+ }
+ ]
+ };
+ });
+ },
+ addIpv4StaticTableRow() {
+ this.$v.form.ipv4StaticTableItems.$touch();
+ this.form.ipv4StaticTableItems.push({
+ Address: '',
+ SubnetMask: '',
+ actions: [
+ {
+ value: 'delete',
+ enabled: this.dhcpEnabled,
+ title: 'delete static ipv4 row'
+ }
+ ]
+ });
+ },
+ deleteIpv4StaticTableRow(index) {
+ this.$v.form.ipv4StaticTableItems.$touch();
+ this.form.ipv4StaticTableItems.splice(index, 1);
+ },
+ onDeleteIpv4StaticTableRow(action, row) {
+ this.deleteIpv4StaticTableRow(row);
+ },
+ submitForm() {
+ 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>
diff --git a/src/views/Configuration/NetworkSettings/index.js b/src/views/Configuration/NetworkSettings/index.js
new file mode 100644
index 00000000..1215e1c1
--- /dev/null
+++ b/src/views/Configuration/NetworkSettings/index.js
@@ -0,0 +1,2 @@
+import NetworkSettings from './NetworkSettings.vue';
+export default NetworkSettings;