blob: 03f5af4c622a05a9d5ec7e60cfa42f079c6824e2 [file] [log] [blame]
<template>
<b-container fluid="xl">
<page-title :description="$t('pageFirmware.pageDescriptionSingleImage')" />
<!-- 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 #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 #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 #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.updateProcessInfoSingleImage') }}</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/getFirmwareInformation'),
]).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>