blob: e8c6ae817a605399f4b9b8ba263d96170a6d65a1 [file] [log] [blame]
Dixsie Wolmersbb81d552020-02-26 19:52:28 -06001<template>
2 <b-container fluid="xl">
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -05003 <page-title :description="$t('pageNetworkSettings.pageDescription')" />
4 <page-section :section-title="$t('pageNetworkSettings.interface')">
Dixsie Wolmersbb81d552020-02-26 19:52:28 -06005 <b-row>
6 <b-col lg="3">
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -05007 <b-form-group
8 label-for="interface-select"
9 :label="$t('pageNetworkSettings.form.networkInterface')"
10 >
Dixsie Wolmersbb81d552020-02-26 19:52:28 -060011 <b-form-select
12 id="interface-select"
13 v-model="selectedInterfaceIndex"
Mateusz Gapski471f2e02020-07-27 14:43:26 +020014 :disabled="loading"
Dixsie Wolmers55888a12020-07-01 11:21:47 -050015 data-test-id="networkSettings-select-interface"
Dixsie Wolmersbb81d552020-02-26 19:52:28 -060016 :options="interfaceSelectOptions"
17 @change="selectInterface"
18 >
19 </b-form-select>
20 </b-form-group>
21 </b-col>
22 </b-row>
23 </page-section>
24 <b-form novalidate @submit.prevent="submitForm">
Mateusz Gapski471f2e02020-07-27 14:43:26 +020025 <b-form-group :disabled="loading">
26 <page-section :section-title="$t('pageNetworkSettings.system')">
27 <b-row>
28 <b-col lg="3">
29 <b-form-group
30 :label="$t('pageNetworkSettings.form.defaultGateway')"
31 label-for="default-gateway"
32 >
Dixsie Wolmersbb81d552020-02-26 19:52:28 -060033 <b-form-input
Mateusz Gapski471f2e02020-07-27 14:43:26 +020034 id="default-gateway"
35 v-model.trim="form.gateway"
36 data-test-id="networkSettings-input-gateway"
37 type="text"
Dixsie Wolmersbb81d552020-02-26 19:52:28 -060038 :readonly="dhcpEnabled"
Mateusz Gapski471f2e02020-07-27 14:43:26 +020039 :state="getValidationState($v.form.gateway)"
40 @change="$v.form.gateway.$touch()"
Dixsie Wolmersbb81d552020-02-26 19:52:28 -060041 />
42 <b-form-invalid-feedback role="alert">
Mateusz Gapski471f2e02020-07-27 14:43:26 +020043 <div v-if="!$v.form.gateway.required">
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -050044 {{ $t('global.form.fieldRequired') }}
Dixsie Wolmersbb81d552020-02-26 19:52:28 -060045 </div>
Mateusz Gapski471f2e02020-07-27 14:43:26 +020046 <div v-if="!$v.form.gateway.validateAddress">
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -050047 {{ $t('global.form.invalidFormat') }}
Dixsie Wolmersbb81d552020-02-26 19:52:28 -060048 </div>
49 </b-form-invalid-feedback>
Mateusz Gapski471f2e02020-07-27 14:43:26 +020050 </b-form-group>
51 </b-col>
52 <b-col lg="3">
53 <b-form-group
54 :label="$t('pageNetworkSettings.form.hostname')"
55 label-for="hostname-field"
56 >
Dixsie Wolmersbb81d552020-02-26 19:52:28 -060057 <b-form-input
Mateusz Gapski471f2e02020-07-27 14:43:26 +020058 id="hostname-field"
59 v-model.trim="form.hostname"
60 data-test-id="networkSettings-input-hostname"
61 type="text"
62 :state="getValidationState($v.form.hostname)"
63 @change="$v.form.hostname.$touch()"
Dixsie Wolmersbb81d552020-02-26 19:52:28 -060064 />
65 <b-form-invalid-feedback role="alert">
Mateusz Gapski471f2e02020-07-27 14:43:26 +020066 <div v-if="!$v.form.hostname.required">
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -050067 {{ $t('global.form.fieldRequired') }}
Dixsie Wolmersbb81d552020-02-26 19:52:28 -060068 </div>
Mateusz Gapski471f2e02020-07-27 14:43:26 +020069 <div v-if="!$v.form.hostname.validateHostname">
70 {{
71 $t('global.form.lengthMustBeBetween', { min: 1, max: 64 })
72 }}
73 </div>
74 </b-form-invalid-feedback>
75 </b-form-group>
76 </b-col>
77 <b-col lg="3">
78 <b-form-group
79 :label="$t('pageNetworkSettings.form.macAddress')"
80 label-for="mac-address"
81 >
82 <b-form-input
83 id="mac-address"
84 v-model.trim="form.macAddress"
85 data-test-id="networkSettings-input-macAddress"
86 type="text"
87 :state="getValidationState($v.form.macAddress)"
88 @change="$v.form.macAddress.$touch()"
89 />
90 <b-form-invalid-feedback role="alert">
91 <div v-if="!$v.form.macAddress.required">
92 {{ $t('global.form.fieldRequired') }}
93 </div>
94 <div v-if="!$v.form.macAddress.validateMacAddress">
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -050095 {{ $t('global.form.invalidFormat') }}
Dixsie Wolmersbb81d552020-02-26 19:52:28 -060096 </div>
97 </b-form-invalid-feedback>
Mateusz Gapski471f2e02020-07-27 14:43:26 +020098 </b-form-group>
99 </b-col>
100 </b-row>
101 </page-section>
102 <page-section :section-title="$t('pageNetworkSettings.staticIpv4')">
103 <b-row>
104 <b-col lg="9" class="mb-3">
105 <b-table
106 responsive="md"
107 :fields="ipv4StaticTableFields"
108 :items="form.ipv4StaticTableItems"
109 class="mb-0"
110 >
111 <template v-slot:cell(Address)="{ item, index }">
112 <b-form-input
113 v-model.trim="item.Address"
114 :data-test-id="`networkSettings-input-staticIpv4-${index}`"
115 :aria-label="
116 $t('pageNetworkSettings.ariaLabel.staticIpv4AddressRow') +
117 ' ' +
118 (index + 1)
119 "
120 :readonly="dhcpEnabled"
121 :state="
122 getValidationState(
123 $v.form.ipv4StaticTableItems.$each.$iter[index].Address
124 )
125 "
126 @change="
127 $v.form.ipv4StaticTableItems.$each.$iter[
128 index
129 ].Address.$touch()
130 "
131 />
132 <b-form-invalid-feedback role="alert">
133 <div
134 v-if="
135 !$v.form.ipv4StaticTableItems.$each.$iter[index].Address
136 .required
137 "
138 >
139 {{ $t('global.form.fieldRequired') }}
140 </div>
141 <div
142 v-if="
143 !$v.form.ipv4StaticTableItems.$each.$iter[index].Address
144 .validateAddress
145 "
146 >
147 {{ $t('global.form.invalidFormat') }}
148 </div>
149 </b-form-invalid-feedback>
150 </template>
151 <template v-slot:cell(SubnetMask)="{ item, index }">
152 <b-form-input
153 v-model.trim="item.SubnetMask"
154 :data-test-id="`networkSettings-input-subnetMask-${index}`"
155 :aria-label="
156 $t('pageNetworkSettings.ariaLabel.staticIpv4SubnetRow') +
157 ' ' +
158 (index + 1)
159 "
160 :readonly="dhcpEnabled"
161 :state="
162 getValidationState(
163 $v.form.ipv4StaticTableItems.$each.$iter[index]
164 .SubnetMask
165 )
166 "
167 @change="
168 $v.form.ipv4StaticTableItems.$each.$iter[
169 index
170 ].SubnetMask.$touch()
171 "
172 />
173 <b-form-invalid-feedback role="alert">
174 <div
175 v-if="
176 !$v.form.ipv4StaticTableItems.$each.$iter[index]
177 .SubnetMask.required
178 "
179 >
180 {{ $t('global.form.fieldRequired') }}
181 </div>
182 <div
183 v-if="
184 !$v.form.ipv4StaticTableItems.$each.$iter[index]
185 .SubnetMask.validateAddress
186 "
187 >
188 {{ $t('global.form.invalidFormat') }}
189 </div>
190 </b-form-invalid-feedback>
191 </template>
192 <template v-slot:cell(actions)="{ item, index }">
193 <table-row-action
194 v-for="(action, actionIndex) in item.actions"
195 :key="actionIndex"
196 :value="action.value"
197 :title="action.title"
198 @click:tableAction="
199 onDeleteIpv4StaticTableRow($event, index)
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600200 "
201 >
Mateusz Gapski471f2e02020-07-27 14:43:26 +0200202 <template v-slot:icon>
203 <icon-trashcan v-if="action.value === 'delete'" />
204 </template>
205 </table-row-action>
206 </template>
207 </b-table>
208 <b-button variant="link" @click="addIpv4StaticTableRow">
209 <icon-add />
210 {{ $t('pageNetworkSettings.table.addStaticIpv4Address') }}
211 </b-button>
212 </b-col>
213 </b-row>
214 </page-section>
215 <page-section :section-title="$t('pageNetworkSettings.staticDns')">
216 <b-row>
217 <b-col lg="4" class="mb-3">
218 <b-table
219 responsive
220 :fields="dnsTableFields"
221 :items="form.dnsStaticTableItems"
222 class="mb-0"
223 >
224 <template v-slot:cell(address)="{ item, index }">
225 <b-form-input
226 v-model.trim="item.address"
227 :data-test-id="`networkSettings-input-dnsAddress-${index}`"
228 :aria-label="
229 $t('pageNetworkSettings.ariaLabel.staticDnsRow') +
230 ' ' +
231 (index + 1)
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600232 "
Mateusz Gapski471f2e02020-07-27 14:43:26 +0200233 :readonly="dhcpEnabled"
234 :state="
235 getValidationState(
236 $v.form.dnsStaticTableItems.$each.$iter[index].address
237 )
238 "
239 @change="
240 $v.form.dnsStaticTableItems.$each.$iter[
241 index
242 ].address.$touch()
243 "
244 />
245 <b-form-invalid-feedback role="alert">
246 <div
247 v-if="
248 !$v.form.dnsStaticTableItems.$each.$iter[index].address
249 .required
250 "
251 >
252 {{ $t('global.form.fieldRequired') }}
253 </div>
254 <div
255 v-if="
256 !$v.form.dnsStaticTableItems.$each.$iter[index].address
257 .validateAddress
258 "
259 >
260 {{ $t('global.form.invalidFormat') }}
261 </div>
262 </b-form-invalid-feedback>
263 </template>
264 <template v-slot:cell(actions)="{ item, index }">
265 <table-row-action
266 v-for="(action, actionIndex) in item.actions"
267 :key="actionIndex"
268 :value="action.value"
269 :title="action.title"
270 @click:tableAction="onDeleteDnsTableRow($event, index)"
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600271 >
Mateusz Gapski471f2e02020-07-27 14:43:26 +0200272 <template v-slot:icon>
273 <icon-trashcan v-if="action.value === 'delete'" />
274 </template>
275 </table-row-action>
276 </template>
277 </b-table>
278 <b-button variant="link" @click="addDnsTableRow">
279 <icon-add /> {{ $t('pageNetworkSettings.table.addDns') }}
280 </b-button>
281 </b-col>
282 </b-row>
283 </page-section>
284 <b-button
285 variant="primary"
286 type="submit"
287 data-test-id="networkSettings-button-saveNetworkSettings"
288 :disabled="!$v.form.$anyDirty || $v.form.$invalid"
289 >
290 {{ $t('global.action.saveSettings') }}
291 </b-button>
292 </b-form-group>
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600293 </b-form>
294 </b-container>
295</template>
296
297<script>
298import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
299import IconAdd from '@carbon/icons-vue/es/add--alt/20';
300import BVToastMixin from '@/components/Mixins/BVToastMixin';
301import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
302import PageSection from '@/components/Global/PageSection';
303import PageTitle from '@/components/Global/PageTitle';
304import TableRowAction from '@/components/Global/TableRowAction';
305import VuelidateMixin from '@/components/Mixins/VuelidateMixin';
306import { mapState } from 'vuex';
307import { required, helpers } from 'vuelidate/lib/validators';
308
309// IP address, gateway and subnet pattern
310const validateAddress = helpers.regex(
311 'validateAddress',
312 /^(?=.*[^.]$)((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.?){4}$/
313);
314// Hostname pattern
315const validateHostname = helpers.regex('validateHostname', /^\S{0,64}$/);
316// MAC address pattern
317const validateMacAddress = helpers.regex(
318 'validateMacAddress',
319 /^(?:[0-9A-Fa-f]{2}([:-]?)[0-9A-Fa-f]{2})(?:(?:\1|\.)(?:[0-9A-Fa-f]{2}([:-]?)[0-9A-Fa-f]{2})){2}$/
320);
321
322export default {
323 name: 'NetworkSettings',
324 components: {
325 PageTitle,
326 PageSection,
327 TableRowAction,
328 IconTrashcan,
329 IconAdd
330 },
331 mixins: [BVToastMixin, VuelidateMixin, LoadingBarMixin],
332 data() {
333 return {
334 dhcpEnabled: null,
335 ipv4Configuration: '',
336 ipv4StaticTableFields: [
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -0500337 {
338 key: 'Address',
339 label: this.$t('pageNetworkSettings.table.ipAddress')
340 },
341 {
342 key: 'SubnetMask',
343 label: this.$t('pageNetworkSettings.table.subnet')
344 },
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600345 { key: 'actions', label: '', tdClass: 'text-right' }
346 ],
347 dnsTableFields: [
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -0500348 {
349 key: 'address',
350 label: this.$t('pageNetworkSettings.table.ipAddress')
351 },
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600352 { key: 'actions', label: '', tdClass: 'text-right' }
353 ],
354 selectedInterfaceIndex: 0,
355 selectedInterface: {},
356 form: {
357 gateway: '',
358 hostname: '',
359 macAddress: '',
360 ipv4StaticTableItems: [],
361 dnsStaticTableItems: []
362 }
363 };
364 },
365 validations() {
366 return {
367 form: {
368 gateway: { required, validateAddress },
369 hostname: { required, validateHostname },
370 ipv4StaticTableItems: {
371 $each: {
372 Address: {
373 required,
374 validateAddress
375 },
376 SubnetMask: {
377 required,
378 validateAddress
379 }
380 }
381 },
382 macAddress: { required, validateMacAddress },
383 dnsStaticTableItems: {
384 $each: {
385 address: {
386 required,
387 validateAddress
388 }
389 }
390 }
391 }
392 };
393 },
394 computed: {
395 ...mapState('networkSettings', [
396 'ethernetData',
397 'interfaceOptions',
398 'defaultGateway'
399 ]),
400 interfaceSelectOptions() {
401 return this.interfaceOptions.map((option, index) => {
402 return {
403 text: option,
404 value: index
405 };
406 });
407 }
408 },
409 watch: {
410 ethernetData: function() {
411 this.selectInterface();
412 }
413 },
414 created() {
415 this.startLoader();
416 this.$store
417 .dispatch('networkSettings/getEthernetData')
418 .finally(() => this.endLoader());
419 },
420 beforeRouteLeave(to, from, next) {
421 this.hideLoader();
422 next();
423 },
424 methods: {
425 selectInterface() {
426 this.selectedInterface = this.ethernetData[this.selectedInterfaceIndex];
427 this.getIpv4StaticTableItems();
428 this.getDnsStaticTableItems();
429 this.getInterfaceSettings();
430 },
431 getInterfaceSettings() {
432 this.form.gateway = this.defaultGateway;
433 this.form.hostname = this.selectedInterface.HostName;
434 this.form.macAddress = this.selectedInterface.MACAddress;
435 this.dhcpEnabled = this.selectedInterface.DHCPv4.DHCPEnabled;
436 },
437 getDnsStaticTableItems() {
438 const dns = this.selectedInterface.StaticNameServers || [];
439 this.form.dnsStaticTableItems = dns.map(server => {
440 return {
441 address: server,
442 actions: [
443 {
444 value: 'delete',
445 enabled: this.dhcpEnabled,
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -0500446 title: this.$t('pageNetworkSettings.table.deleteDns')
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600447 }
448 ]
449 };
450 });
451 },
452 addDnsTableRow() {
453 this.$v.form.dnsStaticTableItems.$touch();
454 this.form.dnsStaticTableItems.push({
455 address: '',
456 actions: [
457 {
458 value: 'delete',
459 enabled: this.dhcpEnabled,
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -0500460 title: this.$t('pageNetworkSettings.table.deleteDns')
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600461 }
462 ]
463 });
464 },
465 deleteDnsTableRow(index) {
466 this.$v.form.dnsStaticTableItems.$touch();
467 this.form.dnsStaticTableItems.splice(index, 1);
468 },
469 onDeleteDnsTableRow(action, row) {
470 this.deleteDnsTableRow(row);
471 },
472 getIpv4StaticTableItems() {
473 const addresses = this.selectedInterface.IPv4StaticAddresses || [];
474 this.form.ipv4StaticTableItems = addresses.map(ipv4 => {
475 return {
476 Address: ipv4.Address,
477 SubnetMask: ipv4.SubnetMask,
478 actions: [
479 {
480 value: 'delete',
481 enabled: this.dhcpEnabled,
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -0500482 title: this.$t('pageNetworkSettings.table.deleteStaticIpv4')
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600483 }
484 ]
485 };
486 });
487 },
488 addIpv4StaticTableRow() {
489 this.$v.form.ipv4StaticTableItems.$touch();
490 this.form.ipv4StaticTableItems.push({
491 Address: '',
492 SubnetMask: '',
493 actions: [
494 {
495 value: 'delete',
496 enabled: this.dhcpEnabled,
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -0500497 title: this.$t('pageNetworkSettings.table.deleteStaticIpv4')
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600498 }
499 ]
500 });
501 },
502 deleteIpv4StaticTableRow(index) {
503 this.$v.form.ipv4StaticTableItems.$touch();
504 this.form.ipv4StaticTableItems.splice(index, 1);
505 },
506 onDeleteIpv4StaticTableRow(action, row) {
507 this.deleteIpv4StaticTableRow(row);
508 },
509 submitForm() {
510 this.startLoader();
511 let networkInterfaceSelected = this.selectedInterface;
512 let selectedInterfaceIndex = this.selectedInterfaceIndex;
513 let interfaceId = networkInterfaceSelected.Id;
514 let isDhcpEnabled = networkInterfaceSelected.DHCPv4.DHCPEnabled;
515 let macAddress = this.form.macAddress;
516 let hostname = this.form.hostname;
517 let networkSettingsForm = {
518 interfaceId,
519 hostname,
520 macAddress,
521 selectedInterfaceIndex,
522 isDhcpEnabled
523 };
524 networkSettingsForm.staticIpv4 = this.form.ipv4StaticTableItems.map(
525 updateIpv4 => {
526 delete updateIpv4.actions;
527 updateIpv4.Gateway = this.form.gateway;
528 return updateIpv4;
529 }
530 );
531 networkSettingsForm.staticNameServers = this.form.dnsStaticTableItems.map(
532 updateDns => {
533 return updateDns.address;
534 }
535 );
536 this.$store
537 .dispatch(
538 'networkSettings/updateInterfaceSettings',
539 networkSettingsForm
540 )
541 .then(success => {
542 this.successToast(success);
543 })
544 .catch(({ message }) => this.errorToast(message))
545 .finally(() => {
546 this.$v.form.$reset();
547 this.endLoader();
548 });
549 }
550 }
551};
552</script>