/** * Controller for date-time * * @module app/configuration * @exports dateTimeController * @name dateTimeController */ window.angular && (function(angular) { 'use strict'; angular.module('app.configuration').controller('dateTimeController', [ '$scope', 'APIUtils', '$route', '$q', 'toastService', '$timeout', function($scope, APIUtils, $route, $q, toastService, $timeout) { $scope.bmc = {}; // Only used when the owner is "Split" $scope.host = {}; $scope.ntp = {servers: []}; $scope.time = {mode: '', owner: ''}; // Possible time owners // https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/xyz/openbmc_project/Time/Owner.interface.yaml#L13 $scope.timeOwners = ['BMC', 'Host', 'Both', 'Split']; $scope.loading = true; var timePath = '/xyz/openbmc_project/time/'; var getTimePromise = APIUtils.getTime().then( function(data) { // The time is returned as Epoch microseconds convert to // milliseconds. if (data.data[timePath + 'bmc'] && data.data[timePath + 'bmc'].hasOwnProperty('Elapsed')) { $scope.bmc.date = new Date(data.data[timePath + 'bmc'].Elapsed / 1000); // Don't care about milliseconds and don't want them displayed $scope.bmc.date.setMilliseconds(0); // Examples: // Central Standard Time (UTC-06:00) // Moscow Standard Time (UTC+03:00) $scope.bmc.timezone = getUserTimezone($scope.bmc.date) + ' ' + createOffset($scope.bmc.date); } if (data.data[timePath + 'host'] && data.data[timePath + 'host'].hasOwnProperty('Elapsed')) { $scope.host.date = new Date(data.data[timePath + 'host'].Elapsed / 1000); $scope.host.date.setMilliseconds(0); $scope.host.timezone = getUserTimezone($scope.bmc.date) + ' ' + createOffset($scope.bmc.date); } if (data.data[timePath + 'owner'] && data.data[timePath + 'owner'].hasOwnProperty('TimeOwner')) { $scope.time.owner = data.data[timePath + 'owner'].TimeOwner.split('.').pop(); } if (data.data[timePath + 'sync_method'] && data.data[timePath + 'sync_method'].hasOwnProperty( 'TimeSyncMethod')) { $scope.time.mode = data.data[timePath + 'sync_method'] .TimeSyncMethod.split('.') .pop(); } }, function(error) { console.log(JSON.stringify(error)); }); var getNTPPromise = APIUtils.getNTPServers().then( function(data) { $scope.ntp.servers = data.data; }, function(error) { console.log(JSON.stringify(error)); }); var promises = [getTimePromise, getNTPPromise]; $q.all(promises).finally(function() { $scope.loading = false; }); /** * https://github.com/openbmc/phosphor-time-manager/blob/master/README.md#special-note-on-changing-ntp-setting * When time mode is initially set to Manual from NTP, * NTP service is disabled and the NTP service is * stopping but not stopped, setting time will return an error. * There are no responses from backend to notify when NTP is stopped. * To work around, a timeout is set to allow NTP to fully stop * TODO: remove timeout if backend solves * https://github.com/openbmc/openbmc/issues/3459 */ $scope.saveDateTimeSettings = function() { $scope.loading = true; if ($scope.time.mode == 'Manual' || $scope.time.owner == 'Split') { setTimeMode() .then(setTimeOwner) .then(setNTPServers) .then($timeout(setDateTime, 20000)); } else { setTimeMode() .then(setTimeOwner) .then(setNTPServers) .then( function() { toastService.success('Date and time settings saved'); }, function(errors) { console.log(JSON.stringify(errors)); toastService.error( 'Date and time settings could not be saved'); }) .finally(function() { $scope.loading = false; }); } }; const setDateTime = function() { var manualPromises = []; if ($scope.time.mode == 'Manual') { // If owner is 'Split' set both. // If owner is 'Host' set only it. if ($scope.time.owner != 'Host') { manualPromises.push(setBMCTime($scope.bmc.date.getTime() * 1000)); } // Even though we are setting Host time, we are setting from // the BMC date and time fields labeled "BMC and Host Time" // currently. if ($scope.time.owner == 'Host') { manualPromises.push(setHostTime($scope.bmc.date.getTime() * 1000)); } } // Set the Host if Split even if NTP. In split mode, the host has // its own date and time field set from it. if ($scope.time.owner == 'Split') { manualPromises.push(setHostTime($scope.host.date.getTime() * 1000)); } $q.all(manualPromises) .then( function() { toastService.success('Date and time settings saved'); }, function(errors) { console.log(JSON.stringify(errors)); toastService.error( 'Date and time settings could not be saved'); }) .finally(function() { $scope.loading = false; }); }; $scope.refresh = function() { $route.reload(); }; $scope.addNTPField = function() { $scope.ntp.servers.push(''); }; $scope.removeNTPField = function(index) { $scope.ntp.servers.splice(index, 1); }; function setNTPServers() { // Remove any empty strings from the array. If the // user doesn't fill out the field, we don't want to add. $scope.ntp.servers = $scope.ntp.servers.filter(Boolean); return APIUtils.setNTPServers($scope.ntp.servers); } function setTimeMode() { return APIUtils.setTimeMode( 'xyz.openbmc_project.Time.Synchronization.Method.' + $scope.time.mode); } function setTimeOwner() { return APIUtils.setTimeOwner( 'xyz.openbmc_project.Time.Owner.Owners.' + $scope.time.owner); } function setBMCTime(time) { // Add the separate date and time objects and convert to Epoch time in // microseconds. return APIUtils.setBMCTime(time); } function setHostTime(time) { // Add the separate date and time objects and convert to Epoch time // microseconds. return APIUtils.setHostTime(time); } function createOffset(date) { // https://stackoverflow.com/questions/9149556/how-to-get-utc-offset-in-javascript-analog-of-timezoneinfo-getutcoffset-in-c var sign = (date.getTimezoneOffset() > 0) ? '-' : '+'; var offset = Math.abs(date.getTimezoneOffset()); var hours = pad(Math.floor(offset / 60)); var minutes = pad(offset % 60); return '(UTC' + sign + hours + ':' + minutes + ')'; } function getUserTimezone(date) { const ro = Intl.DateTimeFormat().resolvedOptions(); // A safe, easy way to get the timezone (e.g. Central Standard Time) is // to subtract the time string without a timezone from the time string // with a timezone. // Hardcoded to 'en-US' so all timezones are displayed in English // (e.g. Moscow Standard Time). var ret = date.toLocaleTimeString('en-US', {timeZoneName: 'long'}) .replace(date.toLocaleTimeString('en-US'), '') .trim(); // Do not return GMT+/-offset. if (ret.indexOf('GMT') >= 0) { return ''; } return ret; } function pad(value) { return value < 10 ? '0' + value : value; } } ]); })(angular);