diff options
-rw-r--r-- | app/common/directives/input.js | 19 | ||||
-rw-r--r-- | app/common/services/api-utils.js | 100 | ||||
-rw-r--r-- | app/configuration/controllers/date-time-controller.html | 84 | ||||
-rw-r--r-- | app/configuration/controllers/date-time-controller.js | 161 | ||||
-rw-r--r-- | app/configuration/controllers/network-controller.html | 2 | ||||
-rw-r--r-- | app/configuration/controllers/network-controller.js | 15 | ||||
-rw-r--r-- | app/configuration/styles/date-time.scss | 67 | ||||
-rw-r--r-- | app/index.js | 1 |
8 files changed, 378 insertions, 71 deletions
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 @@ window.angular && (function(angular) { 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> + <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> - </fieldset> + <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 @@ window.angular && (function(angular) { '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 log_search_control from './common/directives/log-search-control.js'; 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'; |