xref: /openbmc/phosphor-webui/app/configuration/controllers/date-time-controller.js (revision 254b16ee2e354b1fe7a27c0efe38f568bd159c29)
1cd789508SIftekharul Islam/**
2cd789508SIftekharul Islam * Controller for date-time
3cd789508SIftekharul Islam *
4cd789508SIftekharul Islam * @module app/configuration
5cd789508SIftekharul Islam * @exports dateTimeController
6cd789508SIftekharul Islam * @name dateTimeController
7cd789508SIftekharul Islam */
8cd789508SIftekharul Islam
9cd789508SIftekharul Islamwindow.angular && (function(angular) {
10cd789508SIftekharul Islam  'use strict';
11cd789508SIftekharul Islam
12d27bb135SAndrew Geissler  angular.module('app.configuration').controller('dateTimeController', [
13fdcb35eeSDixsie Wolmers    '$scope', 'APIUtils', '$route', '$q', 'toastService', '$timeout',
14fdcb35eeSDixsie Wolmers    function($scope, APIUtils, $route, $q, toastService, $timeout) {
15b7ea2790SGunnar Mills      $scope.bmc = {};
16eab3213cSGunnar Mills      // Only used when the owner is "Split"
17b7ea2790SGunnar Mills      $scope.host = {};
18b7ea2790SGunnar Mills      $scope.ntp = {servers: []};
192f955bd8SGunnar Mills      $scope.time = {mode: '', owner: ''};
202f955bd8SGunnar Mills      // Possible time owners
212f955bd8SGunnar Mills      // https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/xyz/openbmc_project/Time/Owner.interface.yaml#L13
222f955bd8SGunnar Mills      $scope.timeOwners = ['BMC', 'Host', 'Both', 'Split'];
237de38662SGunnar Mills      $scope.loading = true;
242f955bd8SGunnar Mills      var timePath = '/xyz/openbmc_project/time/';
257de38662SGunnar Mills
26c74d434cSGunnar Mills      var getTimePromise = APIUtils.getTime().then(
277de38662SGunnar Mills          function(data) {
28b7ea2790SGunnar Mills            // The time is returned as Epoch microseconds convert to
29b7ea2790SGunnar Mills            // milliseconds.
302f955bd8SGunnar Mills            if (data.data[timePath + 'bmc'] &&
312f955bd8SGunnar Mills                data.data[timePath + 'bmc'].hasOwnProperty('Elapsed')) {
32b7ea2790SGunnar Mills              $scope.bmc.date =
332f955bd8SGunnar Mills                  new Date(data.data[timePath + 'bmc'].Elapsed / 1000);
34b7ea2790SGunnar Mills              // Don't care about milliseconds and don't want them displayed
35b7ea2790SGunnar Mills              $scope.bmc.date.setMilliseconds(0);
36dbf04811SAlexander Filippov
37dbf04811SAlexander Filippov              // Examples:
38dbf04811SAlexander Filippov              //   Central Standard Time (UTC-06:00)
39dbf04811SAlexander Filippov              //   Moscow Standard Time (UTC+03:00)
40dbf04811SAlexander Filippov              $scope.bmc.timezone = getUserTimezone($scope.bmc.date) + ' ' +
41dbf04811SAlexander Filippov                  createOffset($scope.bmc.date);
42b7ea2790SGunnar Mills            }
432f955bd8SGunnar Mills            if (data.data[timePath + 'host'] &&
442f955bd8SGunnar Mills                data.data[timePath + 'host'].hasOwnProperty('Elapsed')) {
45b7ea2790SGunnar Mills              $scope.host.date =
462f955bd8SGunnar Mills                  new Date(data.data[timePath + 'host'].Elapsed / 1000);
47b7ea2790SGunnar Mills              $scope.host.date.setMilliseconds(0);
48dbf04811SAlexander Filippov              $scope.host.timezone = getUserTimezone($scope.bmc.date) + ' ' +
49dbf04811SAlexander Filippov                  createOffset($scope.bmc.date);
50b7ea2790SGunnar Mills            }
512f955bd8SGunnar Mills            if (data.data[timePath + 'owner'] &&
522f955bd8SGunnar Mills                data.data[timePath + 'owner'].hasOwnProperty('TimeOwner')) {
532f955bd8SGunnar Mills              $scope.time.owner =
542f955bd8SGunnar Mills                  data.data[timePath + 'owner'].TimeOwner.split('.').pop();
55b7ea2790SGunnar Mills            }
562f955bd8SGunnar Mills            if (data.data[timePath + 'sync_method'] &&
572f955bd8SGunnar Mills                data.data[timePath + 'sync_method'].hasOwnProperty(
58b7ea2790SGunnar Mills                    'TimeSyncMethod')) {
592f955bd8SGunnar Mills              $scope.time.mode = data.data[timePath + 'sync_method']
60c74d434cSGunnar Mills                                     .TimeSyncMethod.split('.')
61c74d434cSGunnar Mills                                     .pop();
62b7ea2790SGunnar Mills            }
637de38662SGunnar Mills          },
647de38662SGunnar Mills          function(error) {
657de38662SGunnar Mills            console.log(JSON.stringify(error));
667de38662SGunnar Mills          });
677de38662SGunnar Mills
68b7ea2790SGunnar Mills      var getNTPPromise = APIUtils.getNTPServers().then(
69b7ea2790SGunnar Mills          function(data) {
70b7ea2790SGunnar Mills            $scope.ntp.servers = data.data;
71b7ea2790SGunnar Mills          },
72b7ea2790SGunnar Mills          function(error) {
73b7ea2790SGunnar Mills            console.log(JSON.stringify(error));
74b7ea2790SGunnar Mills          });
75b7ea2790SGunnar Mills
76fdcb35eeSDixsie Wolmers      var promises = [getTimePromise, getNTPPromise];
77b7ea2790SGunnar Mills
78b7ea2790SGunnar Mills      $q.all(promises).finally(function() {
797de38662SGunnar Mills        $scope.loading = false;
807de38662SGunnar Mills      });
81b7ea2790SGunnar Mills
82fdcb35eeSDixsie Wolmers      /**
83fdcb35eeSDixsie Wolmers       * https://github.com/openbmc/phosphor-time-manager/blob/master/README.md#special-note-on-changing-ntp-setting
84fdcb35eeSDixsie Wolmers       * When time mode is initially set to Manual from NTP,
85fdcb35eeSDixsie Wolmers       * NTP service is disabled and the NTP service is
86fdcb35eeSDixsie Wolmers       * stopping but not stopped, setting time will return an error.
87fdcb35eeSDixsie Wolmers       * There are no responses from backend to notify when NTP is stopped.
88fdcb35eeSDixsie Wolmers       * To work around, a timeout is set to allow NTP to fully stop
89fdcb35eeSDixsie Wolmers       * TODO: remove timeout if backend solves
90fdcb35eeSDixsie Wolmers       * https://github.com/openbmc/openbmc/issues/3459
91fdcb35eeSDixsie Wolmers       */
92fdcb35eeSDixsie Wolmers      $scope.saveDateTimeSettings = function() {
93b7ea2790SGunnar Mills        $scope.loading = true;
94fdcb35eeSDixsie Wolmers        if ($scope.time.mode == 'Manual' || $scope.time.owner == 'Split') {
95fdcb35eeSDixsie Wolmers          setTimeMode()
96fdcb35eeSDixsie Wolmers              .then(setTimeOwner)
97fdcb35eeSDixsie Wolmers              .then(setNTPServers)
98fdcb35eeSDixsie Wolmers              .then($timeout(setDateTime, 20000));
99fdcb35eeSDixsie Wolmers        } else {
100fdcb35eeSDixsie Wolmers          setTimeMode()
101fdcb35eeSDixsie Wolmers              .then(setTimeOwner)
102fdcb35eeSDixsie Wolmers              .then(setNTPServers)
103b7ea2790SGunnar Mills              .then(
104b7ea2790SGunnar Mills                  function() {
10527ce84d2Sbeccabroek                    toastService.success('Date and time settings saved');
106b7ea2790SGunnar Mills                  },
107b7ea2790SGunnar Mills                  function(errors) {
108b7ea2790SGunnar Mills                    console.log(JSON.stringify(errors));
10927ce84d2Sbeccabroek                    toastService.error(
11030444c6cSbeccabroek                        'Date and time settings could not be saved');
111b7ea2790SGunnar Mills                  })
112b7ea2790SGunnar Mills              .finally(function() {
113b7ea2790SGunnar Mills                $scope.loading = false;
114b7ea2790SGunnar Mills              });
115fdcb35eeSDixsie Wolmers        }
116fdcb35eeSDixsie Wolmers      };
117fdcb35eeSDixsie Wolmers
118fdcb35eeSDixsie Wolmers      const setDateTime = function() {
119fdcb35eeSDixsie Wolmers        var manualPromises = [];
120fdcb35eeSDixsie Wolmers        if ($scope.time.mode == 'Manual') {
121fdcb35eeSDixsie Wolmers          // If owner is 'Split' set both.
122fdcb35eeSDixsie Wolmers          // If owner is 'Host' set only it.
123fdcb35eeSDixsie Wolmers          if ($scope.time.owner != 'Host') {
124fdcb35eeSDixsie Wolmers            manualPromises.push(setBMCTime($scope.bmc.date.getTime() * 1000));
125fdcb35eeSDixsie Wolmers          }
126fdcb35eeSDixsie Wolmers          // Even though we are setting Host time, we are setting from
127fdcb35eeSDixsie Wolmers          // the BMC date and time fields labeled "BMC and Host Time"
128fdcb35eeSDixsie Wolmers          // currently.
129fdcb35eeSDixsie Wolmers          if ($scope.time.owner == 'Host') {
130fdcb35eeSDixsie Wolmers            manualPromises.push(setHostTime($scope.bmc.date.getTime() * 1000));
131fdcb35eeSDixsie Wolmers          }
132fdcb35eeSDixsie Wolmers        }
133fdcb35eeSDixsie Wolmers        // Set the Host if Split even if NTP. In split mode, the host has
134fdcb35eeSDixsie Wolmers        // its own date and time field set from it.
135fdcb35eeSDixsie Wolmers        if ($scope.time.owner == 'Split') {
136fdcb35eeSDixsie Wolmers          manualPromises.push(setHostTime($scope.host.date.getTime() * 1000));
137fdcb35eeSDixsie Wolmers        }
138fdcb35eeSDixsie Wolmers
139fdcb35eeSDixsie Wolmers        $q.all(manualPromises)
140fdcb35eeSDixsie Wolmers            .then(
141fdcb35eeSDixsie Wolmers                function() {
142fdcb35eeSDixsie Wolmers                  toastService.success('Date and time settings saved');
143b7ea2790SGunnar Mills                },
144b7ea2790SGunnar Mills                function(errors) {
145b7ea2790SGunnar Mills                  console.log(JSON.stringify(errors));
146fdcb35eeSDixsie Wolmers                  toastService.error(
147fdcb35eeSDixsie Wolmers                      'Date and time settings could not be saved');
148fdcb35eeSDixsie Wolmers                })
149fdcb35eeSDixsie Wolmers            .finally(function() {
150b7ea2790SGunnar Mills              $scope.loading = false;
151b7ea2790SGunnar Mills            });
152b7ea2790SGunnar Mills      };
153fdcb35eeSDixsie Wolmers
154b7ea2790SGunnar Mills      $scope.refresh = function() {
155b7ea2790SGunnar Mills        $route.reload();
156b7ea2790SGunnar Mills      };
157b7ea2790SGunnar Mills
158b7ea2790SGunnar Mills      $scope.addNTPField = function() {
159b7ea2790SGunnar Mills        $scope.ntp.servers.push('');
160b7ea2790SGunnar Mills      };
161b7ea2790SGunnar Mills
16290121f3fSGunnar Mills      $scope.removeNTPField = function(index) {
16390121f3fSGunnar Mills        $scope.ntp.servers.splice(index, 1);
16490121f3fSGunnar Mills      };
16590121f3fSGunnar Mills
166b7ea2790SGunnar Mills      function setNTPServers() {
167*254b16eeSGunnar Mills        // Remove any empty strings from the array. If the
168b7ea2790SGunnar Mills        // user doesn't fill out the field, we don't want to add.
169b7ea2790SGunnar Mills        $scope.ntp.servers = $scope.ntp.servers.filter(Boolean);
170*254b16eeSGunnar Mills
171b7ea2790SGunnar Mills        return APIUtils.setNTPServers($scope.ntp.servers);
172b7ea2790SGunnar Mills      }
173b7ea2790SGunnar Mills
174b7ea2790SGunnar Mills      function setTimeMode() {
175b7ea2790SGunnar Mills        return APIUtils.setTimeMode(
176b7ea2790SGunnar Mills            'xyz.openbmc_project.Time.Synchronization.Method.' +
1772f955bd8SGunnar Mills            $scope.time.mode);
178b7ea2790SGunnar Mills      }
179b7ea2790SGunnar Mills
180b7ea2790SGunnar Mills      function setTimeOwner() {
181b7ea2790SGunnar Mills        return APIUtils.setTimeOwner(
1822f955bd8SGunnar Mills            'xyz.openbmc_project.Time.Owner.Owners.' + $scope.time.owner);
183b7ea2790SGunnar Mills      }
184b7ea2790SGunnar Mills
185eab3213cSGunnar Mills      function setBMCTime(time) {
186b7ea2790SGunnar Mills        // Add the separate date and time objects and convert to Epoch time in
187b7ea2790SGunnar Mills        // microseconds.
188eab3213cSGunnar Mills        return APIUtils.setBMCTime(time);
189b7ea2790SGunnar Mills      }
190b7ea2790SGunnar Mills
191eab3213cSGunnar Mills      function setHostTime(time) {
192b7ea2790SGunnar Mills        // Add the separate date and time objects and convert to Epoch time
193b7ea2790SGunnar Mills        // microseconds.
194eab3213cSGunnar Mills        return APIUtils.setHostTime(time);
195b7ea2790SGunnar Mills      }
196569ccf66Sbeccabroek      function createOffset(date) {
197569ccf66Sbeccabroek        // https://stackoverflow.com/questions/9149556/how-to-get-utc-offset-in-javascript-analog-of-timezoneinfo-getutcoffset-in-c
198569ccf66Sbeccabroek        var sign = (date.getTimezoneOffset() > 0) ? '-' : '+';
199569ccf66Sbeccabroek        var offset = Math.abs(date.getTimezoneOffset());
200569ccf66Sbeccabroek        var hours = pad(Math.floor(offset / 60));
201569ccf66Sbeccabroek        var minutes = pad(offset % 60);
202569ccf66Sbeccabroek        return '(UTC' + sign + hours + ':' + minutes + ')';
203569ccf66Sbeccabroek      }
204dbf04811SAlexander Filippov      function getUserTimezone(date) {
205dbf04811SAlexander Filippov        const ro = Intl.DateTimeFormat().resolvedOptions();
206dbf04811SAlexander Filippov        // A safe, easy way to get the timezone (e.g. Central Standard Time) is
207dbf04811SAlexander Filippov        // to subtract the time string without a timezone from the time string
208dbf04811SAlexander Filippov        // with a timezone.
209dbf04811SAlexander Filippov        // Hardcoded to 'en-US' so all timezones are displayed in English
210dbf04811SAlexander Filippov        // (e.g. Moscow Standard Time).
211dbf04811SAlexander Filippov        var ret = date.toLocaleTimeString('en-US', {timeZoneName: 'long'})
212dbf04811SAlexander Filippov                      .replace(date.toLocaleTimeString('en-US'), '')
213dbf04811SAlexander Filippov                      .trim();
214dbf04811SAlexander Filippov        // Do not return GMT+/-offset.
215dbf04811SAlexander Filippov        if (ret.indexOf('GMT') >= 0) {
216dbf04811SAlexander Filippov          return '';
217dbf04811SAlexander Filippov        }
218dbf04811SAlexander Filippov        return ret;
219dbf04811SAlexander Filippov      }
220569ccf66Sbeccabroek      function pad(value) {
221569ccf66Sbeccabroek        return value < 10 ? '0' + value : value;
222569ccf66Sbeccabroek      }
223cd789508SIftekharul Islam    }
224ba5e3f34SAndrew Geissler  ]);
225cd789508SIftekharul Islam})(angular);
226