xref: /openbmc/webui-vue/src/views/Settings/Network/TableIpv6.vue (revision d36ac8a8be8636ddd0e64ce005d507b21bcdeb00)
1<template>
2  <page-section :section-title="$t('pageNetwork.ipv6')">
3    <b-row class="mb-4">
4      <b-col lg="2" md="6">
5        <dl>
6          <dt>{{ $t('pageNetwork.dhcp6') }}</dt>
7          <dd>
8            <b-form-checkbox
9              id="dhcp6Switch"
10              v-model="dhcp6EnabledState"
11              data-test-id="networkSettings-switch-dhcp6Enabled"
12              switch
13              @change="changeDhcp6EnabledState"
14            >
15              <span v-if="dhcp6EnabledState">
16                {{ $t('global.status.enabled') }}
17              </span>
18              <span v-else>{{ $t('global.status.disabled') }}</span>
19            </b-form-checkbox>
20          </dd>
21        </dl>
22      </b-col>
23      <b-col lg="2" md="6">
24        <dl class="text-nowrap">
25          <dt>
26            {{ $t('pageNetwork.ipv6DefaultGateway') }}
27            <b-button
28              v-if="defaultGatewayEditable"
29              variant="link"
30              class="p-1"
31              @click="initDefaultGatewayModal()"
32            >
33              <icon-edit
34                :title="$t('pageNetwork.modal.editIPv6DefaultGatewayTitle')"
35              />
36            </b-button>
37          </dt>
38          <dd>
39            {{ dataFormatter(defaultGateway) }}
40          </dd>
41        </dl>
42      </b-col>
43    </b-row>
44    <b-row>
45      <b-col>
46        <h3 class="h5">
47          {{ $t('pageNetwork.ipv6Addresses') }}
48        </h3>
49      </b-col>
50      <b-col class="text-end">
51        <b-button variant="primary" @click="initAddIpv6Address()">
52          <icon-add />
53          {{ $t('pageNetwork.table.addIpv6Address') }}
54        </b-button>
55      </b-col>
56    </b-row>
57    <b-table
58      responsive="md"
59      hover
60      thead-class="table-light"
61      :fields="ipv6TableFields"
62      :items="form.ipv6TableItems"
63      :empty-text="$t('global.table.emptyMessage')"
64      class="mb-0"
65      show-empty
66    >
67      <template #cell(actions)="{ item, index }">
68        <table-row-action
69          v-for="(action, actionIndex) in filteredActions(item)"
70          :key="actionIndex"
71          :value="action.value"
72          :title="action.title"
73          :enabled="action.enabled"
74          @click-table-action="onIpv6TableAction(action, $event, index)"
75        >
76          <template #icon>
77            <icon-edit v-if="action.value === 'edit'" />
78            <icon-trashcan v-if="action.value === 'delete'" />
79          </template>
80        </table-row-action>
81      </template>
82    </b-table>
83    <modal-ipv6 v-model="showAddIpv6" />
84  </page-section>
85</template>
86
87<script>
88import BVToastMixin from '@/components/Mixins/BVToastMixin';
89import IconAdd from '@carbon/icons-vue/es/add--alt/20';
90import IconEdit from '@carbon/icons-vue/es/edit/20';
91import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
92import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
93import PageSection from '@/components/Global/PageSection';
94import TableRowAction from '@/components/Global/TableRowAction';
95import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin';
96import ModalIpv6 from './ModalIpv6.vue';
97import { mapState } from 'vuex';
98import i18n from '@/i18n';
99import { useI18n } from 'vue-i18n';
100import { useModal } from 'bootstrap-vue-next';
101
102export default {
103  name: 'Ipv6Table',
104  components: {
105    IconAdd,
106    IconEdit,
107    IconTrashcan,
108    PageSection,
109    TableRowAction,
110    ModalIpv6,
111  },
112  mixins: [BVToastMixin, LoadingBarMixin, DataFormatterMixin],
113  props: {
114    tabIndex: {
115      type: Number,
116      default: 0,
117    },
118  },
119  setup() {
120    const bvModal = useModal();
121    return { bvModal };
122  },
123  data() {
124    return {
125      $t: useI18n().t,
126      showAddIpv6: false,
127      showDefaultGatewayModal: false,
128      form: {
129        ipv6TableItems: [],
130      },
131      actions: [
132        {
133          value: 'edit',
134          title: i18n.global.t('global.action.edit'),
135        },
136        {
137          value: 'delete',
138          title: i18n.global.t('global.action.delete'),
139        },
140      ],
141      ipv6TableFields: [
142        {
143          key: 'Address',
144          label: i18n.global.t('pageNetwork.table.ipAddress'),
145        },
146        {
147          key: 'PrefixLength',
148          label: i18n.global.t('pageNetwork.table.prefixLength'),
149        },
150        {
151          key: 'AddressOrigin',
152          label: i18n.global.t('pageNetwork.table.addressOrigin'),
153        },
154        { key: 'actions', label: '', tdClass: 'text-end' },
155      ],
156      defaultGateway: '',
157      defaultGatewayEditable:
158        process.env.VUE_APP_ENV_NAME !== 'nvidia-bluefield',
159    };
160  },
161  computed: {
162    ...mapState('network', ['ethernetData']),
163    selectedInterface() {
164      return this.$store.getters['network/selectedInterfaceIndex'];
165    },
166    dhcp6EnabledState: {
167      get() {
168        return (
169          this.$store.getters['network/globalNetworkSettings'][
170            this.selectedInterface
171          ].dhcp6Enabled === 'Enabled'
172        );
173      },
174      set(newValue) {
175        return newValue;
176      },
177    },
178    filteredActions() {
179      return (item) => {
180        if (item.AddressOrigin === 'DHCPv6' || item.AddressOrigin === 'SLAAC') {
181          return item.actions.filter((action) => action.value !== 'delete');
182        } else {
183          return item.actions;
184        }
185      };
186    },
187  },
188  watch: {
189    // Watch for change in tab index
190    tabIndex() {
191      this.getIpv6TableItems();
192      this.getDefaultGateway();
193    },
194    ethernetData() {
195      this.getIpv6TableItems();
196      this.getDefaultGateway();
197    },
198  },
199  created() {
200    this.getIpv6TableItems();
201    this.getDefaultGateway();
202    this.$store.dispatch('network/getEthernetData').finally(() => {
203      // Emit initial data fetch complete to parent component
204      require('@/eventBus').default.$emit('network-table-ipv6-complete');
205    });
206  },
207  methods: {
208    getDefaultGateway() {
209      this.defaultGateway = this.ethernetData[this.tabIndex].IPv6DefaultGateway;
210    },
211    getIpv6TableItems() {
212      const index = this.tabIndex;
213      const addresses =
214        this.ethernetData[index].IPv6Addresses.filter(
215          (ipv6) =>
216            ipv6.AddressOrigin === 'LinkLocal' ||
217            ipv6.AddressOrigin === 'Static' ||
218            ipv6.AddressOrigin === 'SLAAC' ||
219            ipv6.AddressOrigin === 'DHCPv6',
220        ) || [];
221      this.form.ipv6TableItems = addresses.map((ipv6) => {
222        return {
223          Address: ipv6.Address,
224          PrefixLength: ipv6.PrefixLength,
225          AddressOrigin: ipv6.AddressOrigin,
226          actions: [
227            {
228              value: 'delete',
229              title: i18n.global.t('pageNetwork.table.deleteIpv6'),
230            },
231          ],
232        };
233      });
234    },
235    onIpv6TableAction(action, $event, index) {
236      if ($event === 'delete') {
237        this.deleteIpv6TableRow(index);
238      }
239    },
240    deleteIpv6TableRow(index) {
241      const AddressOrigin = this.form.ipv6TableItems[index].AddressOrigin;
242      this.form.ipv6TableItems.splice(index, 1);
243      const newIpv6Array = this.form.ipv6TableItems.map((ipv6) => {
244        const { Address, PrefixLength } = ipv6;
245        return {
246          Address,
247          PrefixLength,
248        };
249      });
250      if (
251        newIpv6Array.length == 0 &&
252        (AddressOrigin === 'Static' || AddressOrigin === 'LinkLocal')
253      ) {
254        this.$store
255          .dispatch('network/saveDhcp6EnabledState', true)
256          .then((message) => this.successToast(message))
257          .catch(({ message }) => this.errorToast(message));
258      }
259      this.$store
260        .dispatch('network/editIpv6Address', newIpv6Array)
261        .then((message) => this.successToast(message))
262        .catch(({ message }) => this.errorToast(message));
263    },
264    initAddIpv6Address() {
265      this.showAddIpv6 = true;
266    },
267    changeDhcp6EnabledState(state) {
268      const dhcpState = state
269        ? i18n.global.t('global.action.enable')
270        : i18n.global.t('global.action.disable');
271      this.confirmDialog(
272        state
273          ? i18n.global.t('pageNetwork.modal.confirmEnableDhcp')
274          : i18n.global.t('pageNetwork.modal.confirmDisableDhcp'),
275        {
276          title: i18n.global.t('pageNetwork.modal.dhcpConfirmTitle', {
277            dhcpState,
278          }),
279          okTitle: dhcpState,
280          okVariant: 'danger',
281          cancelTitle: i18n.global.t('global.action.cancel'),
282          autoFocusButton: 'cancel',
283        },
284      ).then((dhcpEnableConfirmed) => {
285        if (dhcpEnableConfirmed) {
286          this.$store
287            .dispatch('network/saveDhcp6EnabledState', state)
288            .then((message) => this.successToast(message))
289            .catch(({ message }) => this.errorToast(message));
290        } else {
291          let onDhcpCancel = document.getElementById('dhcp6Switch');
292          onDhcpCancel.checked = !state;
293        }
294      });
295    },
296    confirmDialog(message, options = {}) {
297      return this.$confirm({ message, ...options });
298    },
299    initDefaultGatewayModal() {
300      this.showDefaultGatewayModal = true;
301    },
302  },
303};
304</script>
305