Add DumpsStore API calls

Ties in API requests to the Dumps page and adds ability to:
- Create new System or BMC dump
- Delete single or multiple BMC dumps. Uses ClearLog service to
  delete all and DELETE request for single dump deletion

Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: Iae928fa3b8fab00e549c33c0ab914a4b04de0f40
diff --git a/src/assets/styles/bmc/custom/_forms.scss b/src/assets/styles/bmc/custom/_forms.scss
index cea5ef5..428a40c 100644
--- a/src/assets/styles/bmc/custom/_forms.scss
+++ b/src/assets/styles/bmc/custom/_forms.scss
@@ -51,7 +51,7 @@
 .custom-select,
 .custom-control-label,
 .form-control {
-  color: theme-color("dark");
+  color: theme-color("dark") !important;
   font-size: 1rem;
 }
 
diff --git a/src/env/components/Dumps/Dumps.vue b/src/env/components/Dumps/Dumps.vue
index eba90b7..3bf5579 100644
--- a/src/env/components/Dumps/Dumps.vue
+++ b/src/env/components/Dumps/Dumps.vue
@@ -3,14 +3,14 @@
     <page-title />
     <b-row>
       <b-col sm="6" lg="5" xl="4">
-        <page-section :section-title="$t('pageDumps.newDump')">
+        <page-section :section-title="$t('pageDumps.initiateDump')">
           <dumps-form />
         </page-section>
       </b-col>
     </b-row>
     <b-row>
       <b-col xl="10">
-        <page-section :section-title="$t('pageDumps.dumpHistory')">
+        <page-section :section-title="$t('pageDumps.dumpsAvailableOnBmc')">
           <b-row class="align-items-start">
             <b-col sm="8" xl="6" class="d-sm-flex align-items-end">
               <search
@@ -41,6 +41,7 @@
             hover
             sort-icon-left
             no-sort-reset
+            sort-desc
             selectable
             no-select-on-click
             responsive="md"
@@ -121,6 +122,7 @@
   tableHeaderCheckboxModel,
   tableHeaderCheckboxIndeterminate,
 } from '@/components/Mixins/BVTableSelectableMixin';
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
 import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
 import SearchFilterMixin, {
   searchFilter,
@@ -141,6 +143,7 @@
   },
   mixins: [
     BVTableSelectableMixin,
+    BVToastMixin,
     LoadingBarMixin,
     SearchFilterMixin,
     TableFilterMixin,
@@ -202,7 +205,7 @@
   },
   computed: {
     dumps() {
-      return this.$store.getters['dumps/allDumps'];
+      return this.$store.getters['dumps/bmcDumps'];
     },
     tableItems() {
       return this.dumps.map((item) => {
@@ -246,7 +249,7 @@
       this.filterStartDate = fromDate;
       this.filterEndDate = toDate;
     },
-    onTableRowAction(action) {
+    onTableRowAction(action, dump) {
       if (action === 'delete') {
         this.$bvModal
           .msgBoxConfirm(this.$tc('pageDumps.modal.deleteDumpConfirmation'), {
@@ -255,7 +258,19 @@
             cancelTitle: this.$t('global.action.cancel'),
           })
           .then((deleteConfrimed) => {
-            if (deleteConfrimed); // delete dump
+            if (deleteConfrimed) {
+              this.$store
+                .dispatch('dumps/deleteDumps', [dump])
+                .then((messages) => {
+                  messages.forEach(({ type, message }) => {
+                    if (type === 'success') {
+                      this.successToast(message);
+                    } else if (type === 'error') {
+                      this.errorToast(message);
+                    }
+                  });
+                });
+            }
           });
       }
     },
@@ -280,7 +295,26 @@
             }
           )
           .then((deleteConfrimed) => {
-            if (deleteConfrimed); // delete dump
+            if (deleteConfrimed) {
+              if (this.selectedRows.length === this.dumps.length) {
+                this.$store
+                  .dispatch('dumps/deleteAllDumps')
+                  .then((success) => this.successToast(success))
+                  .catch(({ message }) => this.errorToast(message));
+              } else {
+                this.$store
+                  .dispatch('dumps/deleteDumps', this.selectedRows)
+                  .then((messages) => {
+                    messages.forEach(({ type, message }) => {
+                      if (type === 'success') {
+                        this.successToast(message);
+                      } else if (type === 'error') {
+                        this.errorToast(message);
+                      }
+                    });
+                  });
+              }
+            }
           });
       }
     },
