diff --git a/app/agent/rest/host.js b/app/agent/rest/host.js
new file mode 100644
index 000000000..a717def30
--- /dev/null
+++ b/app/agent/rest/host.js
@@ -0,0 +1,15 @@
+angular.module('portainer.agent').factory('Host', [
+ '$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider',
+ function AgentFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
+ 'use strict';
+ return $resource(
+ API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/host/:action',
+ {
+ endpointId: EndpointProvider.endpointID
+ },
+ {
+ info: { method: 'GET', params: { action: 'info' } }
+ }
+ );
+ }
+]);
diff --git a/app/agent/services/agentService.js b/app/agent/services/agentService.js
index 5f011d01c..7143e1e0f 100644
--- a/app/agent/services/agentService.js
+++ b/app/agent/services/agentService.js
@@ -1,24 +1,34 @@
-angular.module('portainer.agent')
-.factory('AgentService', ['$q', 'Agent', function AgentServiceFactory($q, Agent) {
- 'use strict';
- var service = {};
+angular.module('portainer.agent').factory('AgentService', [
+ '$q', 'Agent','HttpRequestHelper', 'Host',
+ function AgentServiceFactory($q, Agent, HttpRequestHelper, Host) {
+ 'use strict';
+ var service = {};
- service.agents = function() {
- var deferred = $q.defer();
+ service.agents = agents;
+ service.hostInfo = hostInfo;
- Agent.query({}).$promise
- .then(function success(data) {
- var agents = data.map(function (item) {
- return new AgentViewModel(item);
- });
- deferred.resolve(agents);
- })
- .catch(function error(err) {
- deferred.reject({ msg: 'Unable to retrieve agents', err: err });
- });
+ function hostInfo(nodeName) {
+ HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
+ return Host.info().$promise;
+ }
- return deferred.promise;
- };
+ function agents() {
+ var deferred = $q.defer();
- return service;
-}]);
+ Agent.query({})
+ .$promise.then(function success(data) {
+ var agents = data.map(function(item) {
+ return new AgentViewModel(item);
+ });
+ deferred.resolve(agents);
+ })
+ .catch(function error(err) {
+ deferred.reject({ msg: 'Unable to retrieve agents', err: err });
+ });
+
+ return deferred.promise;
+ }
+
+ return service;
+ }
+]);
diff --git a/app/docker/__module.js b/app/docker/__module.js
index ffd451fbd..23ce18433 100644
--- a/app/docker/__module.js
+++ b/app/docker/__module.js
@@ -129,13 +129,12 @@ angular.module('portainer.docker', ['portainer.app'])
}
};
- var engine = {
- name: 'docker.engine',
- url: '/engine',
+ var host = {
+ name: 'docker.host',
+ url: '/host',
views: {
'content@': {
- templateUrl: 'app/docker/views/engine/engine.html',
- controller: 'EngineController'
+ component: 'hostView'
}
}
};
@@ -239,8 +238,7 @@ angular.module('portainer.docker', ['portainer.app'])
url: '/:id',
views: {
'content@': {
- templateUrl: 'app/docker/views/nodes/edit/node.html',
- controller: 'NodeController'
+ component: 'nodeDetailsView'
}
}
};
@@ -428,7 +426,7 @@ angular.module('portainer.docker', ['portainer.app'])
$stateRegistryProvider.register(containerStats);
$stateRegistryProvider.register(docker);
$stateRegistryProvider.register(dashboard);
- $stateRegistryProvider.register(engine);
+ $stateRegistryProvider.register(host);
$stateRegistryProvider.register(events);
$stateRegistryProvider.register(images);
$stateRegistryProvider.register(image);
diff --git a/app/docker/components/dockerSidebarContent/dockerSidebarContent.html b/app/docker/components/dockerSidebarContent/dockerSidebarContent.html
index 870c959ed..2e0eeb497 100644
--- a/app/docker/components/dockerSidebarContent/dockerSidebarContent.html
+++ b/app/docker/components/dockerSidebarContent/dockerSidebarContent.html
@@ -35,5 +35,5 @@
Swarm
diff --git a/app/docker/components/host-overview/host-overview.html b/app/docker/components/host-overview/host-overview.html
new file mode 100644
index 000000000..baa43628e
--- /dev/null
+++ b/app/docker/components/host-overview/host-overview.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ Docker
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/docker/components/host-overview/host-overview.js b/app/docker/components/host-overview/host-overview.js
new file mode 100644
index 000000000..add9515b4
--- /dev/null
+++ b/app/docker/components/host-overview/host-overview.js
@@ -0,0 +1,12 @@
+angular.module('portainer.docker').component('hostOverview', {
+ templateUrl: 'app/docker/components/host-overview/host-overview.html',
+ bindings: {
+ hostDetails: '<',
+ engineDetails: '<',
+ devices: '<',
+ disks: '<',
+ isAgent: '<',
+ refreshUrl: '@'
+ },
+ transclude: true
+});
diff --git a/app/docker/components/host-view-panels/devices-panel/devices-panel.html b/app/docker/components/host-view-panels/devices-panel/devices-panel.html
new file mode 100644
index 000000000..5279f4ac1
--- /dev/null
+++ b/app/docker/components/host-view-panels/devices-panel/devices-panel.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+ Name |
+ Vendor |
+
+
+
+
+ {{device.Name}} |
+ {{device.Vendor}} |
+
+
+ Loading... |
+
+
+
+ No device available.
+ |
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/docker/components/host-view-panels/devices-panel/devices-panel.js b/app/docker/components/host-view-panels/devices-panel/devices-panel.js
new file mode 100644
index 000000000..1bd32e1f6
--- /dev/null
+++ b/app/docker/components/host-view-panels/devices-panel/devices-panel.js
@@ -0,0 +1,7 @@
+angular.module('portainer.docker').component('devicesPanel', {
+ templateUrl:
+ 'app/docker/components/host-view-panels/devices-panel/devices-panel.html',
+ bindings: {
+ devices: '<'
+ }
+});
diff --git a/app/docker/components/host-view-panels/disks-panel/disks-panel.html b/app/docker/components/host-view-panels/disks-panel/disks-panel.html
new file mode 100644
index 000000000..632f07151
--- /dev/null
+++ b/app/docker/components/host-view-panels/disks-panel/disks-panel.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+ Vendor |
+ Size |
+
+
+
+
+ {{disk.Vendor}} |
+ {{disk.Size | humansize}} |
+
+
+ Loading... |
+
+
+
+ No disks available.
+ |
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/docker/components/host-view-panels/disks-panel/disks-panel.js b/app/docker/components/host-view-panels/disks-panel/disks-panel.js
new file mode 100644
index 000000000..ae96224ee
--- /dev/null
+++ b/app/docker/components/host-view-panels/disks-panel/disks-panel.js
@@ -0,0 +1,7 @@
+angular.module('portainer.docker').component('disksPanel', {
+ templateUrl:
+ 'app/docker/components/host-view-panels/disks-panel/disks-panel.html',
+ bindings: {
+ disks: '<'
+ }
+});
diff --git a/app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.html b/app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.html
new file mode 100644
index 000000000..4eebf6bcd
--- /dev/null
+++ b/app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Version |
+ {{ $ctrl.engine.releaseVersion }} (API: {{ $ctrl.engine.apiVersion }}) |
+
+
+ Root directory |
+ {{ $ctrl.engine.rootDirectory }} |
+
+
+ Storage Driver |
+ {{ $ctrl.engine.storageDriver }} |
+
+
+ Logging Driver |
+ {{ $ctrl.engine.loggingDriver }} |
+
+
+ Volume Plugins |
+ {{ $ctrl.engine.volumePlugins | arraytostr: ', ' }} |
+
+
+ Network Plugins |
+ {{ $ctrl.engine.networkPlugins | arraytostr: ', ' }} |
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.js b/app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.js
new file mode 100644
index 000000000..666dd5254
--- /dev/null
+++ b/app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.js
@@ -0,0 +1,7 @@
+angular.module('portainer.docker').component('engineDetailsPanel', {
+ templateUrl:
+ 'app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.html',
+ bindings: {
+ engine: '<'
+ }
+});
diff --git a/app/docker/components/host-view-panels/host-details-panel/host-details-panel.html b/app/docker/components/host-view-panels/host-details-panel/host-details-panel.html
new file mode 100644
index 000000000..2ea05f88f
--- /dev/null
+++ b/app/docker/components/host-view-panels/host-details-panel/host-details-panel.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+ Hostname |
+ {{ $ctrl.host.name }} |
+
+
+ OS Information |
+ {{ $ctrl.host.os.type }} {{$ctrl.host.os.arch}}
+ {{$ctrl.host.os.name}} |
+
+
+ Kernel Version |
+ {{ $ctrl.host.kernelVersion }} |
+
+
+ Total CPU |
+ {{ $ctrl.host.totalCPU }} |
+
+
+ Total memory |
+ {{ $ctrl.host.totalMemory | humansize }} |
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/docker/components/host-view-panels/host-details-panel/host-details-panel.js b/app/docker/components/host-view-panels/host-details-panel/host-details-panel.js
new file mode 100644
index 000000000..6865b5872
--- /dev/null
+++ b/app/docker/components/host-view-panels/host-details-panel/host-details-panel.js
@@ -0,0 +1,8 @@
+angular.module('portainer.docker').component('hostDetailsPanel', {
+ templateUrl:
+ 'app/docker/components/host-view-panels/host-details-panel/host-details-panel.html',
+ bindings: {
+ host: '<',
+ isAgent: '<'
+ }
+});
diff --git a/app/docker/components/host-view-panels/node-availability-select/node-availability-select-controller.js b/app/docker/components/host-view-panels/node-availability-select/node-availability-select-controller.js
new file mode 100644
index 000000000..52df40cd6
--- /dev/null
+++ b/app/docker/components/host-view-panels/node-availability-select/node-availability-select-controller.js
@@ -0,0 +1,11 @@
+angular
+ .module('portainer.docker')
+ .controller('NodeAvailabilitySelectController', [
+ function NodeAvailabilitySelectController() {
+ this.onChange = onChange;
+
+ function onChange() {
+ this.onSave({ availability: this.availability });
+ }
+ }
+ ]);
diff --git a/app/docker/components/host-view-panels/node-availability-select/node-availability-select.html b/app/docker/components/host-view-panels/node-availability-select/node-availability-select.html
new file mode 100644
index 000000000..94e086127
--- /dev/null
+++ b/app/docker/components/host-view-panels/node-availability-select/node-availability-select.html
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/app/docker/components/host-view-panels/node-availability-select/node-availability-select.js b/app/docker/components/host-view-panels/node-availability-select/node-availability-select.js
new file mode 100644
index 000000000..b09730f37
--- /dev/null
+++ b/app/docker/components/host-view-panels/node-availability-select/node-availability-select.js
@@ -0,0 +1,10 @@
+angular.module('portainer.docker').component('nodeAvailabilitySelect', {
+ templateUrl:
+ 'app/docker/components/host-view-panels/node-availability-select/node-availability-select.html',
+ controller: 'NodeAvailabilitySelectController',
+ bindings: {
+ availability: '<',
+ originalValue: '<',
+ onSave: '&'
+ }
+});
diff --git a/app/docker/components/host-view-panels/node-labels-table/node-labels-table-controller.js b/app/docker/components/host-view-panels/node-labels-table/node-labels-table-controller.js
new file mode 100644
index 000000000..a9ad8ab58
--- /dev/null
+++ b/app/docker/components/host-view-panels/node-labels-table/node-labels-table-controller.js
@@ -0,0 +1,23 @@
+angular.module('portainer.docker').controller('NodeLabelsTableController', [
+ function NodeLabelsTableController() {
+ var ctrl = this;
+ ctrl.removeLabel = removeLabel;
+ ctrl.updateLabel = updateLabel;
+
+ function removeLabel(index) {
+ var label = ctrl.labels.splice(index, 1);
+ if (label !== null) {
+ ctrl.onChangedLabels({ labels: ctrl.labels });
+ }
+ }
+
+ function updateLabel(label) {
+ if (
+ label.value !== label.originalValue ||
+ label.key !== label.originalKey
+ ) {
+ ctrl.onChangedLabels({ labels: ctrl.labels });
+ }
+ }
+ }
+]);
diff --git a/app/docker/components/host-view-panels/node-labels-table/node-labels-table.html b/app/docker/components/host-view-panels/node-labels-table/node-labels-table.html
new file mode 100644
index 000000000..86eee9356
--- /dev/null
+++ b/app/docker/components/host-view-panels/node-labels-table/node-labels-table.html
@@ -0,0 +1,35 @@
+
+ There are no labels for this node.
+
+
+
\ No newline at end of file
diff --git a/app/docker/components/host-view-panels/node-labels-table/node-labels-table.js b/app/docker/components/host-view-panels/node-labels-table/node-labels-table.js
new file mode 100644
index 000000000..5d6d6c320
--- /dev/null
+++ b/app/docker/components/host-view-panels/node-labels-table/node-labels-table.js
@@ -0,0 +1,9 @@
+angular.module('portainer.docker').component('nodeLabelsTable', {
+ templateUrl:
+ 'app/docker/components/host-view-panels/node-labels-table/node-labels-table.html',
+ controller: 'NodeLabelsTableController',
+ bindings: {
+ labels: '<',
+ onChangedLabels: '&'
+ }
+});
diff --git a/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel-controller.js b/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel-controller.js
new file mode 100644
index 000000000..65e376d99
--- /dev/null
+++ b/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel-controller.js
@@ -0,0 +1,96 @@
+angular
+ .module('portainer.docker')
+ .controller('SwarmNodeDetailsPanelController', [
+ 'NodeService', 'LabelHelper', 'Notifications', '$state',
+ function SwarmNodeDetailsPanelController(NodeService, LabelHelper, Notifications, $state) {
+ var ctrl = this;
+ ctrl.state = {
+ managerAddress: '',
+ hasChanges: false
+ };
+ ctrl.$onChanges = $onChanges;
+ ctrl.addLabel = addLabel;
+ ctrl.updateNodeLabels = updateNodeLabels;
+ ctrl.updateNodeAvailability = updateNodeAvailability;
+ ctrl.saveChanges = saveChanges;
+ ctrl.cancelChanges = cancelChanges;
+
+ var managerRole = 'manager';
+
+ function $onChanges() {
+ if (!ctrl.details) {
+ return;
+ }
+ if (ctrl.details.role === managerRole) {
+ ctrl.state.managerAddress = '(' + ctrl.details.managerAddress + ')';
+ }
+ }
+
+ function addLabel() {
+ ctrl.details.nodeLabels.push({
+ key: '',
+ value: '',
+ originalValue: '',
+ originalKey: ''
+ });
+ }
+
+ function updateNodeLabels(labels) {
+ ctrl.details.nodeLabels = labels;
+ ctrl.state.hasChanges = true;
+ }
+
+ function updateNodeAvailability(availability) {
+ ctrl.details.availability = availability;
+ ctrl.state.hasChanges = true;
+ }
+
+ function saveChanges() {
+ var originalNode = ctrl.originalNode;
+ var config = {
+ Name: originalNode.Name,
+ Availability: ctrl.details.availability,
+ Role: originalNode.Role,
+ Labels: LabelHelper.fromKeyValueToLabelHash(ctrl.details.nodeLabels),
+ Id: originalNode.Id,
+ Version: originalNode.Version
+ };
+
+ NodeService.updateNode(config)
+ .then(onUpdateSuccess)
+ .catch(notifyOnError);
+
+ function onUpdateSuccess() {
+ Notifications.success('Node successfully updated', 'Node updated');
+ $state.go(
+ 'docker.nodes.node',
+ { id: originalNode.Id },
+ { reload: true }
+ );
+ }
+
+ function notifyOnError(error) {
+ Notifications.error('Failure', error, 'Failed to update node');
+ }
+ }
+
+ function cancelChanges() {
+ cancelLabelChanges();
+ ctrl.details.availability = ctrl.originalNode.Availability;
+ ctrl.state.hasChanges = false;
+ }
+
+ function cancelLabelChanges() {
+ ctrl.details.nodeLabels = ctrl.details.nodeLabels
+ .filter(function(label) {
+ return label.originalValue || label.originalKey;
+ })
+ .map(function(label) {
+ return Object.assign(label, {
+ value: label.originalValue,
+ key: label.originalKey
+ });
+ });
+ }
+ }
+ ]);
diff --git a/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.html b/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.html
new file mode 100644
index 000000000..291025e13
--- /dev/null
+++ b/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.html
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+ Node name |
+ {{ $ctrl.details.name }} |
+
+
+ Role |
+ {{ $ctrl.details.role }} {{ $ctrl.state.managerAddress }} |
+
+
+ Availability |
+
+
+
+ |
+
+
+ Status |
+ {{
+ $ctrl.details.status }} |
+
+
+ Engine Labels |
+ {{ $ctrl.details.engineLabels | arraytostr:', ' }} |
+
+
+
+
+ Node Labels
+ |
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.js b/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.js
new file mode 100644
index 000000000..7eea3e708
--- /dev/null
+++ b/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.js
@@ -0,0 +1,9 @@
+angular.module('portainer.docker').component('swarmNodeDetailsPanel', {
+ templateUrl:
+ 'app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.html',
+ controller: 'SwarmNodeDetailsPanelController',
+ bindings: {
+ details: '<',
+ originalNode: '<'
+ }
+});
diff --git a/app/docker/services/nodeService.js b/app/docker/services/nodeService.js
index 706f26647..5ebfeeee4 100644
--- a/app/docker/services/nodeService.js
+++ b/app/docker/services/nodeService.js
@@ -1,24 +1,48 @@
-angular.module('portainer.docker')
-.factory('NodeService', ['$q', 'Node', function NodeServiceFactory($q, Node) {
- 'use strict';
- var service = {};
+angular.module('portainer.docker').factory('NodeService', [
+ '$q', 'Node',
+ function NodeServiceFactory($q, Node) {
+ 'use strict';
+ var service = {};
- service.nodes = function() {
- var deferred = $q.defer();
+ service.nodes = nodes;
+ service.node = node;
+ service.updateNode = updateNode;
- Node.query({}).$promise
- .then(function success(data) {
- var nodes = data.map(function (item) {
- return new NodeViewModel(item);
- });
- deferred.resolve(nodes);
- })
- .catch(function error(err) {
- deferred.reject({ msg: 'Unable to retrieve nodes', err: err });
- });
+ function node(id) {
+ var deferred = $q.defer();
+ Node.get({ id: id })
+ .$promise.then(function onNodeLoaded(rawNode) {
+ var node = new NodeViewModel(rawNode);
+ return deferred.resolve(node);
+ })
+ .catch(function onFailed(err) {
+ deferred.reject({ msg: 'Unable to retrieve node', err: err });
+ });
- return deferred.promise;
- };
+ return deferred.promise;
+ }
- return service;
-}]);
+ function nodes() {
+ var deferred = $q.defer();
+
+ Node.query({})
+ .$promise.then(function success(data) {
+ var nodes = data.map(function(item) {
+ return new NodeViewModel(item);
+ });
+ deferred.resolve(nodes);
+ })
+ .catch(function error(err) {
+ deferred.reject({ msg: 'Unable to retrieve nodes', err: err });
+ });
+
+ return deferred.promise;
+ }
+
+ function updateNode(node) {
+ return Node.update({ id: node.Id, version: node.Version }, node).$promise;
+ }
+
+ return service;
+ }
+]);
diff --git a/app/docker/views/engine/engine.html b/app/docker/views/engine/engine.html
deleted file mode 100644
index 73b1248b2..000000000
--- a/app/docker/views/engine/engine.html
+++ /dev/null
@@ -1,118 +0,0 @@
-
-
-
-
-
-
- Docker
-
-
-
-
-
-
-
-
-
-
- Version |
- {{ version.Version }} |
-
-
- API version |
- {{ version.ApiVersion }} |
-
-
- Go version |
- {{ version.GoVersion }} |
-
-
- OS type |
- {{ version.Os }} |
-
-
- OS |
- {{ info.OperatingSystem }} |
-
-
- Architecture |
- {{ version.Arch }} |
-
-
- Kernel version |
- {{ version.KernelVersion }} |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Total CPU |
- {{ info.NCPU }} |
-
-
- Total memory |
- {{ info.MemTotal|humansize }} |
-
-
- Docker root directory |
- {{ info.DockerRootDir }} |
-
-
- Storage driver |
- {{ info.Driver }} |
-
-
- Logging driver |
- {{ info.LoggingDriver }} |
-
-
- Cgroup driver |
- {{ info.CgroupDriver }} |
-
-
- Execution driver |
- {{ info.ExecutionDriver }} |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Volume |
- {{ info.Plugins.Volume|arraytostr: ', '}} |
-
-
- Network |
- {{ info.Plugins.Network|arraytostr: ', '}} |
-
-
- Authorization |
- {{ info.Plugins.Authorization|arraytostr: ', '}} |
-
-
-
-
-
-
-
diff --git a/app/docker/views/engine/engineController.js b/app/docker/views/engine/engineController.js
deleted file mode 100644
index f106273a5..000000000
--- a/app/docker/views/engine/engineController.js
+++ /dev/null
@@ -1,22 +0,0 @@
-angular.module('portainer.docker')
-.controller('EngineController', ['$q', '$scope', 'SystemService', 'Notifications',
-function ($q, $scope, SystemService, Notifications) {
-
- function initView() {
- $q.all({
- version: SystemService.version(),
- info: SystemService.info()
- })
- .then(function success(data) {
- $scope.version = data.version;
- $scope.info = data.info;
- })
- .catch(function error(err) {
- $scope.info = {};
- $scope.version = {};
- Notifications.error('Failure', err, 'Unable to retrieve engine details');
- });
- }
-
- initView();
-}]);
diff --git a/app/docker/views/host/host-view-controller.js b/app/docker/views/host/host-view-controller.js
new file mode 100644
index 000000000..43bc49391
--- /dev/null
+++ b/app/docker/views/host/host-view-controller.js
@@ -0,0 +1,70 @@
+angular.module('portainer.docker').controller('HostViewController', [
+ '$q', 'SystemService', 'Notifications', 'StateManager', 'AgentService',
+ function HostViewController($q, SystemService, Notifications, StateManager, AgentService) {
+ var ctrl = this;
+ this.$onInit = initView;
+
+ ctrl.state = {
+ isAgent: false
+ };
+
+ this.engineDetails = {};
+ this.hostDetails = {};
+
+ function initView() {
+ var applicationState = StateManager.getState();
+ ctrl.state.isAgent = applicationState.endpoint.mode.agentProxy;
+
+ $q.all({
+ version: SystemService.version(),
+ info: SystemService.info()
+ })
+ .then(function success(data) {
+ ctrl.engineDetails = buildEngineDetails(data);
+ ctrl.hostDetails = buildHostDetails(data.info);
+
+ if (ctrl.state.isAgent) {
+ return AgentService.hostInfo(data.info.Hostname).then(function onHostInfoLoad(agentHostInfo) {
+ ctrl.devices = agentHostInfo.PCIDevices;
+ ctrl.disks = agentHostInfo.PhysicalDisks;
+ });
+ }
+ })
+ .catch(function error(err) {
+ Notifications.error(
+ 'Failure',
+ err,
+ 'Unable to retrieve engine details'
+ );
+ });
+ }
+
+ function buildEngineDetails(data) {
+ var versionDetails = data.version;
+ var info = data.info;
+ return {
+ releaseVersion: versionDetails.Version,
+ apiVersion: versionDetails.ApiVersion,
+ rootDirectory: info.DockerRootDir,
+ storageDriver: info.Driver,
+ loggingDriver: info.LoggingDriver,
+ volumePlugins: info.Plugins.Volume,
+ networkPlugins: info.Plugins.Network
+ };
+ }
+
+ function buildHostDetails(info) {
+ return {
+ os: {
+ arch: info.Architecture,
+ type: info.OSType,
+ name: info.OperatingSystem
+ },
+ name: info.Name,
+ kernelVersion: info.KernelVersion,
+ totalCPU: info.NCPU,
+ totalMemory: info.MemTotal
+ };
+ }
+ }
+]);
diff --git a/app/docker/views/host/host-view.html b/app/docker/views/host/host-view.html
new file mode 100644
index 000000000..419e96995
--- /dev/null
+++ b/app/docker/views/host/host-view.html
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/app/docker/views/host/host-view.js b/app/docker/views/host/host-view.js
new file mode 100644
index 000000000..e321dccb4
--- /dev/null
+++ b/app/docker/views/host/host-view.js
@@ -0,0 +1,4 @@
+angular.module('portainer.docker').component('hostView', {
+ templateUrl: 'app/docker/views/host/host-view.html',
+ controller: 'HostViewController'
+});
diff --git a/app/docker/views/nodes/edit/node.html b/app/docker/views/nodes/edit/node.html
deleted file mode 100644
index b7d733206..000000000
--- a/app/docker/views/nodes/edit/node.html
+++ /dev/null
@@ -1,243 +0,0 @@
-
-
-
-
-
-
-
- Swarm nodes > {{ node.Hostname }}
-
-
-
-
-
-
- Loading...
-
-
-
-
-
- It looks like the node you wish to inspect does not exist.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- View the Docker Swarm mode Node documentation here.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Leader |
-
- Yes
- No
- |
-
-
- Reachability |
- {{ node.Reachability }} |
-
-
- Manager address |
- {{ node.ManagerAddr }} |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- CPU |
- {{ node.CPUs / 1000000000 }} |
-
-
- Memory |
- {{ node.Memory|humansize: 2 }} |
-
-
- Platform |
- {{ node.PlatformOS }} {{ node.PlatformArchitecture }} |
-
-
- Docker Engine version |
- {{ node.EngineVersion }} |
-
-
-
-
-
-
-
-
-
-
-
-
-
- There are no engine labels for this node.
-
-
-
-
-
- Label |
- Value |
-
-
-
-
- {{ engineLabel.key }} |
- {{ engineLabel.value }} |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- There are no labels for this node.
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/docker/views/nodes/edit/nodeController.js b/app/docker/views/nodes/edit/nodeController.js
deleted file mode 100644
index 30f210b2a..000000000
--- a/app/docker/views/nodes/edit/nodeController.js
+++ /dev/null
@@ -1,96 +0,0 @@
-angular.module('portainer.docker')
-.controller('NodeController', ['$scope', '$state', '$transition$', 'LabelHelper', 'Node', 'NodeHelper', 'Task', 'Notifications',
-function ($scope, $state, $transition$, LabelHelper, Node, NodeHelper, Task, Notifications) {
-
- $scope.loading = true;
- $scope.tasks = [];
-
- var originalNode = {};
- var editedKeys = [];
-
- $scope.updateNodeAttribute = function updateNodeAttribute(node, key) {
- editedKeys.push(key);
- };
- $scope.addLabel = function addLabel(node) {
- node.Labels.push({ key: '', value: '', originalValue: '', originalKey: '' });
- $scope.updateNodeAttribute(node, 'Labels');
- };
- $scope.removeLabel = function removeLabel(node, index) {
- var removedElement = node.Labels.splice(index, 1);
- if (removedElement !== null) {
- $scope.updateNodeAttribute(node, 'Labels');
- }
- };
- $scope.updateLabel = function updateLabel(node, label) {
- if (label.value !== label.originalValue || label.key !== label.originalKey) {
- $scope.updateNodeAttribute(node, 'Labels');
- }
- };
-
- $scope.hasChanges = function(node, elements) {
- if (!elements) {
- elements = Object.keys(originalNode);
- }
- var hasChanges = false;
- elements.forEach(function(key) {
- hasChanges = hasChanges || ((editedKeys.indexOf(key) >= 0) && node[key] !== originalNode[key]);
- });
- return hasChanges;
- };
-
- $scope.cancelChanges = function(node) {
- editedKeys.forEach(function(key) {
- node[key] = originalNode[key];
- });
- editedKeys = [];
- };
-
- $scope.updateNode = function updateNode(node) {
- var config = NodeHelper.nodeToConfig(node.Model);
- config.Name = node.Name;
- config.Availability = node.Availability;
- config.Role = node.Role;
- config.Labels = LabelHelper.fromKeyValueToLabelHash(node.Labels);
-
- Node.update({ id: node.Id, version: node.Version }, config, function () {
- Notifications.success('Node successfully updated', 'Node updated');
- $state.go('docker.nodes.node', {id: node.Id}, {reload: true});
- }, function (e) {
- Notifications.error('Failure', e, 'Failed to update node');
- });
- };
-
- function loadNodeAndTasks() {
- $scope.loading = true;
- if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') {
- Node.get({ id: $transition$.params().id}, function(d) {
- if (d.message) {
- Notifications.error('Failure', e, 'Unable to inspect the node');
- } else {
- var node = new NodeViewModel(d);
- originalNode = angular.copy(node);
- $scope.node = node;
- getTasks(d);
- }
- $scope.loading = false;
- });
- } else {
- $scope.loading = false;
- }
- }
-
- function getTasks(node) {
- if (node) {
- Task.query({filters: {node: [node.ID]}}, function (tasks) {
- $scope.tasks = tasks.map(function (task) {
- return new TaskViewModel(task);
- });
- }, function (e) {
- Notifications.error('Failure', e, 'Unable to retrieve tasks associated to the node');
- });
- }
- }
-
- loadNodeAndTasks();
-
-}]);
diff --git a/app/docker/views/nodes/node-details/node-details-view-controller.js b/app/docker/views/nodes/node-details/node-details-view-controller.js
new file mode 100644
index 000000000..cde111986
--- /dev/null
+++ b/app/docker/views/nodes/node-details/node-details-view-controller.js
@@ -0,0 +1,75 @@
+angular.module('portainer.docker').controller('NodeDetailsViewController', [
+ '$stateParams', 'NodeService', 'StateManager', 'AgentService',
+ function NodeDetailsViewController($stateParams, NodeService, StateManager, AgentService) {
+ var ctrl = this;
+
+ ctrl.$onInit = initView;
+
+ ctrl.state = {
+ isAgent: false
+ };
+
+ function initView() {
+ var applicationState = StateManager.getState();
+ ctrl.state.isAgent = applicationState.endpoint.mode.agentProxy;
+
+ var nodeId = $stateParams.id;
+ NodeService.node(nodeId).then(function(node) {
+ ctrl.originalNode = node;
+ ctrl.hostDetails = buildHostDetails(node);
+ ctrl.engineDetails = buildEngineDetails(node);
+ ctrl.nodeDetails = buildNodeDetails(node);
+ if (ctrl.state.isAgent) {
+ AgentService.hostInfo(node.Hostname).then(function onHostInfoLoad(
+ agentHostInfo
+ ) {
+ ctrl.devices = agentHostInfo.PCIDevices;
+ ctrl.disks = agentHostInfo.PhysicalDisks;
+ });
+ }
+ });
+ }
+
+ function buildHostDetails(node) {
+ return {
+ os: {
+ arch: node.PlatformArchitecture,
+ type: node.PlatformOS
+ },
+ name: node.Hostname,
+ totalCPU: node.CPUs / 1e9,
+ totalMemory: node.Memory
+ };
+ }
+
+ function buildEngineDetails(node) {
+ return {
+ releaseVersion: node.EngineVersion,
+ volumePlugins: transformPlugins(node.Plugins, 'Volume'),
+ networkPlugins: transformPlugins(node.Plugins, 'Network')
+ };
+ }
+
+ function buildNodeDetails(node) {
+ return {
+ name: node.Name,
+ role: node.Role,
+ managerAddress: node.ManagerAddr,
+ availability: node.Availability,
+ status: node.Status,
+ engineLabels: node.EngineLabels,
+ nodeLabels: node.Labels
+ };
+ }
+
+ function transformPlugins(pluginsList, type) {
+ return pluginsList
+ .filter(function(plugin) {
+ return plugin.Type === type;
+ })
+ .map(function(plugin) {
+ return plugin.Name;
+ });
+ }
+ }
+]);
diff --git a/app/docker/views/nodes/node-details/node-details-view.html b/app/docker/views/nodes/node-details/node-details-view.html
new file mode 100644
index 000000000..13df99558
--- /dev/null
+++ b/app/docker/views/nodes/node-details/node-details-view.html
@@ -0,0 +1,13 @@
+
+
+
\ No newline at end of file
diff --git a/app/docker/views/nodes/node-details/node-details-view.js b/app/docker/views/nodes/node-details/node-details-view.js
new file mode 100644
index 000000000..5b1c76e2b
--- /dev/null
+++ b/app/docker/views/nodes/node-details/node-details-view.js
@@ -0,0 +1,4 @@
+angular.module('portainer.docker').component('nodeDetailsView', {
+ templateUrl: 'app/docker/views/nodes/node-details/node-details-view.html',
+ controller: 'NodeDetailsViewController'
+});