1/** 2 * Controller for date-time 3 * 4 * @module app/configuration 5 * @exports dateTimeController 6 * @name dateTimeController 7 */ 8 9window.angular && (function(angular) { 10 'use strict'; 11 12 angular.module('app.configuration').controller('dateTimeController', [ 13 '$scope', '$window', 'APIUtils', '$route', '$q', 'toastService', 14 function($scope, $window, APIUtils, $route, $q, toastService) { 15 $scope.bmc = {}; 16 // Only used when the owner is "Split" 17 $scope.host = {}; 18 $scope.ntp = {servers: []}; 19 $scope.time = {mode: '', owner: ''}; 20 // Possible time owners 21 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/xyz/openbmc_project/Time/Owner.interface.yaml#L13 22 $scope.timeOwners = ['BMC', 'Host', 'Both', 'Split']; 23 $scope.loading = true; 24 var timePath = '/xyz/openbmc_project/time/'; 25 26 var getTimePromise = APIUtils.getTime().then( 27 function(data) { 28 // The time is returned as Epoch microseconds convert to 29 // milliseconds. 30 if (data.data[timePath + 'bmc'] && 31 data.data[timePath + 'bmc'].hasOwnProperty('Elapsed')) { 32 $scope.bmc.date = 33 new Date(data.data[timePath + 'bmc'].Elapsed / 1000); 34 // Don't care about milliseconds and don't want them displayed 35 $scope.bmc.date.setMilliseconds(0); 36 37 // Examples: 38 // Central Standard Time (UTC-06:00) 39 // Moscow Standard Time (UTC+03:00) 40 $scope.bmc.timezone = getUserTimezone($scope.bmc.date) + ' ' + 41 createOffset($scope.bmc.date); 42 } 43 if (data.data[timePath + 'host'] && 44 data.data[timePath + 'host'].hasOwnProperty('Elapsed')) { 45 $scope.host.date = 46 new Date(data.data[timePath + 'host'].Elapsed / 1000); 47 $scope.host.date.setMilliseconds(0); 48 $scope.host.timezone = getUserTimezone($scope.bmc.date) + ' ' + 49 createOffset($scope.bmc.date); 50 } 51 if (data.data[timePath + 'owner'] && 52 data.data[timePath + 'owner'].hasOwnProperty('TimeOwner')) { 53 $scope.time.owner = 54 data.data[timePath + 'owner'].TimeOwner.split('.').pop(); 55 } 56 if (data.data[timePath + 'sync_method'] && 57 data.data[timePath + 'sync_method'].hasOwnProperty( 58 'TimeSyncMethod')) { 59 $scope.time.mode = data.data[timePath + 'sync_method'] 60 .TimeSyncMethod.split('.') 61 .pop(); 62 } 63 }, 64 function(error) { 65 console.log(JSON.stringify(error)); 66 }); 67 68 var getNTPPromise = APIUtils.getNTPServers().then( 69 function(data) { 70 $scope.ntp.servers = data.data; 71 }, 72 function(error) { 73 console.log(JSON.stringify(error)); 74 }); 75 76 var promises = [ 77 getTimePromise, 78 getNTPPromise, 79 ]; 80 81 $q.all(promises).finally(function() { 82 $scope.loading = false; 83 }); 84 85 $scope.setTime = function() { 86 $scope.loading = true; 87 var promises = [setTimeMode(), setTimeOwner(), setNTPServers()]; 88 89 $q.all(promises).then( 90 function() { 91 // Have to set the time mode and time owner first to avoid a 92 // insufficient permissions if the time mode or time owner had 93 // changed. 94 var manual_promises = []; 95 if ($scope.time.mode == 'Manual') { 96 // If owner is 'Split' set both. 97 // If owner is 'Host' set only it. 98 // Else set BMC only. See: 99 // https://github.com/openbmc/phosphor-time-manager/blob/master/README.md 100 if ($scope.time.owner != 'Host') { 101 manual_promises.push( 102 setBMCTime($scope.bmc.date.getTime() * 1000)); 103 } 104 // Even though we are setting Host time, we are setting from 105 // the BMC date and time fields labeled "BMC and Host Time" 106 // currently. 107 if ($scope.time.owner == 'Host') { 108 manual_promises.push( 109 setHostTime($scope.bmc.date.getTime() * 1000)); 110 } 111 } 112 // Set the Host if Split even if NTP. In split mode, the host has 113 // its own date and time field. Set from it. 114 if ($scope.time.owner == 'Split') { 115 manual_promises.push( 116 setHostTime($scope.host.date.getTime() * 1000)); 117 } 118 119 $q.all(manual_promises) 120 .then( 121 function() { 122 toastService.success('Date and time settings saved'); 123 }, 124 function(errors) { 125 console.log(JSON.stringify(errors)); 126 toastService.error( 127 'Date and time settings could not be saved'); 128 }) 129 .finally(function() { 130 $scope.loading = false; 131 }); 132 }, 133 function(errors) { 134 console.log(JSON.stringify(errors)); 135 toastService.error('Date and time settings could not be saved'); 136 $scope.loading = false; 137 }); 138 }; 139 $scope.refresh = function() { 140 $route.reload(); 141 }; 142 143 $scope.addNTPField = function() { 144 $scope.ntp.servers.push(''); 145 }; 146 147 $scope.removeNTPField = function(index) { 148 $scope.ntp.servers.splice(index, 1); 149 }; 150 151 function setNTPServers() { 152 // Remove any empty strings from the array. Important because we add an 153 // empty string to the end so the user can add a new NTP server, if the 154 // user doesn't fill out the field, we don't want to add. 155 $scope.ntp.servers = $scope.ntp.servers.filter(Boolean); 156 // NTP servers does not allow an empty array, since we remove all empty 157 // strings above, could have an empty array. TODO: openbmc/openbmc#3240 158 if ($scope.ntp.servers.length == 0) { 159 $scope.ntp.servers.push(''); 160 } 161 return APIUtils.setNTPServers($scope.ntp.servers); 162 } 163 164 function setTimeMode() { 165 return APIUtils.setTimeMode( 166 'xyz.openbmc_project.Time.Synchronization.Method.' + 167 $scope.time.mode); 168 } 169 170 function setTimeOwner() { 171 return APIUtils.setTimeOwner( 172 'xyz.openbmc_project.Time.Owner.Owners.' + $scope.time.owner); 173 } 174 175 function setBMCTime(time) { 176 // Add the separate date and time objects and convert to Epoch time in 177 // microseconds. 178 return APIUtils.setBMCTime(time); 179 } 180 181 function setHostTime(time) { 182 // Add the separate date and time objects and convert to Epoch time 183 // microseconds. 184 return APIUtils.setHostTime(time); 185 } 186 function createOffset(date) { 187 // https://stackoverflow.com/questions/9149556/how-to-get-utc-offset-in-javascript-analog-of-timezoneinfo-getutcoffset-in-c 188 var sign = (date.getTimezoneOffset() > 0) ? '-' : '+'; 189 var offset = Math.abs(date.getTimezoneOffset()); 190 var hours = pad(Math.floor(offset / 60)); 191 var minutes = pad(offset % 60); 192 return '(UTC' + sign + hours + ':' + minutes + ')'; 193 } 194 function getUserTimezone(date) { 195 const ro = Intl.DateTimeFormat().resolvedOptions(); 196 // A safe, easy way to get the timezone (e.g. Central Standard Time) is 197 // to subtract the time string without a timezone from the time string 198 // with a timezone. 199 // Hardcoded to 'en-US' so all timezones are displayed in English 200 // (e.g. Moscow Standard Time). 201 var ret = date.toLocaleTimeString('en-US', {timeZoneName: 'long'}) 202 .replace(date.toLocaleTimeString('en-US'), '') 203 .trim(); 204 // Do not return GMT+/-offset. 205 if (ret.indexOf('GMT') >= 0) { 206 return ''; 207 } 208 return ret; 209 } 210 function pad(value) { 211 return value < 10 ? '0' + value : value; 212 } 213 } 214 ]); 215})(angular); 216