Set time fields
Squashed 5 commits to set the date-time fields:
time mode, time owner, BMC time, and host time.
Also included is a commit to display the timezone.
Set time mode
Moved the selection of NTP vs Manual (time mode) to a radio
button.
Added code to allow the user set it.
Tested: Set the time mode on a Witherspoon.
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
Set time owner
The time owner is now a dropdown and is set when "Save settings"
is pressed.
Tested: Set the time owner on a Witherspoon
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
Set the BMC and Host time
The BMC and host have the same time except when time mode is split.
Only need to set BMC or host time when time mode is not split.
When time mode is split, set both. Use time owner to determine
which to set.
https://github.com/openbmc/phosphor-time-manager#time-settings
Have date and time as two separate inputs, this is due to Firefox
not supporting "datetime-local". The "date" and "time" input
fields are more widely supported.
https://caniuse.com/#feat=input-datetime
The idea for 2 separate input fields came from:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local
Must set the time mode and time owner before setting the time,
this is due to permissions of who can set the time.
Tested: Set the date and time on a Witherspoon.
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
Add NTP Servers
The user can now view and set NTP Servers.
Moved setFocusOnNewInput to a common directive since it is used
on the NTP Servers and the DNS Servers on the network page.
Even though NTPServers is a network interface specific path
(e.g. /xyz/openbmc_project/network/eth0/attr/NTPServers) it acts
like a global setting, openbmc/phosphor-time-manager#4.
Using eth0 for setting and getting the NTP Servers until
NTPServers is moved to a non-network interface specific path.
In Redfish, NTPServers is a non-network interface specific.
Tested: Set NTP Servers on a Witherspoon.
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
Add timezone
Added the timezone for the host and bmc date time.
The timezone looks like "GMT-0400 (EDT)" or "GMT-0500 (CDT)".
I got this from
https://stackoverflow.com/questions/1091372/getting-the-clients-timezone-in-javascript
and choose this formatting over something like "America/Chicago".
Tested: See the timezone on a Witherspoon
Change-Id: I59a4449d63f73a6ed14cb934f3d8577e46620c4e
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/app/common/directives/input.js b/app/common/directives/input.js
new file mode 100644
index 0000000..cb4d830
--- /dev/null
+++ b/app/common/directives/input.js
@@ -0,0 +1,19 @@
+window.angular && (function(angular) {
+ 'use strict';
+
+ angular.module('app.common.directives')
+ .directive('setFocusOnNewInput', function() {
+ return function(scope, element, attrs) {
+ var elem = window.document.getElementById(element[0].id);
+ // Focus on the newly created input.
+ // Since this directive is also called when initializing fields
+ // on a page load, need to determine if the call is from a page load
+ // or from the user pressing the "Add new" button. The easiest way to
+ // do this is to check if the field is empty, if it is we know this is
+ // a new field since all empty fields are removed from the array.
+ if (!scope[attrs.ngModel] && elem) {
+ elem.focus();
+ }
+ };
+ });
+})(window.angular);
diff --git a/app/common/services/api-utils.js b/app/common/services/api-utils.js
index 434ca2f..a9fb99a 100644
--- a/app/common/services/api-utils.js
+++ b/app/common/services/api-utils.js
@@ -1052,6 +1052,106 @@
return response.data;
});
},
+ // Even though NTPServers is a network interface specific path
+ // (e.g. /xyz/openbmc_project/network/eth0/attr/NTPServers) it acts
+ // like a global setting. Just use eth0 for setting and getting the
+ // NTP Servers until it is moved to a non-network interface specific
+ // path like it is in Redfish. TODO: openbmc/phosphor-time-manager#4
+ getNTPServers: function() {
+ return $http({
+ method: 'GET',
+ url: DataService.getHost() +
+ '/xyz/openbmc_project/network/eth0/attr/NTPServers',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ withCredentials: true
+ })
+ .then(function(response) {
+ return response.data;
+ });
+ },
+ setNTPServers: function(ntpServers) {
+ return $http({
+ method: 'PUT',
+ url: DataService.getHost() +
+ '/xyz/openbmc_project/network/eth0/attr/NTPServers',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ withCredentials: true,
+ data: JSON.stringify({'data': ntpServers})
+ })
+ .then(function(response) {
+ return response.data;
+ });
+ },
+ setTimeMode: function(timeMode) {
+ return $http({
+ method: 'PUT',
+ url: DataService.getHost() +
+ '/xyz/openbmc_project/time/sync_method/attr/TimeSyncMethod',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ withCredentials: true,
+ data: JSON.stringify({'data': timeMode})
+ })
+ .then(function(response) {
+ return response.data;
+ });
+ },
+ setTimeOwner: function(timeOwner) {
+ return $http({
+ method: 'PUT',
+ url: DataService.getHost() +
+ '/xyz/openbmc_project/time/owner/attr/TimeOwner',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ withCredentials: true,
+ data: JSON.stringify({'data': timeOwner})
+ })
+ .then(function(response) {
+ return response.data;
+ });
+ },
+ setBMCTime: function(time) {
+ return $http({
+ method: 'PUT',
+ url: DataService.getHost() +
+ '/xyz/openbmc_project/time/bmc/attr/Elapsed',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ withCredentials: true,
+ data: JSON.stringify({'data': time})
+ })
+ .then(function(response) {
+ return response.data;
+ });
+ },
+ setHostTime: function(time) {
+ return $http({
+ method: 'PUT',
+ url: DataService.getHost() +
+ '/xyz/openbmc_project/time/host/attr/Elapsed',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ withCredentials: true,
+ data: JSON.stringify({'data': time})
+ })
+ .then(function(response) {
+ return response.data;
+ });
+ },
getHardwares: function(callback) {
$http({
method: 'GET',
diff --git a/app/configuration/controllers/date-time-controller.html b/app/configuration/controllers/date-time-controller.html
index a64a001..491ab56 100644
--- a/app/configuration/controllers/date-time-controller.html
+++ b/app/configuration/controllers/date-time-controller.html
@@ -1,31 +1,65 @@
<loader loading="loading"></loader>
<div id="configuration-date-time">
<div class="row column">
- <h1>Date time settings</h1>
+ <h1>Date and time settings</h1>
</div>
- <div class="page-header">
- <h2 class="bold h4">Time information</h2>
- </div>
- <fieldset>
- <div class="column large-8">
- <ul class="date-time__metadata-wrapper">
- <li class="date-time__metadata-block">
- <p class="content-label">BMC <span ng-if="time_owner != 'Split'">and Host</span> Time</p>
- <p class="courier-bold">{{bmc_time | date:'medium'}}</p>
- </li>
- <li class="date-time__metadata-block" ng-if="time_owner == 'Split'">
- <p class="content-label">Host Time</p>
- <p class="courier-bold">{{host_time | date:'medium'}}</p>
- </li>
- <li class="date-time__metadata-block">
- <p class="content-label">Time Owner</p>
- <p class="courier-bold">{{time_owner}}</p>
- </li>
- <li class="date-time__metadata-block">
- <p class="content-label">Time Mode</p>
- <p class="courier-bold">{{time_mode}}</p>
- </li>
- </ul>
+ <form class="time__form" role="form" action="">
+ <div class="page-header">
+ <h2 class="bold h4">Date and time set to Network Time Protocol (NTP) or manually</h2>
</div>
- </fieldset>
+ <fieldset>
+ <div class="column large-8">
+ <div class="row column">
+ <label class="control-radio" for="ntp-time">Obtain Automatically from a Network Time Protocol (NTP) Server
+ <input type="radio" id="ntp-time" ng-model="time_mode" value="NTP">
+ <span class="control__indicator control__indicator-on"></span>
+ </label>
+ </div>
+ <div class="row column date-time__ntp-servers-wrap">
+ <fieldset class="date-time__ntp-servers" ng-repeat="server in ntp.servers track by $index">
+ <label for="ntp-server{{$index+1}}">NTP Server Address {{$index+1}}</label>
+ <input id="ntp-server{{$index+1}}" type="text" ng-readonly="time_mode != 'NTP'" ng-model="server" ng-blur="ntp.servers[$index] = server" set-focus-on-new-input/>
+ </fieldset>
+ <button type="button" class="btn-primary inline" ng-click="addNTPField()">Add new NTP server</button>
+ </div>
+ <div class="row column">
+ <label class="control-radio" for="manual-time">Manually set date and time
+ <input type="radio" id="manual-time" ng-model="time_mode" value="Manual"/>
+ <span class="control__indicator control__indicator-on"></span>
+ </label>
+ </div>
+ <ul class="date-time__metadata-wrapper">
+ <li class="date-time__metadata-block">
+ <p class="content-label">BMC <span ng-if="time_owner != 'Split'">and Host</span> Time</p>
+ <div class="inline">
+ <input type="date" ng-model="bmc.date" ng-readonly="time_mode == 'NTP'" min="2018-01-01" max="2099-12-31"/>
+ <input type="time" ng-model="bmc.date" ng-readonly="time_mode == 'NTP'" />
+ <p class="courier-bold">{{bmc.timezone}}</p>
+ </div>
+ </li>
+ <li class="date-time__metadata-block" ng-if="time_owner == 'Split'">
+ <p class="content-label">Host Time</p>
+ <div class="inline">
+ <!--- Ideally, would just use one input, datetime-local, but datetime-local is not supported on Firefox.--->
+ <input type="date" ng-model="host.date" min="2018-01-01" max="2099-12-31"/>
+ <input type="time" ng-model="host.date"/>
+ <p class="courier-bold">{{host.timezone}}</p>
+ </div>
+ </li>
+ <li class="date-time__metadata-block">
+ <label class="content-label">Time Owner</label>
+ <select ng-model="time_owner" class="date-time__owner-dropdown">
+ <option class="courier-bold" ng-repeat="owner in time_owners">{{owner}}</option>
+ </select>
+ </li>
+ </ul>
+ </div>
+ </fieldset>
+ <div class="time__submit-wrapper">
+ <button type="button" class="btn-primary inline" ng-click="setTime()">Save settings</button>
+ <button type="button" class="btn-secondary inline" ng-click="refresh()">Cancel</button>
+ </div>
+ <p class="success-msg" ng-show="set_time_success" role="alert">Success! Time changed!</p>
+ <p class="set_time_error error-msg" ng-show="set_time_error" role="alert">Error setting time!</p>
+ </form>
</div>
diff --git a/app/configuration/controllers/date-time-controller.js b/app/configuration/controllers/date-time-controller.js
index cbc7443..02752fe 100644
--- a/app/configuration/controllers/date-time-controller.js
+++ b/app/configuration/controllers/date-time-controller.js
@@ -10,35 +10,164 @@
'use strict';
angular.module('app.configuration').controller('dateTimeController', [
- '$scope', '$window', 'APIUtils',
- function($scope, $window, APIUtils) {
- $scope.bmc_time = '';
+ '$scope', '$window', 'APIUtils', '$route', '$q',
+ function($scope, $window, APIUtils, $route, $q) {
+ $scope.bmc = {};
+ $scope.host = {};
+ $scope.ntp = {servers: []};
$scope.time_mode = '';
$scope.time_owner = '';
+ $scope.time_owners = ['BMC', 'Host', 'Both', 'Split'];
+ $scope.set_time_error = false;
+ $scope.set_time_success = false;
$scope.loading = true;
+ var time_path = '/xyz/openbmc_project/time/';
var getTimePromise = APIUtils.getTime().then(
function(data) {
- $scope.bmc_time =
- data.data['/xyz/openbmc_project/time/bmc'].Elapsed / 1000;
- $scope.host_time =
- data.data['/xyz/openbmc_project/time/host'].Elapsed / 1000;
-
- $scope.time_owner = data.data['/xyz/openbmc_project/time/owner']
- .TimeOwner.split('.')
- .pop();
- $scope.time_mode =
- data.data['/xyz/openbmc_project/time/sync_method']
- .TimeSyncMethod.split('.')
- .pop();
+ // The time is returned as Epoch microseconds convert to
+ // milliseconds.
+ if (data.data[time_path + 'bmc'] &&
+ data.data[time_path + 'bmc'].hasOwnProperty('Elapsed')) {
+ $scope.bmc.date =
+ new Date(data.data[time_path + 'bmc'].Elapsed / 1000);
+ // Don't care about milliseconds and don't want them displayed
+ $scope.bmc.date.setMilliseconds(0);
+ // https://stackoverflow.com/questions/1091372/getting-the-clients-timezone-in-javascript
+ // GMT-0400 (EDT)
+ $scope.bmc.timezone =
+ $scope.bmc.date.toString().match(/([A-Z]+[\+-][0-9]+.*)/)[1];
+ }
+ if (data.data[time_path + 'host'] &&
+ data.data[time_path + 'host'].hasOwnProperty('Elapsed')) {
+ $scope.host.date =
+ new Date(data.data[time_path + 'host'].Elapsed / 1000);
+ $scope.host.date.setMilliseconds(0);
+ $scope.host.timezone =
+ $scope.host.date.toString().match(/([A-Z]+[\+-][0-9]+.*)/)[1];
+ }
+ if (data.data[time_path + 'owner'] &&
+ data.data[time_path + 'owner'].hasOwnProperty('TimeOwner')) {
+ $scope.time_owner =
+ data.data[time_path + 'owner'].TimeOwner.split('.').pop();
+ }
+ if (data.data[time_path + 'sync_method'] &&
+ data.data[time_path + 'sync_method'].hasOwnProperty(
+ 'TimeSyncMethod')) {
+ $scope.time_mode = data.data[time_path + 'sync_method']
+ .TimeSyncMethod.split('.')
+ .pop();
+ }
},
function(error) {
console.log(JSON.stringify(error));
});
- getTimePromise.finally(function() {
+ 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.set_time_error = false;
+ $scope.set_time_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());
+ }
+
+ if ($scope.time_owner == 'Host' ||
+ $scope.time_owner == 'Split') {
+ manual_promises.push(setHostTime());
+ }
+ }
+ $q.all(manual_promises)
+ .then(
+ function() {
+ $scope.set_time_success = true;
+ },
+ function(errors) {
+ console.log(JSON.stringify(errors));
+ $scope.set_time_error = true;
+ })
+ .finally(function() {
+ $scope.loading = false;
+ });
+ },
+ function(errors) {
+ console.log(JSON.stringify(errors));
+ $scope.set_time_error = true;
+ $scope.loading = false;
+ });
+ };
+ $scope.refresh = function() {
+ $route.reload();
+ };
+
+ $scope.addNTPField = function() {
+ $scope.ntp.servers.push('');
+ };
+
+ 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() {
+ // Add the separate date and time objects and convert to Epoch time in
+ // microseconds.
+ return APIUtils.setBMCTime($scope.bmc.date.getTime() * 1000);
+ }
+
+ function setHostTime() {
+ // Add the separate date and time objects and convert to Epoch time
+ // microseconds.
+ return APIUtils.setHostTime($scope.host.date.getTime() * 1000);
+ }
}
]);
})(angular);
diff --git a/app/configuration/controllers/network-controller.html b/app/configuration/controllers/network-controller.html
index 1dd92eb..4bc210a 100644
--- a/app/configuration/controllers/network-controller.html
+++ b/app/configuration/controllers/network-controller.html
@@ -77,7 +77,7 @@
<!-- Call Nameservers "DNS Servers" on the GUI -->
<fieldset class="net-config__static-ip-wrap" ng-repeat="dns in interface.Nameservers track by $index">
<label for="net-config__prime-dns{{$index+1}}">DNS Server {{$index+1}}</label>
- <input id="net-config__prime-dns{{$index+1}}" type="text" ng-model="dns" ng-blur="interface.Nameservers[$index] = dns" set-focus-dns-field/>
+ <input id="net-config__prime-dns{{$index+1}}" type="text" ng-model="dns" ng-blur="interface.Nameservers[$index] = dns" set-focus-on-new-input//>
</fieldset>
<button type="button" class="btn-primary inline" ng-click="addDNSField()">Add new DNS server</button>
</div>
diff --git a/app/configuration/controllers/network-controller.js b/app/configuration/controllers/network-controller.js
index 431b97e..9bec746 100644
--- a/app/configuration/controllers/network-controller.js
+++ b/app/configuration/controllers/network-controller.js
@@ -9,21 +9,6 @@
window.angular && (function(angular) {
'use strict';
- angular.module('app.configuration').directive('setFocusDnsField', function() {
- return function(scope, element, attrs) {
- var elem = window.document.getElementById(element[0].id);
- // Focus on the newly created DNS server field
- // Since this directive is also called when initializing DNS server fields
- // on a page load, need to determine if the call is from a page load or
- // from the user pressing the "Add new DNS server" button. The easiest way
- // to do this is to check if the field is empty, if it is we know
- // this is a new field since all empty fields are removed from the array.
- if (!scope[attrs.ngModel] && elem) {
- elem.focus();
- }
- };
- });
-
angular.module('app.configuration').controller('networkController', [
'$scope', '$window', 'APIUtils', 'dataService', '$timeout', '$route', '$q',
function($scope, $window, APIUtils, dataService, $timeout, $route, $q) {
diff --git a/app/configuration/styles/date-time.scss b/app/configuration/styles/date-time.scss
index 670e549..d5d9b40 100644
--- a/app/configuration/styles/date-time.scss
+++ b/app/configuration/styles/date-time.scss
@@ -1,19 +1,58 @@
// Date Time SCSS
+.time__form {
-.date-time__metadata-wrapper {
- margin: 0;
- padding: 0;
-}
+ input {
+ height: 2.1em;
+ margin-bottom: 0em;
+ }
-.date-time__metadata-block {
- list-style-type: none;
- width: 47%;
- margin-bottom: 1.8em;
- margin-right: .7em;
- display: inline-block;
- white-space: normal;
- word-break: break-all;
- @include mediaQuery(small) {
- float: left;
+ fieldset {
+ padding-left: 1.8em;
+ }
+
+ .date-time__metadata-wrapper {
+ margin: 0;
+ padding: 0;
+ }
+ .date-time__owner-dropdown {
+ width: 200px;
+ &:hover {
+ background: $dropdown__focus-bg;
+ }
+ }
+ .date-time__ntp-servers{
+ padding-bottom: .6em;
+ padding-top: .6em;
+ padding-left: 0em;
+ width: 300px;
+ }
+ .date-time__ntp-servers-wrap{
+ padding-bottom: 1em;
+ padding-top: 1em;
+ padding-left: 3em;
+ }
+ .date-time__metadata-block {
+ list-style-type: none;
+ width: 47%;
+ margin-bottom: .7em;
+ margin-top: 1.8em;
+ margin-right: .7em;
+ margin-left: 3em;
+ display: inline;
+ white-space: normal;
+ word-break: break-all;
+ @include mediaQuery(small) {
+ float: left;
+ }
+ }
+ .time__submit-wrapper {
+ width: 100%;
+ margin-top: 3em;
+ padding-top: 1em;
+ border-top: 1px solid $medgrey;
+ button {
+ float: right;
+ margin: .5em;
+ }
}
}
diff --git a/app/index.js b/app/index.js
index cde0296..602d601 100644
--- a/app/index.js
+++ b/app/index.js
@@ -47,6 +47,7 @@
import toggle_flag from './common/directives/toggle-flag.js';
import firmware_list from './common/directives/firmware-list.js';
import file from './common/directives/file.js';
+import input from './common/directives/input.js';
import loader from './common/directives/loader.js';
import paginate from './common/directives/paginate.js';
import serial_console from './common/directives/serial-console.js';