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