Make copy of single file firmware upload
Make a copy of single file firmware upload component and store
in env directory.
Update IBM router to use copied single file firmware components.
Update IBM store module to use copied single file firmware store.
Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: I046ce6c69a02b90400058866295b84c5435f5a4b
diff --git a/.env.ibm b/.env.ibm
index cb57a87..4fe4ab4 100644
--- a/.env.ibm
+++ b/.env.ibm
@@ -3,4 +3,5 @@
VUE_APP_COMPANY_NAME="IBM"
CUSTOM_STYLES=true
CUSTOM_APP_NAV=true
-CUSTOM_ROUTER=true
\ No newline at end of file
+CUSTOM_ROUTER=true
+CUSTOM_STORE=true
\ No newline at end of file
diff --git a/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue b/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue
new file mode 100644
index 0000000..f719631
--- /dev/null
+++ b/src/env/components/FirmwareSingleImage/FirmwareSingleImage.vue
@@ -0,0 +1,405 @@
+<template>
+ <b-container fluid="xl">
+ <page-title :description="$t('pageFirmware.pageDescription')" />
+ <!-- Operation in progress alert -->
+ <alert v-if="isOperationInProgress" variant="info" class="mb-5">
+ <p>
+ {{ $t('pageFirmware.alert.operationInProgress') }}
+ </p>
+ </alert>
+ <!-- Shutdown server warning alert -->
+ <alert v-else-if="!isHostOff" variant="warning" class="mb-5">
+ <p class="font-weight-bold mb-1">
+ {{ $t('pageFirmware.alert.serverShutdownRequiredBeforeUpdate') }}
+ </p>
+ {{ $t('pageFirmware.alert.serverShutdownRequiredInfo') }}
+ <template v-slot:action>
+ <b-btn variant="link" class="text-nowrap" @click="onClickShutDown">
+ {{ $t('pageFirmware.alert.shutDownServer') }}
+ </b-btn>
+ </template>
+ </alert>
+ <b-row class="mb-4">
+ <!-- Firmware on system -->
+ <b-col md="10" lg="12" xl="8" class="pr-xl-4">
+ <page-section :section-title="$t('pageFirmware.firmwareOnSystem')">
+ <b-card-group deck>
+ <!-- Current FW -->
+ <b-card header-bg-variant="success">
+ <template v-slot:header>
+ <dl class="mb-0">
+ <dt>{{ $t('pageFirmware.current') }}</dt>
+ <dd class="mb-0">{{ systemFirmwareVersion }}</dd>
+ </dl>
+ </template>
+ <b-row>
+ <b-col xs="6">
+ <dl class="my-0">
+ <dt>{{ $t('pageFirmware.bmcStatus') }}</dt>
+ <dd>{{ $t('pageFirmware.running') }}</dd>
+ </dl>
+ </b-col>
+ <b-col xs="6">
+ <dl class="my-0">
+ <dt>{{ $t('pageFirmware.hostStatus') }}</dt>
+ <dd v-if="hostStatus === 'on'">
+ {{ $t('global.status.on') }}
+ </dd>
+ <dd v-else-if="hostStatus === 'off'">
+ {{ $t('global.status.off') }}
+ </dd>
+ <dd v-else>
+ {{ $t('global.status.notAvailable') }}
+ </dd>
+ </dl>
+ </b-col>
+ </b-row>
+ </b-card>
+
+ <!-- Backup FW -->
+ <b-card>
+ <template v-slot:header>
+ <dl class="mb-0">
+ <dt>{{ $t('pageFirmware.backup') }}</dt>
+ <dd class="mb-0">{{ backupFirmwareVersion }}</dd>
+ </dl>
+ </template>
+ <b-row>
+ <b-col xs="6">
+ <dl class="my-0">
+ <dt>{{ $t('pageFirmware.state') }}</dt>
+ <dd>{{ backupFirmwareStatus }}</dd>
+ </dl>
+ </b-col>
+ </b-row>
+ </b-card>
+ </b-card-group>
+ </page-section>
+
+ <!-- Change to backup image -->
+ <page-section :section-title="$t('pageFirmware.changeToBackupImage')">
+ <dl class="mb-5">
+ <dt>
+ {{ $t('pageFirmware.backupImage') }}
+ </dt>
+ <dd>{{ backupFirmwareVersion }}</dd>
+ </dl>
+ <b-btn
+ v-b-modal.modal-reboot-backup
+ type="button"
+ variant="primary"
+ :disabled="isPageDisabled || !isRebootFromBackupAvailable"
+ >
+ {{ $t('pageFirmware.changeAndRebootBmc') }}
+ </b-btn>
+ </page-section>
+ </b-col>
+
+ <!-- Update code -->
+ <b-col sm="8" xl="4" class="update-code pl-xl-4">
+ <page-section :section-title="$t('pageFirmware.updateCode')">
+ <b-form @submit.prevent="onSubmitUpload">
+ <b-form-group
+ :label="$t('pageFirmware.form.uploadLocation')"
+ :disabled="isPageDisabled"
+ >
+ <b-form-radio v-model="isWorkstationSelected" :value="true">
+ {{ $t('pageFirmware.form.workstation') }}
+ </b-form-radio>
+ <b-form-radio v-model="isWorkstationSelected" :value="false">
+ {{ $t('pageFirmware.form.tftpServer') }}
+ </b-form-radio>
+ </b-form-group>
+
+ <!-- Workstation Upload -->
+ <template v-if="isWorkstationSelected">
+ <b-form-group
+ :label="$t('pageFirmware.form.imageFile')"
+ label-for="image-file"
+ >
+ <b-form-text id="image-file-help-block">
+ {{ $t('pageFirmware.form.onlyTarFilesAccepted') }}
+ </b-form-text>
+ <b-form-file
+ id="image-file"
+ v-model="file"
+ accept=".tar"
+ aria-describedby="image-file-help-block"
+ :browse-text="$t('global.fileUpload.browseText')"
+ :drop-placeholder="$t('global.fileUpload.dropPlaceholder')"
+ :placeholder="$t('global.fileUpload.placeholder')"
+ :disabled="isPageDisabled"
+ :state="getValidationState($v.file)"
+ @input="$v.file.$touch()"
+ />
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.required') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </template>
+
+ <!-- TFTP Server Upload -->
+ <template v-else>
+ <b-form-group
+ :label="$t('pageFirmware.form.tftpServerAddress')"
+ label-for="tftp-ip"
+ >
+ <b-form-text id="server-address-help-block">
+ {{ $t('pageFirmware.form.tftpServerAddressHelper') }}
+ </b-form-text>
+ <b-form-input
+ id="tftp-id"
+ v-model="tftpIpAddress"
+ type="text"
+ :state="getValidationState($v.tftpIpAddress)"
+ :disabled="isPageDisabled"
+ aria-describedby="server-address-help-block"
+ @input="$v.tftpIpAddress.$touch()"
+ />
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.fieldRequired') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ <b-form-group
+ :label="$t('pageFirmware.form.imageFileName')"
+ label-for="tftp-file-name"
+ >
+ <b-form-input
+ id="tftp-file-name"
+ v-model="tftpFileName"
+ type="text"
+ :state="getValidationState($v.tftpFileName)"
+ :disabled="isPageDisabled"
+ @input="$v.tftpFileName.$touch()"
+ />
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.fieldRequired') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </template>
+
+ <!-- Info alert -->
+ <alert variant="info" class="mt-4 mb-5">
+ <p class="font-weight-bold mb-1">
+ {{ $t('pageFirmware.alert.updateProcess') }}
+ </p>
+ <p>{{ $t('pageFirmware.alert.updateProcessInfo') }}</p>
+ </alert>
+ <b-form-group>
+ <b-btn type="submit" variant="primary" :disabled="isPageDisabled">
+ {{ $t('pageFirmware.form.uploadAndRebootBmc') }}
+ </b-btn>
+ </b-form-group>
+ </b-form>
+ </page-section>
+ </b-col>
+ </b-row>
+
+ <!-- Modals -->
+ <modal-upload @ok="uploadFirmware" />
+ <modal-reboot-backup
+ :current="systemFirmwareVersion"
+ :backup="backupFirmwareVersion"
+ @ok="rebootFromBackup"
+ />
+ </b-container>
+</template>
+
+<script>
+import { requiredIf } from 'vuelidate/lib/validators';
+import { mapGetters } from 'vuex';
+
+import PageSection from '@/components/Global/PageSection';
+import PageTitle from '@/components/Global/PageTitle';
+import Alert from '@/components/Global/Alert';
+import ModalUpload from './FirmwareSingleImageModalUpload';
+import ModalRebootBackup from './FirmwareSingleImageModalRebootBackup';
+
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
+import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
+
+export default {
+ name: 'FirmwareSingleImage',
+ components: {
+ Alert,
+ ModalRebootBackup,
+ ModalUpload,
+ PageSection,
+ PageTitle
+ },
+ mixins: [BVToastMixin, LoadingBarMixin, VuelidateMixin],
+ data() {
+ return {
+ isWorkstationSelected: true,
+ file: null,
+ tftpIpAddress: null,
+ tftpFileName: null,
+ timeoutId: null
+ };
+ },
+ computed: {
+ hostStatus() {
+ return this.$store.getters['global/hostStatus'];
+ },
+ isHostOff() {
+ return this.hostStatus === 'off' ? true : false;
+ },
+ isOperationInProgress() {
+ return this.$store.getters['controls/isOperationInProgress'];
+ },
+ ...mapGetters('firmwareSingleImage', [
+ 'backupFirmwareStatus',
+ 'backupFirmwareVersion',
+ 'isRebootFromBackupAvailable',
+ 'systemFirmwareVersion'
+ ]),
+ isPageDisabled() {
+ return !this.isHostOff || this.loading || this.isOperationInProgress;
+ }
+ },
+ watch: {
+ isWorkstationSelected: function() {
+ this.$v.$reset();
+ this.file = null;
+ this.tftpIpAddress = null;
+ this.tftpFileName = null;
+ }
+ },
+ created() {
+ this.startLoader();
+ this.$store.dispatch('firmwareSingleImage/getUpdateServiceApplyTime');
+ Promise.all([
+ this.$store.dispatch('global/getHostStatus'),
+ this.$store.dispatch('firmwareSingleImage/getSystemFirwareVersion')
+ ]).finally(() => this.endLoader());
+ },
+ beforeRouteLeave(to, from, next) {
+ this.hideLoader();
+ this.clearRebootTimeout();
+ next();
+ },
+ validations() {
+ return {
+ file: {
+ required: requiredIf(function() {
+ return this.isWorkstationSelected;
+ })
+ },
+ tftpIpAddress: {
+ required: requiredIf(function() {
+ return !this.isWorkstationSelected;
+ })
+ },
+ tftpFileName: {
+ required: requiredIf(function() {
+ return !this.isWorkstationSelected;
+ })
+ }
+ };
+ },
+ methods: {
+ uploadFirmware() {
+ const startTime = this.$options.filters.formatTime(new Date());
+ this.setRebootTimeout(360000); //6 minute timeout
+ this.infoToast(
+ this.$t('pageFirmware.toast.infoUploadStartTimeMessage', { startTime }),
+ this.$t('pageFirmware.toast.infoUploadStartTimeTitle')
+ );
+ if (this.isWorkstationSelected) {
+ this.dispatchWorkstationUpload();
+ } else {
+ this.dispatchTftpUpload();
+ }
+ },
+ dispatchWorkstationUpload() {
+ this.$store
+ .dispatch('firmwareSingleImage/uploadFirmware', this.file)
+ .then(success =>
+ this.infoToast(
+ success,
+ this.$t('pageFirmware.toast.successUploadTitle')
+ )
+ )
+ .catch(({ message }) => {
+ this.errorToast(message);
+ this.clearRebootTimeout();
+ });
+ },
+ dispatchTftpUpload() {
+ const data = {
+ address: this.tftpIpAddress,
+ filename: this.tftpFileName
+ };
+ this.$store
+ .dispatch('firmwareSingleImage/uploadFirmwareTFTP', data)
+ .then(success =>
+ this.infoToast(
+ success,
+ this.$t('pageFirmware.toast.successUploadTitle')
+ )
+ )
+ .catch(({ message }) => {
+ this.errorToast(message);
+ this.clearRebootTimeout();
+ });
+ },
+ rebootFromBackup() {
+ this.setRebootTimeout();
+ this.$store
+ .dispatch('firmwareSingleImage/switchFirmwareAndReboot')
+ .then(success =>
+ this.infoToast(success, this.$t('global.status.success'))
+ )
+ .catch(({ message }) => {
+ this.errorToast(message);
+ this.clearRebootTimeout();
+ });
+ },
+ setRebootTimeout(timeoutMs = 60000) {
+ // Set a timeout to disable page interactions while
+ // an upload or BMC reboot is in progress
+ this.startLoader();
+ this.timeoutId = setTimeout(() => {
+ this.endLoader();
+ this.infoToast(
+ this.$t('pageFirmware.toast.infoRefreshApplicationMessage'),
+ this.$t('pageFirmware.toast.infoRefreshApplicationTitle')
+ );
+ }, timeoutMs);
+ },
+ clearRebootTimeout() {
+ if (this.timeoutId) {
+ clearTimeout(this.timeoutId);
+ this.endLoader();
+ }
+ },
+ onSubmitUpload() {
+ this.$v.$touch();
+ if (this.$v.$invalid) return;
+ this.$bvModal.show('modal-upload');
+ },
+ onClickShutDown() {
+ this.$bvModal
+ .msgBoxConfirm(this.$t('pageFirmware.modal.serverShutdownMessage'), {
+ title: this.$t('pageFirmware.modal.serverShutdownWillCauseOutage'),
+ okTitle: this.$t('pageFirmware.modal.shutDownServer'),
+ okVariant: 'danger'
+ })
+ .then(shutdownConfirmed => {
+ if (shutdownConfirmed)
+ this.$store.dispatch('controls/hostSoftPowerOff');
+ });
+ }
+ }
+};
+</script>
+
+<style lang="scss" scoped>
+.update-code {
+ border-left: none;
+ @include media-breakpoint-up(xl) {
+ border-left: 1px solid gray('300');
+ }
+}
+</style>
diff --git a/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalRebootBackup.vue b/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalRebootBackup.vue
new file mode 100644
index 0000000..a8fb3ad
--- /dev/null
+++ b/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalRebootBackup.vue
@@ -0,0 +1,33 @@
+<template>
+ <b-modal
+ id="modal-reboot-backup"
+ :ok-title="$t('pageFirmware.modal.rebootFromBackup.primaryAction')"
+ :title="$t('pageFirmware.modal.rebootFromBackup.title')"
+ @ok="$emit('ok')"
+ >
+ <p>
+ {{ $t('pageFirmware.modal.rebootFromBackup.message1', { backup }) }}
+ </p>
+ <p>
+ {{ $t('pageFirmware.modal.rebootFromBackup.message2', { current }) }}
+ </p>
+ <p class="font-weight-bold">
+ {{ $t('pageFirmware.modal.rebootFromBackup.message3', { backup }) }}
+ </p>
+ </b-modal>
+</template>
+
+<script>
+export default {
+ props: {
+ current: {
+ type: String,
+ required: true
+ },
+ backup: {
+ type: String,
+ required: true
+ }
+ }
+};
+</script>
diff --git a/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalUpload.vue b/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalUpload.vue
new file mode 100644
index 0000000..d092bec
--- /dev/null
+++ b/src/env/components/FirmwareSingleImage/FirmwareSingleImageModalUpload.vue
@@ -0,0 +1,18 @@
+<template>
+ <b-modal
+ id="modal-upload"
+ :title="$t('pageFirmware.modal.uploadAndReboot.title')"
+ :ok-title="$t('pageFirmware.modal.uploadAndReboot.primaryAction')"
+ @ok="$emit('ok')"
+ >
+ <p>
+ {{ $t('pageFirmware.modal.uploadAndReboot.message1') }}
+ </p>
+ <p>
+ {{ $t('pageFirmware.modal.uploadAndReboot.message2') }}
+ </p>
+ <p class="font-weight-bold">
+ {{ $t('pageFirmware.modal.uploadAndReboot.message3') }}
+ </p>
+ </b-modal>
+</template>
diff --git a/src/env/components/FirmwareSingleImage/index.js b/src/env/components/FirmwareSingleImage/index.js
new file mode 100644
index 0000000..06f31f9
--- /dev/null
+++ b/src/env/components/FirmwareSingleImage/index.js
@@ -0,0 +1,2 @@
+import FirmwareSingleImage from './FirmwareSingleImage.vue';
+export default FirmwareSingleImage;
diff --git a/src/env/router/ibm.js b/src/env/router/ibm.js
index 213920d..bfba0f0 100644
--- a/src/env/router/ibm.js
+++ b/src/env/router/ibm.js
@@ -1,15 +1,225 @@
-import { remove } from 'lodash';
-import routes from '@/router/routes';
+import AppLayout from '@/layouts/AppLayout.vue';
+import ChangePassword from '@/views/ChangePassword';
+import ConsoleLayout from '@/layouts/ConsoleLayout.vue';
+import DateTimeSettings from '@/views/Configuration/DateTimeSettings';
+import EventLogs from '@/views/Health/EventLogs';
+import HardwareStatus from '@/views/Health/HardwareStatus';
+import Ldap from '@/views/AccessControl/Ldap';
+import LocalUserManagement from '@/views/AccessControl/LocalUserManagement';
+import Login from '@/views/Login';
+import LoginLayout from '@/layouts/LoginLayout';
+import ManagePowerUsage from '@/views/Control/ManagePowerUsage';
+import NetworkSettings from '@/views/Configuration/NetworkSettings';
+import Overview from '@/views/Overview';
+import PageNotFound from '@/views/PageNotFound';
+import ProfileSettings from '@/views/ProfileSettings';
+import RebootBmc from '@/views/Control/RebootBmc';
+import Sensors from '@/views/Health/Sensors';
+import SerialOverLan from '@/views/Control/SerialOverLan';
+import SerialOverLanConsole from '@/views/Control/SerialOverLan/SerialOverLanConsole';
+import ServerLed from '@/views/Control/ServerLed';
+import ServerPowerOperations from '@/views/Control/ServerPowerOperations';
+import SslCertificates from '@/views/AccessControl/SslCertificates';
+import VirtualMedia from '@/views/Control/VirtualMedia';
+import i18n from '@/i18n';
-const customRoutes = routes.map(route => {
- // Removes router definition that includes kvm
- // in name property (main and console layouts)
+// Custom components
+import FirmwareSingleImage from '../components/FirmwareSingleImage';
- // TODO: will revisit this, removing the definition
- // removes the route from the application but
- // the component is still included in the final build
- remove(route.children, ({ name }) => name.includes('kvm'));
- return route;
-});
+const routes = [
+ {
+ path: '/login',
+ component: LoginLayout,
+ children: [
+ {
+ path: '',
+ name: 'login',
+ component: Login,
+ meta: {
+ title: i18n.t('appPageTitle.login')
+ }
+ },
+ {
+ path: '/change-password',
+ name: 'change-password',
+ component: ChangePassword,
+ meta: {
+ title: i18n.t('appPageTitle.changePassword'),
+ requiresAuth: true
+ }
+ }
+ ]
+ },
+ {
+ path: '/console',
+ component: ConsoleLayout,
+ meta: {
+ requiresAuth: true
+ },
+ children: [
+ {
+ path: 'serial-over-lan-console',
+ name: 'serial-over-lan-console',
+ component: SerialOverLanConsole,
+ meta: {
+ title: i18n.t('appPageTitle.serialOverLan')
+ }
+ }
+ ]
+ },
+ {
+ path: '/',
+ meta: {
+ requiresAuth: true
+ },
+ component: AppLayout,
+ children: [
+ {
+ path: '',
+ name: 'overview',
+ component: Overview,
+ meta: {
+ title: i18n.t('appPageTitle.overview')
+ }
+ },
+ {
+ path: '/profile-settings',
+ name: 'profile-settings',
+ component: ProfileSettings,
+ meta: {
+ title: i18n.t('appPageTitle.profileSettings')
+ }
+ },
+ {
+ path: '/health/event-logs',
+ name: 'event-logs',
+ component: EventLogs,
+ meta: {
+ title: i18n.t('appPageTitle.eventLogs')
+ }
+ },
+ {
+ path: '/health/hardware-status',
+ name: 'hardware-status',
+ component: HardwareStatus,
+ meta: {
+ title: i18n.t('appPageTitle.hardwareStatus')
+ }
+ },
+ {
+ path: '/health/sensors',
+ name: 'sensors',
+ component: Sensors,
+ meta: {
+ title: i18n.t('appPageTitle.sensors')
+ }
+ },
+ {
+ path: '/access-control/ldap',
+ name: 'ldap',
+ component: Ldap,
+ meta: {
+ title: i18n.t('appPageTitle.ldap')
+ }
+ },
+ {
+ path: '/access-control/local-user-management',
+ name: 'local-users',
+ component: LocalUserManagement,
+ meta: {
+ title: i18n.t('appPageTitle.localUserManagement')
+ }
+ },
+ {
+ path: '/access-control/ssl-certificates',
+ name: 'ssl-certificates',
+ component: SslCertificates,
+ meta: {
+ title: i18n.t('appPageTitle.sslCertificates')
+ }
+ },
+ {
+ path: '/configuration/date-time-settings',
+ name: 'date-time-settings',
+ component: DateTimeSettings,
+ meta: {
+ title: i18n.t('appPageTitle.dateTimeSettings')
+ }
+ },
+ {
+ path: '/configuration/firmware',
+ name: 'firmware',
+ component: FirmwareSingleImage,
+ meta: {
+ title: i18n.t('appPageTitle.firmware')
+ }
+ },
+ {
+ path: '/control/manage-power-usage',
+ name: 'manage-power-usage',
+ component: ManagePowerUsage,
+ meta: {
+ title: i18n.t('appPageTitle.managePowerUsage')
+ }
+ },
+ {
+ path: '/configuration/network-settings',
+ name: 'network-settings',
+ component: NetworkSettings,
+ meta: {
+ title: i18n.t('appPageTitle.networkSettings')
+ }
+ },
+ {
+ path: '/control/reboot-bmc',
+ name: 'reboot-bmc',
+ component: RebootBmc,
+ meta: {
+ title: i18n.t('appPageTitle.rebootBmc')
+ }
+ },
+ {
+ path: '/control/server-led',
+ name: 'server-led',
+ component: ServerLed,
+ meta: {
+ title: i18n.t('appPageTitle.serverLed')
+ }
+ },
+ {
+ path: '/control/serial-over-lan',
+ name: 'serial-over-lan',
+ component: SerialOverLan,
+ meta: {
+ title: i18n.t('appPageTitle.serialOverLan')
+ }
+ },
+ {
+ path: '/control/server-power-operations',
+ name: 'server-power-operations',
+ component: ServerPowerOperations,
+ meta: {
+ title: i18n.t('appPageTitle.serverPowerOperations')
+ }
+ },
+ {
+ path: '/control/virtual-media',
+ name: 'virtual-media',
+ component: VirtualMedia,
+ meta: {
+ title: i18n.t('appPageTitle.virtualMedia')
+ }
+ },
+ {
+ path: '*',
+ name: 'page-not-found',
+ component: PageNotFound,
+ meta: {
+ title: i18n.t('appPageTitle.pageNotFound')
+ }
+ }
+ ]
+ }
+];
-export default customRoutes;
+export default routes;
diff --git a/src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js b/src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js
new file mode 100644
index 0000000..d00c5f7
--- /dev/null
+++ b/src/env/store/FirmwareSingleImage/FirmwareSingleImageStore.js
@@ -0,0 +1,161 @@
+import api from '@/store/api';
+import i18n from '@/i18n';
+
+const FirmwareSingleImageStore = {
+ namespaced: true,
+ state: {
+ activeFirmware: {
+ version: '--',
+ id: null,
+ location: null
+ },
+ backupFirmware: {
+ version: '--',
+ id: null,
+ location: null,
+ status: '--'
+ },
+ applyTime: null
+ },
+ getters: {
+ systemFirmwareVersion: state => state.activeFirmware.version,
+ backupFirmwareVersion: state => state.backupFirmware.version,
+ backupFirmwareStatus: state => state.backupFirmware.status,
+ isRebootFromBackupAvailable: state =>
+ state.backupFirmware.id ? true : false
+ },
+ mutations: {
+ setActiveFirmware: (state, { version, id, location }) => {
+ state.activeFirmware.version = version;
+ state.activeFirmware.id = id;
+ state.activeFirmware.location = location;
+ },
+ setBackupFirmware: (state, { version, id, location, status }) => {
+ state.backupFirmware.version = version;
+ state.backupFirmware.id = id;
+ state.backupFirmware.location = location;
+ state.backupFirmware.status = status;
+ },
+ setApplyTime: (state, applyTime) => (state.applyTime = applyTime)
+ },
+ actions: {
+ async getSystemFirwareVersion({ commit }) {
+ return await api
+ .get('/redfish/v1/Managers/bmc')
+ .then(({ data: { Links } }) => {
+ const currentLocation = Links.ActiveSoftwareImage['@odata.id'];
+ // Check SoftwareImages list for not ActiveSoftwareImage id
+ const backupLocation = Links.SoftwareImages.map(
+ item => item['@odata.id']
+ ).find(location => {
+ const id = location.split('/').pop();
+ const currentId = currentLocation.split('/').pop();
+ return id !== currentId;
+ });
+ return { currentLocation, backupLocation };
+ })
+ .then(async ({ currentLocation, backupLocation }) => {
+ const currentData = await api.get(currentLocation);
+ let backupData = {};
+
+ if (backupLocation) {
+ backupData = await api.get(backupLocation);
+ }
+
+ commit('setActiveFirmware', {
+ version: currentData?.data?.Version,
+ id: currentData?.data?.Id,
+ location: currentData?.data?.['@odata.id']
+ });
+ commit('setBackupFirmware', {
+ version: backupData.data?.Version,
+ id: backupData.data?.Id,
+ location: backupData.data?.['@odata.id'],
+ status: backupData.data?.Status?.State
+ });
+ })
+ .catch(error => console.log(error));
+ },
+ getUpdateServiceApplyTime({ commit }) {
+ api
+ .get('/redfish/v1/UpdateService')
+ .then(({ data }) => {
+ const applyTime =
+ data.HttpPushUriOptions.HttpPushUriApplyTime.ApplyTime;
+ commit('setApplyTime', applyTime);
+ })
+ .catch(error => console.log(error));
+ },
+ setApplyTimeImmediate({ commit }) {
+ const data = {
+ HttpPushUriOptions: {
+ HttpPushUriApplyTime: {
+ ApplyTime: 'Immediate'
+ }
+ }
+ };
+ return api
+ .patch('/redfish/v1/UpdateService', data)
+ .then(() => commit('setApplyTime', 'Immediate'))
+ .catch(error => console.log(error));
+ },
+ async uploadFirmware({ state, dispatch }, image) {
+ if (state.applyTime !== 'Immediate') {
+ // ApplyTime must be set to Immediate before making
+ // request to update firmware
+ await dispatch('setApplyTimeImmediate');
+ }
+ return await api
+ .post('/redfish/v1/UpdateService', image, {
+ headers: { 'Content-Type': 'application/octet-stream' }
+ })
+ .then(() => dispatch('getSystemFirwareVersion'))
+ .then(() => i18n.t('pageFirmware.toast.successUploadMessage'))
+ .catch(error => {
+ console.log(error);
+ throw new Error(i18n.t('pageFirmware.toast.errorUploadAndReboot'));
+ });
+ },
+ async uploadFirmwareTFTP({ state, dispatch }, { address, filename }) {
+ const data = {
+ TransferProtocol: 'TFTP',
+ ImageURI: `${address}/${filename}`
+ };
+ if (state.applyTime !== 'Immediate') {
+ // ApplyTime must be set to Immediate before making
+ // request to update firmware
+ await dispatch('setApplyTimeImmediate');
+ }
+ return await api
+ .post(
+ '/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate',
+ data
+ )
+ .then(() => dispatch('getSystemFirwareVersion'))
+ .then(() => i18n.t('pageFirmware.toast.successUploadMessage'))
+ .catch(error => {
+ console.log(error);
+ throw new Error(i18n.t('pageFirmware.toast.errorUploadAndReboot'));
+ });
+ },
+ async switchFirmwareAndReboot({ state }) {
+ const backupLoaction = state.backupFirmware.location;
+ const data = {
+ Links: {
+ ActiveSoftwareImage: {
+ '@odata.id': backupLoaction
+ }
+ }
+ };
+ return await api
+ .patch('/redfish/v1/Managers/bmc', data)
+ .then(() => i18n.t('pageFirmware.toast.successRebootFromBackup'))
+ .catch(error => {
+ console.log(error);
+ throw new Error(i18n.t('pageFirmware.toast.errorRebootFromBackup'));
+ });
+ }
+ }
+};
+
+export default FirmwareSingleImageStore;
diff --git a/src/env/store/ibm.js b/src/env/store/ibm.js
index 70c9b98..12fd086 100644
--- a/src/env/store/ibm.js
+++ b/src/env/store/ibm.js
@@ -1,7 +1,7 @@
import store from '@/store';
+import FirmwareSingleImageStore from './FirmwareSingleImage/FirmwareSingleImageStore';
-// Use store.registerModule() to register env specific
-// store modules
-// https://vuex.vuejs.org/api/#registermodule
+store.unregisterModule('firmware');
+store.registerModule('firmwareSingleImage', FirmwareSingleImageStore);
export default store;
diff --git a/src/views/Overview/Overview.vue b/src/views/Overview/Overview.vue
index 7385b0d..1399829 100644
--- a/src/views/Overview/Overview.vue
+++ b/src/views/Overview/Overview.vue
@@ -106,7 +106,7 @@
mixins: [LoadingBarMixin],
computed: mapState({
server: state => state.system.systems[0],
- bmcFirmwareVersion: state => state.firmware.activeFirmware.version,
+ bmcFirmwareVersion: state => state.firmware?.activeFirmware.version,
powerCapValue: state => state.powerControl.powerCapValue,
powerConsumptionValue: state => state.powerControl.powerConsumptionValue,
serverManufacturer() {