mirror of https://github.com/portainer/portainer
Merge pull request #110 from rabelenda/master
Add controls for easy visualization when too many containerspull/2/head
commit
2bc6b2995d
|
@ -1,7 +1,23 @@
|
|||
<div class="detail">
|
||||
<h2>Containers Network</h2>
|
||||
|
||||
<div>
|
||||
<vis-network data="data" options="options" events="events"/>
|
||||
<div class="row">
|
||||
<div class="input-group">
|
||||
<input type="text" ng-model="query" autofocus="true" class="form-control"
|
||||
placeholder="Search" ng-change="network.selectContainers(query)"/>
|
||||
<span class="input-group-addon"><span class="glyphicon glyphicon-search"/></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-warning" ng-click="network.hideSelected()">Hide Selected</button>
|
||||
<button class="btn btn-info" ng-click="network.showSelectedDownstream()">Show Selected Downstream</button>
|
||||
<button class="btn btn-info" ng-click="network.showSelectedUpstream()">Show Selected Upstream</button>
|
||||
<button class="btn btn-success" ng-click="network.showAll()">Show All</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<vis-network data="network.data" options="network.options" events="network.events"
|
||||
component="network.component"/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,29 +1,14 @@
|
|||
angular.module('containersNetwork', ['ngVis'])
|
||||
.controller('ContainersNetworkController', ['$scope', '$location', 'Container', 'Messages', 'VisDataSet', function($scope, $location, Container, Messages, VisDataSet) {
|
||||
$scope.options = {
|
||||
navigation: true,
|
||||
keyboard: true,
|
||||
height: '500px', width: '700px',
|
||||
nodes: {
|
||||
shape: 'box'
|
||||
},
|
||||
edges: {
|
||||
style: 'arrow'
|
||||
},
|
||||
physics: {
|
||||
barnesHut : {
|
||||
springLength: 200
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function ContainerNode(data) {
|
||||
this.Id = data.Id;
|
||||
// names have the following format: /Name
|
||||
this.Name = data.Name.substring(1);
|
||||
this.Image = data.Config.Image;
|
||||
var dataLinks = data.HostConfig.Links;
|
||||
if (dataLinks != null) {
|
||||
this.Links = [];
|
||||
this.Links = {};
|
||||
for (var i = 0; i < dataLinks.length; i++) {
|
||||
// links have the following format: /TargetContainerName:/SourceContainerName/LinkAlias
|
||||
var link = dataLinks[i].split(":");
|
||||
|
@ -38,64 +23,227 @@ angular.module('containersNetwork', ['ngVis'])
|
|||
var dataVolumes = data.HostConfig.VolumesFrom;
|
||||
//converting array into properties for simpler and faster access
|
||||
if (dataVolumes != null) {
|
||||
this.VolumesFrom = [];
|
||||
this.VolumesFrom = {};
|
||||
for (var j = 0; j < dataVolumes.length; j++) {
|
||||
this.VolumesFrom[dataVolumes[j]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ContainersNetwork() {
|
||||
this.containers = [];
|
||||
function ContainersNetworkData() {
|
||||
this.nodes = new VisDataSet();
|
||||
this.edges = new VisDataSet();
|
||||
|
||||
this.add = function(data) {
|
||||
var container = new ContainerNode(data);
|
||||
this.containers.push(container);
|
||||
this.nodes.add({id: container.Id, label: container.Name});
|
||||
for (var i = 0; i < this.containers.length; i++) {
|
||||
var otherContainer = this.containers[i];
|
||||
this.addLinkEdgeIfExists(container, otherContainer);
|
||||
this.addLinkEdgeIfExists(otherContainer, container);
|
||||
this.addVolumeEdgeIfExists(container, otherContainer);
|
||||
this.addVolumeEdgeIfExists(otherContainer, container);
|
||||
}
|
||||
this.addContainerNode = function(container) {
|
||||
this.nodes.add({
|
||||
id: container.Id,
|
||||
label: container.Name,
|
||||
title: "<ul style=\"list-style-type:none; padding: 0px; margin: 0px\">" +
|
||||
"<li><strong>ID:</strong> " + container.Id + "</li>" +
|
||||
"<li><strong>Image:</strong> " + container.Image + "</li>" +
|
||||
"</ul>"});
|
||||
};
|
||||
|
||||
this.addLinkEdgeIfExists = function(from, to) {
|
||||
if (from.Links != null && from.Links[to.Name] != null) {
|
||||
this.edges.add({ from: from.Id, to: to.Id, label: from.Links[to.Name] });
|
||||
this.edges.add({
|
||||
from: from.Id,
|
||||
to: to.Id,
|
||||
label: from.Links[to.Name] });
|
||||
}
|
||||
};
|
||||
|
||||
this.addVolumeEdgeIfExists = function(from, to) {
|
||||
if (from.VolumesFrom != null && from.VolumesFrom[to.Id] != null) {
|
||||
this.edges.add({ from: from.Id, to: to.Id, color: { color: '#A0A0A0', highlight: '#A0A0A0', hover: '#848484'}});
|
||||
this.edges.add({
|
||||
from: from.Id,
|
||||
to: to.Id,
|
||||
color: { color: '#A0A0A0', highlight: '#A0A0A0', hover: '#848484'}});
|
||||
}
|
||||
};
|
||||
|
||||
this.removeContainersNodes = function(containersIds) {
|
||||
this.nodes.remove(containersIds);
|
||||
};
|
||||
}
|
||||
|
||||
$scope.data = new ContainersNetwork();
|
||||
$scope.events = {
|
||||
doubleClick : function(event) {
|
||||
$scope.$apply( function() {
|
||||
$location.path('/containers/' + event.nodes[0]);
|
||||
});
|
||||
}
|
||||
};
|
||||
function ContainersNetwork() {
|
||||
this.data = new ContainersNetworkData();
|
||||
this.containers = {};
|
||||
this.selectedContainersIds = [];
|
||||
this.shownContainersIds = [];
|
||||
this.events = {
|
||||
select : function(event) {
|
||||
$scope.network.selectedContainersIds = event.nodes;
|
||||
$scope.$apply( function() {
|
||||
$scope.query = '';
|
||||
});
|
||||
},
|
||||
doubleClick : function(event) {
|
||||
$scope.$apply( function() {
|
||||
$location.path('/containers/' + event.nodes[0]);
|
||||
});
|
||||
}
|
||||
};
|
||||
this.options = {
|
||||
navigation: true,
|
||||
keyboard: true,
|
||||
height: '500px', width: '700px',
|
||||
nodes: {
|
||||
shape: 'box'
|
||||
},
|
||||
edges: {
|
||||
style: 'arrow'
|
||||
},
|
||||
physics: {
|
||||
barnesHut : {
|
||||
springLength: 200
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.addContainer = function(data) {
|
||||
var container = new ContainerNode(data);
|
||||
this.containers[container.Id] = container;
|
||||
this.shownContainersIds.push(container.Id);
|
||||
this.data.addContainerNode(container);
|
||||
for (var otherContainerId in this.containers) {
|
||||
var otherContainer = this.containers[otherContainerId];
|
||||
this.data.addLinkEdgeIfExists(container, otherContainer);
|
||||
this.data.addLinkEdgeIfExists(otherContainer, container);
|
||||
this.data.addVolumeEdgeIfExists(container, otherContainer);
|
||||
this.data.addVolumeEdgeIfExists(otherContainer, container);
|
||||
}
|
||||
};
|
||||
|
||||
this.selectContainers = function(query) {
|
||||
if (this.component != null) {
|
||||
this.selectedContainersIds = this.searchContainers(query);
|
||||
this.component.selectNodes(this.selectedContainersIds);
|
||||
}
|
||||
};
|
||||
|
||||
this.searchContainers = function(query) {
|
||||
if (query.trim() === "") {
|
||||
return [];
|
||||
}
|
||||
var selectedContainersIds = [];
|
||||
for (var i=0; i < this.shownContainersIds.length; i++) {
|
||||
var container = this.containers[this.shownContainersIds[i]];
|
||||
if (container.Name.indexOf(query) > -1 ||
|
||||
container.Image.indexOf(query) > -1 ||
|
||||
container.Id.indexOf(query) > -1) {
|
||||
selectedContainersIds.push(container.Id);
|
||||
}
|
||||
}
|
||||
return selectedContainersIds;
|
||||
};
|
||||
|
||||
this.hideSelected = function() {
|
||||
var i=0;
|
||||
while ( i < this.shownContainersIds.length ) {
|
||||
if (this.selectedContainersIds.indexOf(this.shownContainersIds[i]) > -1) {
|
||||
this.shownContainersIds.splice(i, 1);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
this.data.removeContainersNodes(this.selectedContainersIds);
|
||||
$scope.query = '';
|
||||
this.selectedContainersIds = [];
|
||||
};
|
||||
|
||||
this.searchDownstream = function(containerId, downstreamContainersIds) {
|
||||
if (downstreamContainersIds.indexOf(containerId) > -1) {
|
||||
return;
|
||||
}
|
||||
downstreamContainersIds.push(containerId);
|
||||
var container = this.containers[containerId];
|
||||
if (container.Links == null && container.VolumesFrom == null) {
|
||||
return;
|
||||
}
|
||||
for (var otherContainerId in this.containers) {
|
||||
var otherContainer = this.containers[otherContainerId];
|
||||
if (container.Links != null && container.Links[otherContainer.Name] != null) {
|
||||
this.searchDownstream(otherContainer.Id, downstreamContainersIds);
|
||||
} else if (container.VolumesFrom != null &&
|
||||
container.VolumesFrom[otherContainer.Id] != null) {
|
||||
this.searchDownstream(otherContainer.Id, downstreamContainersIds);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.updateShownContainers = function(newShownContainersIds) {
|
||||
for (var containerId in this.containers) {
|
||||
if (newShownContainersIds.indexOf(containerId) > -1 &&
|
||||
this.shownContainersIds.indexOf(containerId) === -1) {
|
||||
this.data.addContainerNode(this.containers[containerId]);
|
||||
} else if (newShownContainersIds.indexOf(containerId) === -1 &&
|
||||
this.shownContainersIds.indexOf(containerId) > -1) {
|
||||
this.data.removeContainersNodes(containerId);
|
||||
}
|
||||
}
|
||||
this.shownContainersIds = newShownContainersIds;
|
||||
};
|
||||
|
||||
this.showSelectedDownstream = function() {
|
||||
var downstreamContainersIds = [];
|
||||
for (var i=0; i < this.selectedContainersIds.length; i++) {
|
||||
this.searchDownstream(this.selectedContainersIds[i], downstreamContainersIds);
|
||||
}
|
||||
this.updateShownContainers(downstreamContainersIds);
|
||||
};
|
||||
|
||||
this.searchUpstream = function(containerId, upstreamContainersIds) {
|
||||
if (upstreamContainersIds.indexOf(containerId) > -1) {
|
||||
return;
|
||||
}
|
||||
upstreamContainersIds.push(containerId);
|
||||
var container = this.containers[containerId];
|
||||
for (var otherContainerId in this.containers) {
|
||||
var otherContainer = this.containers[otherContainerId];
|
||||
if (otherContainer.Links != null && otherContainer.Links[container.Name] != null) {
|
||||
this.searchUpstream(otherContainer.Id, upstreamContainersIds);
|
||||
} else if (otherContainer.VolumesFrom != null &&
|
||||
otherContainer.VolumesFrom[container.Id] != null) {
|
||||
this.searchUpstream(otherContainer.Id, upstreamContainersIds);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.showSelectedUpstream = function() {
|
||||
var upstreamContainersIds = [];
|
||||
for (var i=0; i < this.selectedContainersIds.length; i++) {
|
||||
this.searchUpstream(this.selectedContainersIds[i], upstreamContainersIds);
|
||||
}
|
||||
this.updateShownContainers(upstreamContainersIds);
|
||||
};
|
||||
|
||||
this.showAll = function() {
|
||||
for (var containerId in this.containers) {
|
||||
if (this.shownContainersIds.indexOf(containerId) === -1) {
|
||||
this.data.addContainerNode(this.containers[containerId]);
|
||||
this.shownContainersIds.push(containerId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
$scope.network = new ContainersNetwork();
|
||||
|
||||
var showFailure = function (event) {
|
||||
Messages.error('Failure', e.data);
|
||||
Messages.error('Failure', e.data);
|
||||
};
|
||||
|
||||
var addContainer = function (container) {
|
||||
$scope.data.add(container);
|
||||
$scope.network.addContainer(container);
|
||||
};
|
||||
|
||||
Container.query({all: 0}, function(d) {
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
Container.get({id: d[i].Id}, addContainer, showFailure);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}]);
|
||||
|
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
@ -19,7 +19,8 @@ angular.module('ngVis', [])
|
|||
scope: {
|
||||
data: '=',
|
||||
options: '=',
|
||||
events: '='
|
||||
events: '=',
|
||||
component: '='
|
||||
},
|
||||
link: function (scope, element, attr) {
|
||||
var timelineEvents = [
|
||||
|
@ -46,6 +47,7 @@ angular.module('ngVis', [])
|
|||
|
||||
// Create the timeline object
|
||||
timeline = new vis.Timeline(element[0]);
|
||||
scope.component = timeline;
|
||||
|
||||
// Attach an event handler if defined
|
||||
angular.forEach(scope.events, function (callback, event) {
|
||||
|
@ -92,7 +94,8 @@ angular.module('ngVis', [])
|
|||
scope: {
|
||||
data: '=',
|
||||
options: '=',
|
||||
events: '='
|
||||
events: '=',
|
||||
component: '='
|
||||
},
|
||||
link: function (scope, element, attr) {
|
||||
var networkEvents = [
|
||||
|
@ -103,6 +106,7 @@ angular.module('ngVis', [])
|
|||
];
|
||||
|
||||
var network = new vis.Network(element[0], scope.data, scope.options);
|
||||
scope.component = network;
|
||||
|
||||
scope.$watch('data', function () {
|
||||
// Sanity check
|
||||
|
@ -118,6 +122,7 @@ angular.module('ngVis', [])
|
|||
|
||||
// Create the graph2d object
|
||||
network = new vis.Network(element[0]);
|
||||
scope.component = network;
|
||||
|
||||
// Attach an event handler if defined
|
||||
angular.forEach(scope.events, function (callback, event) {
|
||||
|
@ -166,7 +171,8 @@ angular.module('ngVis', [])
|
|||
scope: {
|
||||
data: '=',
|
||||
options: '=',
|
||||
events: '='
|
||||
events: '=',
|
||||
component: '='
|
||||
},
|
||||
link: function (scope, element, attr) {
|
||||
var graphEvents = [
|
||||
|
@ -178,6 +184,7 @@ angular.module('ngVis', [])
|
|||
|
||||
// Create the chart
|
||||
var graph = new vis.Graph2d(element[0]);
|
||||
scope.component = graph;
|
||||
|
||||
scope.$watch('data', function () {
|
||||
// Sanity check
|
||||
|
@ -193,6 +200,7 @@ angular.module('ngVis', [])
|
|||
|
||||
// Create the graph2d object
|
||||
graph = new vis.Graph2d(element[0]);
|
||||
scope.component = graph;
|
||||
|
||||
// Attach an event handler if defined
|
||||
angular.forEach(scope.events, function (callback, event) {
|
||||
|
|
Loading…
Reference in New Issue