diff --git a/src/env/components/Dumps/DumpsForm.vue b/src/env/components/Dumps/DumpsForm.vue
index ed81b3a..9dc8bcb 100644
--- a/src/env/components/Dumps/DumpsForm.vue
+++ b/src/env/components/Dumps/DumpsForm.vue
@@ -21,20 +21,28 @@
           {{ $t('global.form.required') }}
         </b-form-invalid-feedback>
       </b-form-group>
+      <alert variant="info" class="mb-3" :show="selectedDumpType === 'system'">
+        {{ $t('pageDumps.form.systemDumpInfo') }}
+      </alert>
       <b-button variant="primary" type="submit" form="form-new-dump">
-        {{ $t('pageDumps.form.createNewDump') }}
+        {{ $t('pageDumps.form.initiateDump') }}
       </b-button>
     </b-form>
+    <modal-confirmation @ok="createSystemDump" />
   </div>
 </template>
 
 <script>
 import { required } from 'vuelidate/lib/validators';
 
+import ModalConfirmation from './DumpsModalConfirmation';
+import Alert from '@/components/Global/Alert';
+
 import BVToastMixin from '@/components/Mixins/BVToastMixin';
 import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
 
 export default {
+  components: { Alert, ModalConfirmation },
   mixins: [BVToastMixin, VuelidateMixin],
   data() {
     return {
@@ -54,7 +62,33 @@
     handleSubmit() {
       this.$v.$touch();
       if (this.$v.$invalid) return;
-      this.successToast(this.$t('pageDumps.toast.successStartDump'));
+      if (this.selectedDumpType === 'system') {
+        this.showConfirmationModal();
+      } else {
+        this.$store
+          .dispatch('dumps/createBmcDump')
+          .then(() =>
+            this.infoToast(
+              this.$t('pageDumps.toast.successStartBmcDump'),
+              this.$t('pageDumps.toast.successStartBmcDumpTitle')
+            )
+          )
+          .catch(({ message }) => this.errorToast(message));
+      }
+    },
+    showConfirmationModal() {
+      this.$bvModal.show('modal-confirmation');
+    },
+    createSystemDump() {
+      this.$store
+        .dispatch('dumps/createSystemDump')
+        .then(() =>
+          this.infoToast(
+            this.$t('pageDumps.toast.successStartSystemDump'),
+            this.$t('pageDumps.toast.successStartSystemDumpTitle')
+          )
+        )
+        .catch(({ message }) => this.errorToast(message));
     },
   },
 };
diff --git a/src/env/components/Dumps/DumpsModalConfirmation.vue b/src/env/components/Dumps/DumpsModalConfirmation.vue
new file mode 100644
index 0000000..f8e20cf
--- /dev/null
+++ b/src/env/components/Dumps/DumpsModalConfirmation.vue
@@ -0,0 +1,75 @@
+<template>
+  <b-modal
+    id="modal-confirmation"
+    ref="modal"
+    :title="$t('pageDumps.modal.initiateSystemDump')"
+    @hidden="resetForm"
+  >
+    <p>
+      <strong>
+        {{ $t('pageDumps.modal.initiateSystemDumpMessage1') }}
+      </strong>
+    </p>
+    <p>
+      {{ $t('pageDumps.modal.initiateSystemDumpMessage2') }}
+    </p>
+    <p>
+      <status-icon status="danger" />
+      {{ $t('pageDumps.modal.initiateSystemDumpMessage3') }}
+    </p>
+    <b-form-checkbox v-model="confirmed" @input="$v.confirmed.$touch()">
+      {{ $t('pageDumps.modal.initiateSystemDumpMessage4') }}
+    </b-form-checkbox>
+    <b-form-invalid-feedback
+      :state="getValidationState($v.confirmed)"
+      role="alert"
+    >
+      {{ $t('global.form.required') }}
+    </b-form-invalid-feedback>
+    <template #modal-footer="{ cancel }">
+      <b-button variant="secondary" @click="cancel()">
+        {{ $t('global.action.cancel') }}
+      </b-button>
+      <b-button variant="danger" @click="handleSubmit">
+        {{ $t('pageDumps.form.initiateDump') }}
+      </b-button>
+    </template>
+  </b-modal>
+</template>
+
+<script>
+import StatusIcon from '@/components/Global/StatusIcon';
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
+
+export default {
+  components: { StatusIcon },
+  mixins: [VuelidateMixin],
+  data() {
+    return {
+      confirmed: false,
+    };
+  },
+  validations: {
+    confirmed: {
+      mustBeTrue: (value) => value === true,
+    },
+  },
+  methods: {
+    closeModal() {
+      this.$nextTick(() => {
+        this.$refs.modal.hide();
+      });
+    },
+    handleSubmit() {
+      this.$v.$touch();
+      if (this.$v.$invalid) return;
+      this.$emit('ok');
+      this.closeModal();
+    },
+    resetForm() {
+      this.confirmed = false;
+      this.$v.$reset();
+    },
+  },
+};
+</script>
diff --git a/src/env/store/Dumps/DumpsStore.js b/src/env/store/Dumps/DumpsStore.js
index 45f446c..3b91354 100644
--- a/src/env/store/Dumps/DumpsStore.js
+++ b/src/env/store/Dumps/DumpsStore.js
@@ -1,4 +1,5 @@
-import api from '@/store/api';
+import api, { getResponseCount } from '@/store/api';
+import i18n from '@/i18n';
 
 const DumpsStore = {
   namespaced: true,
@@ -6,16 +7,17 @@
     bmcDumps: [],
   },
   getters: {
-    allDumps: (state) => state.bmcDumps,
+    bmcDumps: (state) => state.bmcDumps,
   },
   mutations: {
     setBmcDumps: (state, dumps) => {
       state.bmcDumps = dumps.map((dump) => ({
+        data: dump.AdditionalDataURI,
         dateTime: new Date(dump.Created),
         dumpType: dump.Name,
         id: dump.Id,
+        location: dump['@odata.id'],
         size: dump.AdditionalDataSizeBytes,
-        data: dump.AdditionalDataURI,
       }));
     },
   },
@@ -26,6 +28,89 @@
         .then(({ data = {} }) => commit('setBmcDumps', data.Members || []))
         .catch((error) => console.log(error));
     },
