blob: 863f2ab8faa4418cd4914abbf85e8b7f0751e4f1 [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"
Mateusz Gapski471f2e02020-07-27 14:43:26 +0200288 >
289 {{ $t('global.action.saveSettings') }}
290 </b-button>
291 </b-form-group>
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600292 </b-form>
293 </b-container>
294</template>
295
296<script>
297import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
298import IconAdd from '@carbon/icons-vue/es/add--alt/20';
299import BVToastMixin from '@/components/Mixins/BVToastMixin';
300import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
301import PageSection from '@/components/Global/PageSection';
302import PageTitle from '@/components/Global/PageTitle';
303import TableRowAction from '@/components/Global/TableRowAction';
304import VuelidateMixin from '@/components/Mixins/VuelidateMixin';
305import { mapState } from 'vuex';
306import { required, helpers } from 'vuelidate/lib/validators';
307
308// IP address, gateway and subnet pattern
309const validateAddress = helpers.regex(
310 'validateAddress',
311 /^(?=.*[^.]$)((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.?){4}$/
312);
313// Hostname pattern
314const validateHostname = helpers.regex('validateHostname', /^\S{0,64}$/);
315// MAC address pattern
316const validateMacAddress = helpers.regex(
317 'validateMacAddress',
318 /^(?:[0-9A-Fa-f]{2}([:-]?)[0-9A-Fa-f]{2})(?:(?:\1|\.)(?:[0-9A-Fa-f]{2}([:-]?)[0-9A-Fa-f]{2})){2}$/
319);
320
321export default {
322 name: 'NetworkSettings',
323 components: {
324 PageTitle,
325 PageSection,
326 TableRowAction,
327 IconTrashcan,
328 IconAdd
329 },
330 mixins: [BVToastMixin, VuelidateMixin, LoadingBarMixin],
331 data() {
332 return {
333 dhcpEnabled: null,
334 ipv4Configuration: '',
335 ipv4StaticTableFields: [
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -0500336 {
337 key: 'Address',
338 label: this.$t('pageNetworkSettings.table.ipAddress')
339 },
340 {
341 key: 'SubnetMask',
342 label: this.$t('pageNetworkSettings.table.subnet')
343 },
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600344 { key: 'actions', label: '', tdClass: 'text-right' }
345 ],
346 dnsTableFields: [
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -0500347 {
348 key: 'address',
349 label: this.$t('pageNetworkSettings.table.ipAddress')
350 },
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600351 { key: 'actions', label: '', tdClass: 'text-right' }
352 ],
353 selectedInterfaceIndex: 0,
354 selectedInterface: {},
355 form: {
356 gateway: '',
357 hostname: '',
358 macAddress: '',
359 ipv4StaticTableItems: [],
360 dnsStaticTableItems: []
361 }
362 };
363 },
364 validations() {
365 return {
366 form: {
367 gateway: { required, validateAddress },
368 hostname: { required, validateHostname },
369 ipv4StaticTableItems: {
370 $each: {
371 Address: {
372 required,
373 validateAddress
374 },
375 SubnetMask: {
376 required,
377 validateAddress
378 }
379 }
380 },
381 macAddress: { required, validateMacAddress },
382 dnsStaticTableItems: {
383 $each: {
384 address: {
385 required,
386 validateAddress
387 }
388 }
389 }
390 }
391 };
392 },
393 computed: {
394 ...mapState('networkSettings', [
395 'ethernetData',
396 'interfaceOptions',
397 'defaultGateway'
398 ]),
399 interfaceSelectOptions() {
400 return this.interfaceOptions.map((option, index) => {
401 return {
402 text: option,
403 value: index
404 };
405 });
406 }
407 },
408 watch: {
409 ethernetData: function() {
410 this.selectInterface();
411 }
412 },
413 created() {
414 this.startLoader();
415 this.$store
416 .dispatch('networkSettings/getEthernetData')
417 .finally(() => this.endLoader());
418 },
419 beforeRouteLeave(to, from, next) {
420 this.hideLoader();
421 next();
422 },
423 methods: {
424 selectInterface() {
425 this.selectedInterface = this.ethernetData[this.selectedInterfaceIndex];
426 this.getIpv4StaticTableItems();
427 this.getDnsStaticTableItems();
428 this.getInterfaceSettings();
429 },
430 getInterfaceSettings() {
431 this.form.gateway = this.defaultGateway;
432 this.form.hostname = this.selectedInterface.HostName;
433 this.form.macAddress = this.selectedInterface.MACAddress;
434 this.dhcpEnabled = this.selectedInterface.DHCPv4.DHCPEnabled;
435 },
436 getDnsStaticTableItems() {
437 const dns = this.selectedInterface.StaticNameServers || [];
438 this.form.dnsStaticTableItems = dns.map(server => {
439 return {
440 address: server,
441 actions: [
442 {
443 value: 'delete',
444 enabled: this.dhcpEnabled,
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -0500445 title: this.$t('pageNetworkSettings.table.deleteDns')
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600446 }
447 ]
448 };
449 });
450 },
451 addDnsTableRow() {
452 this.$v.form.dnsStaticTableItems.$touch();
453 this.form.dnsStaticTableItems.push({
454 address: '',
455 actions: [
456 {
457 value: 'delete',
458 enabled: this.dhcpEnabled,
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -0500459 title: this.$t('pageNetworkSettings.table.deleteDns')
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600460 }
461 ]
462 });
463 },
464 deleteDnsTableRow(index) {
465 this.$v.form.dnsStaticTableItems.$touch();
466 this.form.dnsStaticTableItems.splice(index, 1);
467 },
468 onDeleteDnsTableRow(action, row) {
469 this.deleteDnsTableRow(row);
470 },
471 getIpv4StaticTableItems() {
472 const addresses = this.selectedInterface.IPv4StaticAddresses || [];
473 this.form.ipv4StaticTableItems = addresses.map(ipv4 => {
474 return {
475 Address: ipv4.Address,
476 SubnetMask: ipv4.SubnetMask,
477 actions: [
478 {
479 value: 'delete',
480 enabled: this.dhcpEnabled,
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -0500481 title: this.$t('pageNetworkSettings.table.deleteStaticIpv4')
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600482 }
483 ]
484 };
485 });
486 },
487 addIpv4StaticTableRow() {
488 this.$v.form.ipv4StaticTableItems.$touch();
489 this.form.ipv4StaticTableItems.push({
490 Address: '',
491 SubnetMask: '',
492 actions: [
493 {
494 value: 'delete',
495 enabled: this.dhcpEnabled,
Dixsie Wolmerse3c9c092020-05-20 23:27:56 -0500496 title: this.$t('pageNetworkSettings.table.deleteStaticIpv4')
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600497 }
498 ]
499 });
500 },
501 deleteIpv4StaticTableRow(index) {
502 this.$v.form.ipv4StaticTableItems.$touch();
503 this.form.ipv4StaticTableItems.splice(index, 1);
504 },
505 onDeleteIpv4StaticTableRow(action, row) {
506 this.deleteIpv4StaticTableRow(row);
507 },
508 submitForm() {
Dixsie Wolmers4d1dbb52020-08-24 21:43:25 -0500509 this.$v.$touch();
510 if (this.$v.$invalid) return;
Dixsie Wolmersbb81d552020-02-26 19:52:28 -0600511 this.startLoader();
512 let networkInterfaceSelected = this.selectedInterface;
513 let selectedInterfaceIndex = this.selectedInterfaceIndex;
514 let interfaceId = networkInterfaceSelected.Id;
515 let isDhcpEnabled = networkInterfaceSelected.DHCPv4.DHCPEnabled;
516 let macAddress = this.form.macAddress;
517 let hostname = this.form.hostname;
518 let networkSettingsForm = {
519 interfaceId,
520 hostname,
521 macAddress,
522 selectedInterfaceIndex,
523 isDhcpEnabled
524 };
525 networkSettingsForm.staticIpv4 = this.form.ipv4StaticTableItems.map(
526 updateIpv4 => {
527 delete updateIpv4.actions;
528 updateIpv4.Gateway = this.form.gateway;
529 return updateIpv4;
530 }
531 );
532 networkSettingsForm.staticNameServers = this.form.dnsStaticTableItems.map(
533 updateDns => {
534 return updateDns.address;
535 }
536 );
537 this.$store
538 .dispatch(
539 'networkSettings/updateInterfaceSettings',
540 networkSettingsForm
541 )
542 .then(success => {
543 this.successToast(success);
544 })
545 .catch(({ message }) => this.errorToast(message))
546 .finally(() => {
547 this.$v.form.$reset();
548 this.endLoader();
549 });
550 }
551 }
552};
553</script>