diff options
author | Dixsie Wolmers <dixsie@ibm.com> | 2020-06-05 15:00:06 +0300 |
---|---|---|
committer | Derick Montague <derick.montague@ibm.com> | 2020-07-10 04:13:23 +0300 |
commit | 739e459610d92a6fd3021a65398ecb4517cced89 (patch) | |
tree | 247ad85d6e7924f838a1c59efc20b65b6e4812ad /src | |
parent | 2f8bbbfe198087497c475029c573727e0711eb04 (diff) | |
download | webui-vue-739e459610d92a6fd3021a65398ecb4517cced89.tar.xz |
Add date and time settings
Adds ablity to change date and time manually, or
configure using NTP servers.
- If NTP is selected, user is required to enter at least one
NTP address
- Date and time are ISO formatted
Signed-off-by: Dixsie Wolmers <dixsie@ibm.com>
Change-Id: I0d67c80487fdd815eacc3539ccd702b23618260e
Diffstat (limited to 'src')
-rw-r--r-- | src/components/AppNavigation/AppNavigation.vue | 3 | ||||
-rw-r--r-- | src/locales/en-US.json | 75 | ||||
-rw-r--r-- | src/router/index.js | 8 | ||||
-rw-r--r-- | src/store/index.js | 2 | ||||
-rw-r--r-- | src/store/modules/Configuration/DateTimeSettingsStore.js | 68 | ||||
-rw-r--r-- | src/views/Configuration/DateTimeSettings/DateTimeSettings.vue | 352 | ||||
-rw-r--r-- | src/views/Configuration/DateTimeSettings/index.js | 2 |
7 files changed, 484 insertions, 26 deletions
diff --git a/src/components/AppNavigation/AppNavigation.vue b/src/components/AppNavigation/AppNavigation.vue index 1dfba11e..175b0aa0 100644 --- a/src/components/AppNavigation/AppNavigation.vue +++ b/src/components/AppNavigation/AppNavigation.vue @@ -59,6 +59,9 @@ <icon-expand class="icon-expand" /> </b-button> <b-collapse id="configuration-menu" tag="ul" class="nav-item__nav"> + <b-nav-item to="/configuration/date-time-settings"> + {{ $t('appNavigation.dateTimeSettings') }} + </b-nav-item> <b-nav-item href="javascript:void(0)"> {{ $t('appNavigation.firmware') }} </b-nav-item> diff --git a/src/locales/en-US.json b/src/locales/en-US.json index f0494c76..2a418040 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -25,7 +25,7 @@ }, "calendar": { "openDatePicker": "Open date picker", - "useCursorKeysToNavigateCalendarDates" : "Use cursor keys to navigate calendar dates" + "useCursorKeysToNavigateCalendarDates": "Use cursor keys to navigate calendar dates" }, "form": { "dateMustBeAfter": "Date must be after %{date}", @@ -73,6 +73,7 @@ "accessControl": "Access Control", "configuration": "Configuration", "control": "Control", + "dateTimeSettings": "@:appPageTitle.dateTimeSettings", "eventLogs": "@:appPageTitle.eventLogs", "firmware": "@:appPageTitle.firmware", "hardwareStatus": "@:appPageTitle.hardwareStatus", @@ -92,6 +93,7 @@ "sslCertificates": "@:appPageTitle.sslCertificates" }, "appPageTitle": { + "dateTimeSettings": "Date and time settings", "eventLogs": "Event logs", "firmware": "Firmware", "hardwareStatus": "Hardware status", @@ -101,7 +103,7 @@ "managePowerUsage": "Manage power usage", "networkSettings": "Network settings", "overview": "Overview", - "profileSettings":"Profile settings", + "profileSettings": "Profile settings", "rebootBmc": "Reboot BMC", "sensors": "Sensors", "serialOverLan": "Serial over LAN console", @@ -111,6 +113,27 @@ "sslCertificates": "SSL Certificates", "unauthorized": "Unauthorized" }, + "pageDateTimeSettings": { + "alert": { + "message": "To change how date and time are displayed (either UTC or browser offset) throughout the application, visit ", + "link": "Profile Settings" + }, + "configureSettings": "Configure settings", + "form": { + "date": "Date", + "manual": "Manual", + "time": "Time", + "ntpServers": { + "server1": "Server 1", + "server2": "Server 2", + "server3": "Server 3" + } + }, + "toast": { + "errorSaveDateTimeSettings": "Error saving date and time settings.", + "successSaveDateTimeSettings": "Successfully saved date and time settings." + } + }, "pageEventLogs": { "modal": { "deleteTitle": "Delete log | Delete logs", @@ -138,30 +161,30 @@ "chassis": "Chassis", "system": "System", "table": { - "assetTag": "Asset tag", - "chassisType": "Chassis type", - "connectTypesSupported": "Connect types supported", - "description": "Description", - "efficiencyPercent": "Efficiency percent", - "firmwareVersion": "Firmware version", - "graphicalConsole": "Graphical console", - "health": "Health", - "id": "ID", - "indicatorLed": "Indicator LED", - "manufacturer": "Manufacturer", - "maxConcurrentSessions": "Max concurrent sessions", - "model": "Model", - "partNumber": "Part number", - "powerInputWatts": "Power input watts", - "powerState": "Power state", - "serialConsole": "Serial console", - "serialNumber": "Serial number", - "serviceEnabled": "Service enabled", - "serviceEntryPointUuid": "Service entry point UUID", - "statusHealthRollup": "Status (Health rollup)", - "statusState": "Status (State)", - "systemType": "System type", - "uuid": "UUID" + "assetTag": "Asset tag", + "chassisType": "Chassis type", + "connectTypesSupported": "Connect types supported", + "description": "Description", + "efficiencyPercent": "Efficiency percent", + "firmwareVersion": "Firmware version", + "graphicalConsole": "Graphical console", + "health": "Health", + "id": "ID", + "indicatorLed": "Indicator LED", + "manufacturer": "Manufacturer", + "maxConcurrentSessions": "Max concurrent sessions", + "model": "Model", + "partNumber": "Part number", + "powerInputWatts": "Power input watts", + "powerState": "Power state", + "serialConsole": "Serial console", + "serialNumber": "Serial number", + "serviceEnabled": "Service enabled", + "serviceEntryPointUuid": "Service entry point UUID", + "statusHealthRollup": "Status (Health rollup)", + "statusState": "Status (State)", + "systemType": "System type", + "uuid": "UUID" } }, "pageLdap": { diff --git a/src/router/index.js b/src/router/index.js index a3d28063..3d8c646a 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -82,6 +82,14 @@ const routes = [ } }, { + path: '/configuration/date-time-settings', + name: 'date-time-settings', + component: () => import('@/views/Configuration/DateTimeSettings'), + meta: { + title: 'appPageTitle.dateTimeSettings' + } + }, + { path: '/control/manage-power-usage', name: 'manage-power-usage', component: () => import('@/views/Control/ManagePowerUsage'), diff --git a/src/store/index.js b/src/store/index.js index 6ad05390..392344d0 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -22,6 +22,7 @@ import ChassisStore from './modules/Health/ChassisStore'; import BmcStore from './modules/Health/BmcStore'; import WebSocketPlugin from './plugins/WebSocketPlugin'; +import DateTimeStore from './modules/Configuration/DateTimeSettingsStore'; Vue.use(Vuex); @@ -32,6 +33,7 @@ export default new Vuex.Store({ modules: { global: GlobalStore, authentication: AuthenticationStore, + dateTime: DateTimeStore, ldap: LdapStore, localUsers: LocalUserManagementStore, firmware: FirmwareStore, diff --git a/src/store/modules/Configuration/DateTimeSettingsStore.js b/src/store/modules/Configuration/DateTimeSettingsStore.js new file mode 100644 index 00000000..9da0cb41 --- /dev/null +++ b/src/store/modules/Configuration/DateTimeSettingsStore.js @@ -0,0 +1,68 @@ +import api from '../../api'; +import i18n from '@/i18n'; + +const DateTimeStore = { + namespaced: true, + state: { + ntpServers: [], + isNtpProtocolEnabled: null + }, + getters: { + ntpServers: state => state.ntpServers, + isNtpProtocolEnabled: state => state.isNtpProtocolEnabled + }, + mutations: { + setNtpServers: (state, ntpServers) => (state.ntpServers = ntpServers), + setIsNtpProtocolEnabled: (state, isNtpProtocolEnabled) => + (state.isNtpProtocolEnabled = isNtpProtocolEnabled) + }, + actions: { + async getNtpData({ commit }) { + return await api + .get('/redfish/v1/Managers/bmc/NetworkProtocol') + .then(response => { + const ntpServers = response.data.NTP.NTPServers; + const isNtpProtocolEnabled = response.data.NTP.ProtocolEnabled; + commit('setNtpServers', ntpServers); + commit('setIsNtpProtocolEnabled', isNtpProtocolEnabled); + }) + .catch(error => { + console.log(error); + }); + }, + async updateDateTimeSettings(_, dateTimeForm) { + const ntpData = { + NTP: { + ProtocolEnabled: dateTimeForm.ntpProtocolEnabled + } + }; + + if (dateTimeForm.ntpProtocolEnabled) { + ntpData.NTP.NTPServers = dateTimeForm.ntpServersArray; + } + return await api + .patch(`/redfish/v1/Managers/bmc/NetworkProtocol`, ntpData) + .then(() => { + if (!dateTimeForm.ntpProtocolEnabled) { + const dateTimeData = { + DateTime: dateTimeForm.updatedDateTime + }; + api.patch(`/redfish/v1/Managers/bmc`, dateTimeData); + } + }) + .then(() => { + return i18n.t( + 'pageDateTimeSettings.toast.successSaveDateTimeSettings' + ); + }) + .catch(error => { + console.log(error); + throw new Error( + i18n.t('pageDateTimeSettings.toast.errorSaveDateTimeSettings') + ); + }); + } + } +}; + +export default DateTimeStore; diff --git a/src/views/Configuration/DateTimeSettings/DateTimeSettings.vue b/src/views/Configuration/DateTimeSettings/DateTimeSettings.vue new file mode 100644 index 00000000..d7b97d1a --- /dev/null +++ b/src/views/Configuration/DateTimeSettings/DateTimeSettings.vue @@ -0,0 +1,352 @@ +<template> + <b-container fluid="xl"> + <page-title /> + <b-row> + <b-col md="8" xl="6"> + <alert variant="info" class="mb-4"> + <span> + {{ $t('pageDateTimeSettings.alert.message') }} + <b-link to="/profile-settings"> + {{ $t('pageDateTimeSettings.alert.link') }}</b-link + > + </span> + </alert> + </b-col> + </b-row> + <page-section> + <b-row> + <b-col lg="3"> + <dl> + <dt>{{ $t('pageDateTimeSettings.form.date') }}</dt> + <dd v-if="bmcTime">{{ bmcTime | formatDate }}</dd> + <dd v-else>--</dd> + </dl> + </b-col> + <b-col lg="3"> + <dl> + <dt>{{ $t('pageDateTimeSettings.form.time') }}</dt> + <dd v-if="bmcTime">{{ bmcTime | formatTime }}</dd> + <dd v-else>--</dd> + </dl> + </b-col> + </b-row> + </page-section> + <page-section :section-title="$t('pageDateTimeSettings.configureSettings')"> + <b-form novalidate @submit.prevent="submitForm"> + <b-form-group label="Configure date and time" label-sr-only> + <b-form-radio + v-model="form.configurationSelected" + value="manual" + @change="onChangeConfigType" + > + {{ $t('pageDateTimeSettings.form.manual') }} + </b-form-radio> + <b-row class="mt-3 ml-3"> + <b-col sm="6" lg="4" xl="3"> + <b-form-group + :label="$t('pageDateTimeSettings.form.date')" + label-for="input-manual-date" + > + <b-form-text id="date-format-help">(YYYY-MM-DD)</b-form-text> + <b-input-group> + <b-form-input + id="input-manual-date" + v-model="form.manual.date" + :state="getValidationState($v.form.manual.date)" + :disabled="form.configurationSelected === 'ntp'" + @blur="$v.form.manual.date.$touch()" + /> + <b-form-invalid-feedback role="alert"> + <div v-if="!$v.form.manual.date.pattern"> + {{ $t('global.form.invalidFormat') }} + </div> + <div v-if="!$v.form.manual.time.required"> + {{ $t('global.form.fieldRequired') }} + </div> + </b-form-invalid-feedback> + <b-form-datepicker + v-model="form.manual.date" + button-only + right + size="sm" + :hide-header="true" + :locale="locale" + :label-help=" + $t('global.calendar.useCursorKeysToNavigateCalendarDates') + " + :disabled="form.configurationSelected === 'ntp'" + button-variant="link" + aria-controls="input-manual-date" + > + <template v-slot:button-content> + <icon-calendar /> + <span class="sr-only"> + {{ $t('global.calendar.openDatePicker') }} + </span> + </template> + </b-form-datepicker> + </b-input-group> + </b-form-group> + </b-col> + <b-col sm="6" lg="4" xl="3"> + <b-form-group + :label="$t('pageDateTimeSettings.form.time')" + label-for="input-manual-time" + > + <b-form-text id="time-format-help">(HH:MM)</b-form-text> + <b-input-group> + <b-form-input + id="input-manual-time" + v-model="form.manual.time" + :state="getValidationState($v.form.manual.time)" + :disabled="form.configurationSelected === 'ntp'" + @blur="$v.form.manual.time.$touch()" + /> + <b-form-invalid-feedback role="alert"> + <div v-if="!$v.form.manual.time.pattern"> + {{ $t('global.form.invalidFormat') }} + </div> + <div v-if="!$v.form.manual.time.required"> + {{ $t('global.form.fieldRequired') }} + </div> + </b-form-invalid-feedback> + </b-input-group> + </b-form-group> + </b-col> + </b-row> + <b-form-radio + v-model="form.configurationSelected" + value="ntp" + @change="onChangeConfigType" + > + NTP + </b-form-radio> + <b-row class="mt-3 ml-3"> + <b-col sm="6" lg="4" xl="3"> + <b-form-group + :label="$t('pageDateTimeSettings.form.ntpServers.server1')" + label-for="input-ntp-1" + > + <b-input-group> + <b-form-input + id="input-ntp-1" + v-model="form.ntp.firstAddress" + :state="getValidationState($v.form.ntp.firstAddress)" + :disabled="form.configurationSelected === 'manual'" + @blur="$v.form.ntp.firstAddress.$touch()" + /> + <b-form-invalid-feedback role="alert"> + <div v-if="!$v.form.ntp.firstAddress.required"> + {{ $t('global.form.fieldRequired') }} + </div> + </b-form-invalid-feedback> + </b-input-group> + </b-form-group> + </b-col> + <b-col sm="6" lg="4" xl="3"> + <b-form-group + :label="$t('pageDateTimeSettings.form.ntpServers.server2')" + label-for="input-ntp-2" + > + <b-input-group> + <b-form-input + id="input-ntp-2" + v-model="form.ntp.secondAddress" + :disabled="form.configurationSelected === 'manual'" + @blur="$v.form.ntp.secondAddress.$touch()" + /> + </b-input-group> + </b-form-group> + </b-col> + <b-col sm="6" lg="4" xl="3"> + <b-form-group + :label="$t('pageDateTimeSettings.form.ntpServers.server3')" + label-for="input-ntp-3" + > + <b-input-group> + <b-form-input + id="input-ntp-3" + v-model="form.ntp.thirdAddress" + :disabled="form.configurationSelected === 'manual'" + @blur="$v.form.ntp.thirdAddress.$touch()" + /> + </b-input-group> + </b-form-group> + </b-col> + </b-row> + </b-form-group> + <b-button variant="primary" type="submit"> + {{ $t('global.action.saveSettings') }} + </b-button> + </b-form> + </page-section> + </b-container> +</template> + +<script> +import Alert from '@/components/Global/Alert'; +import IconCalendar from '@carbon/icons-vue/es/calendar/20'; +import PageTitle from '@/components/Global/PageTitle'; +import PageSection from '@/components/Global/PageSection'; + +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; +import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; + +import { mapState } from 'vuex'; +import { requiredIf, helpers } from 'vuelidate/lib/validators'; + +const isoDateRegex = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/; +const isoTimeRegex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/; + +export default { + name: 'DateTimeSettings', + components: { Alert, IconCalendar, PageTitle, PageSection }, + mixins: [BVToastMixin, LoadingBarMixin, VuelidateMixin], + data() { + return { + locale: this.$store.getters['global/languagePreference'], + form: { + configurationSelected: '', + manual: { + date: '', + time: '' + }, + ntp: { firstAddress: '', secondAddress: '', thirdAddress: '' } + } + }; + }, + validations() { + return { + form: { + manual: { + date: { + required: requiredIf(function() { + return this.form.configurationSelected === 'manual'; + }), + pattern: helpers.regex('pattern', isoDateRegex) + }, + time: { + required: requiredIf(function() { + return this.form.configurationSelected === 'manual'; + }), + pattern: helpers.regex('pattern', isoTimeRegex) + } + }, + ntp: { + firstAddress: { + required: requiredIf(function() { + return this.form.configurationSelected === 'ntp'; + }) + }, + secondAddress: {}, + thirdAddress: {} + } + } + }; + }, + computed: { + ...mapState('dateTime', ['ntpServers', 'isNtpProtocolEnabled']), + bmcTime() { + return this.$store.getters['global/bmcTime']; + } + }, + watch: { + ntpServers() { + this.setNtpValues(); + }, + manualDate() { + this.emitChange(); + } + }, + created() { + this.startLoader(); + Promise.all([ + this.$store.dispatch('global/getBmcTime'), + this.$store.dispatch('dateTime/getNtpData') + ]).finally(() => this.endLoader()); + }, + beforeRouteLeave(to, from, next) { + this.hideLoader(); + next(); + }, + methods: { + emitChange() { + if (this.$v.$invalid) return; + this.$v.$reset(); //reset to re-validate on blur + this.$emit('change', { + manualDate: this.manualDate ? new Date(this.manualDate) : null + }); + }, + setNtpValues() { + this.form.configurationSelected = this.isNtpProtocolEnabled + ? 'ntp' + : 'manual'; + this.form.ntp.firstAddress = this.ntpServers[0] || ''; + this.form.ntp.secondAddress = this.ntpServers[1] || ''; + this.form.ntp.thirdAddress = this.ntpServers[2] || ''; + }, + onChangeConfigType() { + this.$v.form.$reset(); + this.setNtpValues(); + }, + submitForm() { + this.$v.$touch(); + if (this.$v.$invalid) return; + this.startLoader(); + + let dateTimeForm = {}; + let ntpFirstAddress; + let ntpSecondAddress; + let ntpThirdAddress; + let isNTPEnabled = this.form.configurationSelected === 'ntp'; + + if (!isNTPEnabled) { + dateTimeForm.ntpProtocolEnabled = false; + dateTimeForm.updatedDateTime = new Date( + `${this.form.manual.date} ${this.form.manual.time}` + ).toISOString(); + } else { + ntpFirstAddress = this.form.ntp.firstAddress; + ntpSecondAddress = this.form.ntp.secondAddress; + ntpThirdAddress = this.form.ntp.thirdAddress; + dateTimeForm.ntpProtocolEnabled = true; + dateTimeForm.ntpServersArray = [ + ntpFirstAddress, + ntpSecondAddress, + ntpThirdAddress + ]; + } + + this.$store + .dispatch('dateTime/updateDateTimeSettings', dateTimeForm) + .then(success => { + this.successToast(success); + if (!isNTPEnabled) return; + // Shift address up if second address is empty + // to avoid refreshing after delay when updating NTP + if (ntpSecondAddress === '' && ntpThirdAddress !== '') { + this.form.ntp.secondAddress = ntpThirdAddress; + this.form.ntp.thirdAddress = ''; + } + }) + .catch(({ message }) => this.errorToast(message)) + .finally(() => { + this.$v.form.$reset(); + this.endLoader(); + }); + } + } +}; +</script> + +<style lang="scss" scoped> +@import 'src/assets/styles/helpers'; + +.b-form-datepicker { + position: absolute; + right: 0; + top: 0; + z-index: $zindex-dropdown + 1; +} +</style> diff --git a/src/views/Configuration/DateTimeSettings/index.js b/src/views/Configuration/DateTimeSettings/index.js new file mode 100644 index 00000000..c8b5c08f --- /dev/null +++ b/src/views/Configuration/DateTimeSettings/index.js @@ -0,0 +1,2 @@ +import DateTimeSettings from './DateTimeSettings.vue'; +export default DateTimeSettings; |