+    async createBmcDump() {
+      return await api
+        .post(
+          '/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData',
+          {
+            DiagnosticDataType: 'Manager',
+            OEMDiagnosticDataType: '',
+          }
+        )
+        .catch((error) => {
+          console.log(error);
+          throw new Error(i18n.t('pageDumps.toast.errorStartBmcDump'));
+        });
+    },
+    async createSystemDump() {
+      return await api
+        .post(
+          '/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData',
+          {
+            DiagnosticDataType: 'OEM',
+            OEMDiagnosticDataType: 'System',
+          }
+        )
+        .catch((error) => {
+          console.log(error);
+          throw new Error(i18n.t('pageDumps.toast.errorStartSystemDump'));
+        });
+    },
+    async deleteDumps({ dispatch }, dumps) {
+      const promises = dumps.map(({ location }) =>
+        api.delete(location).catch((error) => {
+          console.log(error);
+          return error;
+        })
+      );
+      return await api
+        .all(promises)
+        .then((response) => {
+          dispatch('getBmcDumps');
+          return response;
+        })
+        .then(
+          api.spread((...responses) => {
+            const { successCount, errorCount } = getResponseCount(responses);
+            const toastMessages = [];
+
+            if (successCount) {
+              const message = i18n.tc(
+                'pageDumps.toast.successDeleteDump',
+                successCount
+              );
+              toastMessages.push({ type: 'success', message });
+            }
+
+            if (errorCount) {
+              const message = i18n.tc(
+                'pageDumps.toast.errorDeleteDump',
+                errorCount
+              );
+              toastMessages.push({ type: 'error', message });
+            }
+
+            return toastMessages;
+          })
+        );
+    },
+    async deleteAllDumps({ commit, state }) {
+      const totalDumpCount = state.bmcDumps.length;
+      return await api
+        .post(
+          '/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog'
+        )
+        .then(() => {
+          commit('setBmcDumps', []);
+          return i18n.tc('pageDumps.toast.successDeleteDump', totalDumpCount);
+        })
+        .catch((error) => {
+          console.log(error);
+          throw new Error(
+            i18n.tc('pageDumps.toast.errorDeleteDump', totalDumpCount)
+          );
+        });
+    },
   },
 };
 
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index 1d564ca..ab9d823 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -201,17 +201,23 @@
     }
   },
   "pageDumps": {
-    "dumpHistory": "Dump history",
-    "newDump": "New dump",
+    "dumpsAvailableOnBmc": "Dumps available on BMC",
+    "initiateDump": "Initiate dump",
     "form": {
       "bmcDump": "BMC dump",
-      "createNewDump": "Create new dump",
+      "initiateDump": "Initiate dump",
       "selectDumpType": "Select dump type",
-      "systemDump": "System dump (disruptive)"
+      "systemDump": "System dump (disruptive)",
+      "systemDumpInfo": "System dumps will be offloaded to the operating system and will not appear in the table below."
     },
     "modal": {
       "deleteDump": "Delete dump | Delete dumps",
-      "deleteDumpConfirmation": "Are you sure you want to delete %{count} dump? This action cannot be undone. | Are you sure you want to delete %{count} dumps? This action cannot be undone."
+      "deleteDumpConfirmation": "Are you sure you want to delete %{count} dump? This action cannot be undone. | Are you sure you want to delete %{count} dumps? This action cannot be undone.",
+      "initiateSystemDump": "Initiate system dump",
+      "initiateSystemDumpMessage1": "Are you sure?",
+      "initiateSystemDumpMessage2": "You will not be able to initiate any other dumps while a system dump is in progress.",
+      "initiateSystemDumpMessage3": "Initiating a system dump will abnormally terminate all active system partitions.",
+      "initiateSystemDumpMessage4": "Proceed with dump initiation"
     },
     "table": {
       "createdBy": "Created by",
@@ -222,7 +228,14 @@
       "size": "Size"
     },
     "toast": {
-      "successStartDump": "Successfully started new dump."
+      "errorDeleteDump": "Error deleting %{count} dump. | Error deleting %{count} dumps.",
+      "errorStartBmcDump": "Error starting new BMC dump.",
+      "errorStartSystemDump": "Error starting new System dump.",
+      "successDeleteDump": "Successfully deleted %{count} dump. | Successfully deleted %{count} dumps.",
+      "successStartBmcDump": "The dump will take some time to complete. Refresh the application to see the completed dump in the table.",
+      "successStartBmcDumpTitle": "BMC dump started",
+      "successStartSystemDump": "The dump will take some time to complete. The dump will be offloaded to the operating system.",
+      "successStartSystemDumpTitle": "System dump started"
     }
   },
   "pageEventLogs": {