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