/** * Controller for date-time * * @module app/configuration * @exports dateTimeController * @name dateTimeController */ window.angular && (function(angular) { 'use strict'; angular.module('app.configuration').controller('dateTimeController', [ '$scope', '$window', 'APIUtils', '$route', '$q', function($scope, $window, APIUtils, $route, $q) { $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.error = false; $scope.success = false; $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; }); $scope.setTime = function() { $scope.error = false; $scope.success = false; $scope.loading = true; var promises = [setTimeMode(), setTimeOwner(), setNTPServers()]; $q.all(promises).then( function() { // Have to set the time mode and time owner first to avoid a // insufficient permissions if the time mode or time owner had // changed. var manual_promises = []; if ($scope.time.mode == 'Manual') { // If owner is 'Split' set both. // If owner is 'Host' set only it. // Else set BMC only. See: // https://github.com/openbmc/phosphor-time-manager/blob/master/README.md if ($scope.time.owner != 'Host') { manual_promises.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') { manual_promises.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') { manual_promises.push( setHostTime($scope.host.date.getTime() * 1000)); } $q.all(manual_promises) .then( function() { $scope.success = true; }, function(errors) { console.log(JSON.stringify(errors)); $scope.error = true; }) .finally(function() { $scope.loading = false; }); }, function(errors) { console.log(JSON.stringify(errors)); $scope.error = true; $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. Important because we add an // empty string to the end so the user can add a new NTP server, if the // user doesn't fill out the field, we don't want to add. $scope.ntp.servers = $scope.ntp.servers.filter(Boolean); // NTP servers does not allow an empty array, since we remove all empty // strings above, could have an empty array. TODO: openbmc/openbmc#3240 if ($scope.ntp.servers.length == 0) { $scope.ntp.servers.push(''); } 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);