blob: 18e73a0434022e72e2328bc2b9b4d13e8646063e [file] [log] [blame]
Dixsie Wolmersbb81d552020-02-26 19:52:28 -06001<template>
2 <b-container fluid="xl">
3 <page-title
4 description="Configure network settings for the BMC and the Virtualization management interface"
5 />
6 <page-section section-title="Interface">
7 <b-row>
8 <b-col lg="3">
9 <b-form-group label-for="interface-select" label="Network interface">
10 <b-form-select
11 id="interface-select"
12 v-model="selectedInterfaceIndex"
13 :options="interfaceSelectOptions"
14 @change="selectInterface"
15 >
16 </b-form-select>
17 </b-form-group>
18 </b-col>
19 </b-row>
20 </page-section>
21 <b-form novalidate @submit.prevent="submitForm">
22 <page-section section-title="System">
23 <b-row>
24 <b-col lg="3">
25 <b-form-group label="Default gateway" label-for="default-gateway">
26 <b-form-input
27 id="default-gateway"
28 v-model.trim="form.gateway"
29 type="text"
30 :readonly="dhcpEnabled"
31 :state="getValidationState($v.form.gateway)"
32 @change="$v.form.gateway.$touch()"
33 />
34 <b-form-invalid-feedback role="alert">
35 <div v-if="!$v.form.gateway.required">Field required</div>
36 <div v-if="!$v.form.gateway.validateAddress">Invalid</div>
37 </b-form-invalid-feedback>
38 </b-form-group>
39 </b-col>
40 <b-col lg="3">
41 <b-form-group label="Hostname" label-for="hostname-field">
42 <b-form-input
43 id="hostname-field"
44 v-model.trim="form.hostname"
45 type="text"
46 :state="getValidationState($v.form.hostname)"
47 @change="$v.form.hostname.$touch()"
48 />
49 <b-form-invalid-feedback role="alert">
50 <div v-if="!$v.form.hostname.required">Field required</div>
51 <div v-if="!$v.form.hostname.validateHostname">
52 Must be less than 64 characters
53 </div>
54 </b-form-invalid-feedback>
55 </b-form-group>
56 </b-col>
57 <b-col lg="3">
58 <b-form-group label="MAC address" label-for="mac-address">
59 <b-form-input
60 id="mac-address"
61 v-model.trim="form.macAddress"
62 type="text"
63 :state="getValidationState($v.form.macAddress)"
64 @change="$v.form.macAddress.$touch()"
65 />
66 <b-form-invalid-feedback role="alert">
67 <div v-if="!$v.form.macAddress.required">Field required</div>
68 <div v-if="!$v.form.macAddress.validateMacAddress">Invalid</div>
69 </b-form-invalid-feedback>
70 </b-form-group>
71 </b-col>
72 </b-row>
73 </page-section>
74 <page-section section-title="Static IPv4">
75 <b-row>
76 <b-col lg="9" class="mb-3">
77 <b-table
78 :fields="ipv4StaticTableFields"
79 :items="form.ipv4StaticTableItems"
80 class="mb-0"
81 >
82 <template v-slot:cell(Address)="{ item, index }">
83 <b-form-input
84 v-model.trim="item.Address"
85 :aria-label="'Static IPV4 address ' + (index + 1)"
86 :readonly="dhcpEnabled"
87 :state="
88 getValidationState(
89 $v.form.ipv4StaticTableItems.$each.$iter[index].Address
90 )
91 "
92 @change="
93 $v.form.ipv4StaticTableItems.$each.$iter[
94 index
95 ].Address.$touch()
96 "
97 />
98 <b-form-invalid-feedback role="alert">
99 <div
100 v-if="
101 !$v.form.ipv4StaticTableItems.$each.$iter[index].Address
102 .required
103 "
104 >
105 Field required
106 </div>
107 <div
108 v-if="
109 !$v.form.ipv4StaticTableItems.$each.$iter[index].Address
110 .validateAddress
111 "
112 >
113 Invalid
114 </div>
115 </b-form-invalid-feedback>
116 </template>
117 <template v-slot:cell(SubnetMask)="{ item, index }">
118 <b-form-input
119 v-model.trim="item.SubnetMask"
120 :aria-label="'Static IPV4 Subnet mask ' + (index + 1)"
121 :readonly="dhcpEnabled"
122 :state="
123 getValidationState(
124 $v.form.ipv4StaticTableItems.$each.$iter[index].SubnetMask
125 )
126 "
127 @change="
128 $v.form.ipv4StaticTableItems.$each.$iter[
129 index
130 ].SubnetMask.$touch()
131 "
132 />
133 <b-form-invalid-feedback role="alert">
134 <div
135 v-if="
136 !$v.form.ipv4StaticTableItems.$each.$iter[index]
137 .SubnetMask.required
138 "
139 >
140 Field required
141 </div>
142 <div
143 v-if="
144 !$v.form.ipv4StaticTableItems.$each.$iter[index]
145 .SubnetMask.validateAddress
146 "
147 >
148 Invalid
149 </div>
150 </b-form-invalid-feedback>
151 </template>
152 <template v-slot:cell(actions)="{ item, index }">
153 <table-row-action
154 v-for="(action, actionIndex) in item.actions"
155 :key="actionIndex"
156 :value="action.value"
157 :title="action.title"
158 @click:tableAction="onDeleteIpv4StaticTableRow($event, index)"
159 >
160 <template v-slot:icon>
161 <icon-trashcan v-if="action.value === 'delete'" />
162 </template>
163 </table-row-action>
164 </template>
165 </b-table>
166 <b-button variant="link" @click="addIpv4StaticTableRow">
167 <icon-add /> Add static IP
168 </b-button>
169 </b-col>
170 </b-row>
171 </page-section>
172 <page-section section-title="Static DNS">
173 <b-row>
174 <b-col lg="4" class="mb-3">
175 <b-table
176 :fields="dnsTableFields"
177 :items="form.dnsStaticTableItems"
178 class="mb-0"
179 >
180 <template v-slot:cell(address)="{ item, index }">
181 <b-form-input
182 v-model.trim="item.address"
183 :aria-label="'Static DNS ' + (index + 1)"
184 :readonly="dhcpEnabled"
185 :state="
186 getValidationState(
187 $v.form.dnsStaticTableItems.$each.$iter[index].address
188 )
189 "
190 @change="
191 $v.form.dnsStaticTableItems.$each.$iter[
192 index
193 ].address.$touch()
194 "
195 />
196 <b-form-invalid-feedback role="alert">
197 <div
198 v-if="
199 !$v.form.dnsStaticTableItems.$each.$iter[index].address
200 .required
201 "
202 >
203 Field required
204 </div>
205 <div
206 v-if="
207 !$v.form.dnsStaticTableItems.$each.$iter[index].address
208 .validateAddress
209 "
210 >
211 Invalid
212 </div>
213 </b-form-invalid-feedback>
214 </template>
215 <template v-slot:cell(actions)="{ item, index }">
216 <table-row-action
217 v-for="(action, actionIndex) in item.actions"
218 :key="actionIndex"
219 :value="action.value"
220 :title="action.title"
221 @click:tableAction="onDeleteDnsTableRow($event, index)"
222 >
223 <template v-slot:icon>
224 <icon-trashcan v-if="action.value === 'delete'" />
225 </template>
226 </table-row-action>
227 </template>
228 </b-table>
229 <b-button variant="link" @click="addDnsTableRow">
230 <icon-add /> Add DNS server
231 </b-button>
232 </b-col>
233 </b-row>
234 </page-section>
235 <b-button
236 variant="primary"
237 type="submit"
238 :disabled="!$v.form.$anyDirty || $v.form.$invalid"
239 >
240 Save settings
241 </b-button>
242 </b-form>
243 </b-container>
244</template>
245
246<script>
247import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
248import IconAdd from '@carbon/icons-vue/es/add--alt/20';
249import BVToastMixin from '@/components/Mixins/BVToastMixin';
250import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
251import PageSection from '@/components/Global/PageSection';
252import PageTitle from '@/components/Global/PageTitle';
253import TableRowAction from '@/components/Global/TableRowAction';
254import VuelidateMixin from '@/components/Mixins/VuelidateMixin';
255import { mapState } from 'vuex';
256import { required, helpers } from 'vuelidate/lib/validators';
257
258// IP address, gateway and subnet pattern
259const validateAddress = helpers.regex(
260 'validateAddress',
261 /^(?=.*[^.]$)((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.?){4}$/
262);
263// Hostname pattern
264const validateHostname = helpers.regex('validateHostname', /^\S{0,64}$/);
265// MAC address pattern
266const validateMacAddress = helpers.regex(
267 'validateMacAddress',
268 /^(?:[0-9A-Fa-f]{2}([:-]?)[0-9A-Fa-f]{2})(?:(?:\1|\.)(?:[0-9A-Fa-f]{2}([:-]?)[0-9A-Fa-f]{2})){2}$/
269);
270
271export default {
272 name: 'NetworkSettings',
273 components: {
274 PageTitle,
275 PageSection,
276 TableRowAction,
277 IconTrashcan,
278 IconAdd
279 },
280 mixins: [BVToastMixin, VuelidateMixin, LoadingBarMixin],
281 data() {
282 return {
283 dhcpEnabled: null,
284 ipv4Configuration: '',
285 ipv4StaticTableFields: [
286 { key: 'Address', label: 'IP address' },
287 { key: 'SubnetMask', label: 'Subnet mask' },
288 { key: 'actions', label: '', tdClass: 'text-right' }
289 ],
290 dnsTableFields: [
291 { key: 'address', label: 'IP address' },
292 { key: 'actions', label: '', tdClass: 'text-right' }
293 ],
294 selectedInterfaceIndex: 0,
295 selectedInterface: {},
296 form: {
297 gateway: '',
298 hostname: '',
299 macAddress: '',
300 ipv4StaticTableItems: [],
301 dnsStaticTableItems: []
302 }
303 };
304 },
305 validations() {
306 return {
307 form: {
308 gateway: { required, validateAddress },
309 hostname: { required, validateHostname },
310 ipv4StaticTableItems: {
311 $each: {
312 Address: {
313 required,
314 validateAddress
315 },
316 SubnetMask: {
317 required,
318 validateAddress
319 }
320 }
321 },
322 macAddress: { required, validateMacAddress },
323 dnsStaticTableItems: {
324 $each: {
325 address: {
326 required,
327 validateAddress
328 }
329 }
330 }
331 }
332 };
333 },
334 computed: {
335 ...mapState('networkSettings', [
336 'ethernetData',
337 'interfaceOptions',
338 'defaultGateway'
339 ]),
340 interfaceSelectOptions() {
341 return this.interfaceOptions.map((option, index) => {
342 return {
343 text: option,
344 value: index
345 };
346 });
347 }
348 },
349 watch: {
350 ethernetData: function() {
351 this.selectInterface();
352 }
353 },
354 created() {
355 this.startLoader();
356 this.$store
357 .dispatch('networkSettings/getEthernetData')
358 .finally(() => this.endLoader());
359 },
360 beforeRouteLeave(to, from, next) {
361 this.hideLoader();
362 next();
363 },
364 methods: {
365 selectInterface() {
366 this.selectedInterface = this.ethernetData[this.selectedInterfaceIndex];
367 this.getIpv4StaticTableItems();
368 this.getDnsStaticTableItems();
369 this.getInterfaceSettings();
370 },
371 getInterfaceSettings() {
372 this.form.gateway = this.defaultGateway;
373 this.form.hostname = this.selectedInterface.HostName;
374 this.form.macAddress = this.selectedInterface.MACAddress;
375 this.dhcpEnabled = this.selectedInterface.DHCPv4.DHCPEnabled;
376 },
377 getDnsStaticTableItems() {
378 const dns = this.selectedInterface.StaticNameServers || [];
379 this.form.dnsStaticTableItems = dns.map(server => {
380 return {
381 address: server,
382 actions: [
383 {
384 value: 'delete',
385 enabled: this.dhcpEnabled,
386 title: 'delete static dns row'
387 }
388 ]
389 };
390 });
391 },
392 addDnsTableRow() {
393 this.$v.form.dnsStaticTableItems.$touch();
394 this.form.dnsStaticTableItems.push({
395 address: '',
396 actions: [
397 {
398 value: 'delete',
399 enabled: this.dhcpEnabled,
400 title: 'delete static dns row'
401 }
402 ]
403 });
404 },
405 deleteDnsTableRow(index) {
406 this.$v.form.dnsStaticTableItems.$touch();
407 this.form.dnsStaticTableItems.splice(index, 1);
408 },
409 onDeleteDnsTableRow(action, row) {
410 this.deleteDnsTableRow(row);
411 },
412 getIpv4StaticTableItems() {
413 const addresses = this.selectedInterface.IPv4StaticAddresses || [];
414 this.form.ipv4StaticTableItems = addresses.map(ipv4 => {
415 return {
416 Address: ipv4.Address,
417 SubnetMask: ipv4.SubnetMask,
418 actions: [
419 {
420 value: 'delete',
421 enabled: this.dhcpEnabled,
422 title: 'delete static ipv4 row'
423 }
424 ]
425 };
426 });
427 },
428 addIpv4StaticTableRow() {
429 this.$v.form.ipv4StaticTableItems.$touch();
430 this.form.ipv4StaticTableItems.push({
431 Address: '',
432 SubnetMask: '',
433 actions: [
434 {
435 value: 'delete',
436 enabled: this.dhcpEnabled,
437 title: 'delete static ipv4 row'
438 }
439 ]
440 });
441 },
442 deleteIpv4StaticTableRow(index) {
443 this.$v.form.ipv4StaticTableItems.$touch();
444 this.form.ipv4StaticTableItems.splice(index, 1);
445 },
446 onDeleteIpv4StaticTableRow(action, row) {
447 this.deleteIpv4StaticTableRow(row);
448 },
449 submitForm() {
450 this.startLoader();
451 let networkInterfaceSelected = this.selectedInterface;
452 let selectedInterfaceIndex = this.selectedInterfaceIndex;
453 let interfaceId = networkInterfaceSelected.Id;
454 let isDhcpEnabled = networkInterfaceSelected.DHCPv4.DHCPEnabled;
455 let macAddress = this.form.macAddress;
456 let hostname = this.form.hostname;
457 let networkSettingsForm = {
458 interfaceId,
459 hostname,
460 macAddress,
461 selectedInterfaceIndex,
462 isDhcpEnabled
463 };
464 networkSettingsForm.staticIpv4 = this.form.ipv4StaticTableItems.map(
465 updateIpv4 => {
466 delete updateIpv4.actions;
467 updateIpv4.Gateway = this.form.gateway;
468 return updateIpv4;
469 }
470 );
471 networkSettingsForm.staticNameServers = this.form.dnsStaticTableItems.map(
472 updateDns => {
473 return updateDns.address;
474 }
475 );
476 this.$store
477 .dispatch(
478 'networkSettings/updateInterfaceSettings',
479 networkSettingsForm
480 )
481 .then(success => {
482 this.successToast(success);
483 })
484 .catch(({ message }) => this.errorToast(message))
485 .finally(() => {
486 this.$v.form.$reset();
487 this.endLoader();
488 });
489 }
490 }
491};
492</script>