summaryrefslogtreecommitdiffstats
path: root/install/ui/src/freeipa
diff options
context:
space:
mode:
authorPetr Vobornik <pvoborni@redhat.com>2015-11-12 18:28:01 +0100
committerPetr Vobornik <pvoborni@redhat.com>2015-11-27 15:50:56 +0100
commit68f6c2c7dc1277d9efd3e729a924d4c9cadc90ec (patch)
treefdc7e6e227ef4484af317be6e2a1620d6ef7a872 /install/ui/src/freeipa
parent24fead79cbe5ef168a42f08da6fd99b4ec2b1bab (diff)
downloadfreeipa-68f6c2c7dc1277d9efd3e729a924d4c9cadc90ec.tar.gz
freeipa-68f6c2c7dc1277d9efd3e729a924d4c9cadc90ec.tar.xz
freeipa-68f6c2c7dc1277d9efd3e729a924d4c9cadc90ec.zip
webui: topology graph facet
https://fedorahosted.org/freeipa/ticket/4286 Reviewed-By: Martin Babinsky <mbabinsk@redhat.com>
Diffstat (limited to 'install/ui/src/freeipa')
-rw-r--r--install/ui/src/freeipa/ipa.js12
-rw-r--r--install/ui/src/freeipa/navigation/menu_spec.js4
-rw-r--r--install/ui/src/freeipa/topology.js347
3 files changed, 360 insertions, 3 deletions
diff --git a/install/ui/src/freeipa/ipa.js b/install/ui/src/freeipa/ipa.js
index eaaaaf7fc..23efd6916 100644
--- a/install/ui/src/freeipa/ipa.js
+++ b/install/ui/src/freeipa/ipa.js
@@ -220,6 +220,18 @@ var IPA = function () {
}
}));
+ batch.add_command(rpc.command({
+ entity: 'domainlevel',
+ method: 'get',
+ retry: false,
+ on_success: function(data, text_status, xhr) {
+ that.domain_level = data.result;
+ },
+ on_error: function(xhr, text_status, error_thrown) {
+ that.domain_level = 0;
+ }
+ }));
+
batch.execute();
};
diff --git a/install/ui/src/freeipa/navigation/menu_spec.js b/install/ui/src/freeipa/navigation/menu_spec.js
index 8140ddf25..fb64ccaea 100644
--- a/install/ui/src/freeipa/navigation/menu_spec.js
+++ b/install/ui/src/freeipa/navigation/menu_spec.js
@@ -224,6 +224,10 @@ var nav = {};
entity: 'domainlevel',
facet: 'details',
hidden: true
+ },
+ {
+ facet: 'topology-graph',
+ hidden: true
}
]
},
diff --git a/install/ui/src/freeipa/topology.js b/install/ui/src/freeipa/topology.js
index 4d585947b..c0fad8518 100644
--- a/install/ui/src/freeipa/topology.js
+++ b/install/ui/src/freeipa/topology.js
@@ -5,7 +5,12 @@
define([
'dojo/_base/lang',
'dojo/_base/declare',
+ 'dojo/Evented',
+ 'dojo/Stateful',
+ 'dojo/Deferred',
'dojo/on',
+ 'dojo/promise/all',
+ 'dojo/when',
'./builder',
'./ipa',
'./jquery',
@@ -18,10 +23,17 @@ define([
'./details',
'./facet',
'./field',
+ './facets/ActionMixin',
+ './facets/HeaderMixin',
+ './facets/Facet',
+ './topology_graph',
+ // plain imports
'./search',
'./entity'],
- function(lang, declare, on, builder, IPA, $, menu, metadata_provider,
- phases, reg, rpc, text, mod_details, mod_facet, mod_field) {
+ function(lang, declare, Evented, Stateful, Deferred, on, all, when,
+ builder, IPA, $, menu, metadata_provider, phases, reg, rpc,
+ text, mod_details, mod_facet, mod_field, ActionMixin,
+ HeaderMixin, Facet, topology_graph) {
/**
* Topology module
* @class
@@ -29,13 +41,16 @@ define([
*/
var topology = IPA.topology = {
+ required_domain_level: 1,
+
search_facet_group: {
name: 'search',
label: '@i18n:tabs.topology',
facets: {
suffix_search: 'topologysuffix_search',
server_search: 'server_search',
- domainlevel: 'domainlevel_details'
+ domainlevel: 'domainlevel_details',
+ topologygraph: 'topology-graph'
}
}
};
@@ -330,6 +345,323 @@ topology.domainlevel_set_action = function(spec) {
};
+topology.topology_graph_facet_spec = {
+ name: 'topology-graph',
+ 'class': 'topology-graph container-fluid',
+ label: 'Topology Graph',
+ tab_label: 'Topology Graph',
+ facet_groups: [topology.search_facet_group],
+ facet_group: 'search',
+ actions: ['refresh'],
+ control_buttons: [
+ {
+ name: 'refresh',
+ label: '@i18n:buttons.refresh',
+ icon: 'fa-refresh'
+ }
+ ],
+ widgets: [
+ {
+ $type: 'activity',
+ name: 'activity',
+ text: 'Working',
+ visible: false
+ },
+ {
+ $type: 'topology-graph',
+ name: 'topology-graph'
+ }
+ ]
+};
+
+/**
+ * Facet containing topology graph
+ *
+ * @class
+ */
+topology.TopologyGraphFacet = declare([Facet, ActionMixin, HeaderMixin], {
+
+ init: function(spec) {
+ this.inherited(arguments);
+ var graph = this.get_widget('topology-graph');
+
+ on(this, 'show', lang.hitch(this, function(args) {
+ graph.update();
+ }));
+ },
+
+ refresh: function() {
+ var graph = this.get_widget('topology-graph');
+ graph.update();
+ }
+});
+
+
+/**
+ * Graph widget encapsulates and supply data to graph component
+ *
+ * Graph is show only with domain level 1.
+ *
+ * @class
+ */
+topology.TopologyGraphWidget = declare([Stateful, Evented], {
+
+ graph: null,
+
+ // nodes
+ container_node: null,
+ el: null,
+
+ disabled_view_el: null,
+ topology_view_el: null,
+ visualization_cnt_el: null,
+
+ _get_servers: function() {
+ var deferred = new Deferred();
+ var s_promise = rpc.command({
+ entity: 'server',
+ method: 'find',
+ options: {
+ sizelimit: 0
+ }
+ }).execute();
+ when(s_promise, lang.hitch(this, function(results) {
+ // suffices load success
+ var servers = results.data.result.result;
+ deferred.resolve(servers);
+ }), function(results) {
+ deferred.reject({
+ message: 'unable to load servers',
+ results: results
+ });
+ });
+ return deferred.promise;
+ },
+
+ _get_suffices: function() {
+ var deferred = new Deferred();
+
+ function get_suffices() {
+ return rpc.command({
+ entity: 'topologysuffix',
+ method: 'find',
+ options: {
+ sizelimit: 0
+ }
+ }).execute();
+ }
+
+ function get_segments(suffix_name) {
+ return rpc.command({
+ entity: 'topologysegment',
+ method: 'find',
+ args: [suffix_name],
+ options: {
+ sizelimit: 0
+ }
+ }).execute();
+ }
+
+ var suff_promise = get_suffices();
+
+ when(suff_promise, lang.hitch(this, function(results) {
+ // suffices load success
+ var suffices = results.data.result.result;
+ var segment_promises = [];
+ for (var i=0,l=suffices.length; i<l; i++) {
+ var suffix = suffices[i];
+ var promise = get_segments(suffix['cn'][0]);
+ segment_promises.push(promise);
+ }
+ all(segment_promises).then(lang.hitch(this, function(results) {
+ // segments load success
+ for (var j=0,l=results.length; j<l; j++) {
+ suffices[j].segments = results[j].data.result.result;
+ }
+ deferred.resolve(suffices);
+ }), lang.hitch(this, function(results) {
+ // segments load failed
+ deferred.reject({
+ message: 'unable to load segments',
+ results: results
+ });
+ }));
+ }), lang.hitch(this, function(results) {
+ // suffix load failed
+ deferred.reject({
+ message: 'unable to load suffices',
+ results: results
+ });
+ }));
+
+ return deferred.promise;
+ },
+
+ _transform_data: function(servers, suffices) {
+
+ var i,l;
+ var nodes = [];
+ var links = [];
+ var node_map = {};
+
+ function add_to_targets(source, target, link) {
+ if (!source.targets[target.id]) {
+ source.targets[target.id] = [];
+ }
+ source.targets[target.id].push(link);
+ source.targets[target.id].sort(function(a, b) {
+ return a.suffix.cn[0] > b.suffix.cn[0];
+ });
+ }
+
+ for (i=0,l=servers.length; i<l; i++) {
+ var server = servers[i];
+ var name = server.cn[0];
+ var node = {
+ id: name,
+ data: server,
+ targets: {}
+ };
+ node_map[name] = i;
+ nodes.push(node);
+ }
+
+ for (i=0,l=suffices.length; i<l; i++) {
+ var suffix = suffices[i];
+
+ for (var j=0,l2=suffix.segments.length; j<l2; j++) {
+ var segment = suffix.segments[j];
+ var direction = segment.iparepltoposegmentdirection[0];
+ var source_cn = segment.iparepltoposegmentleftnode[0];
+ var target_cn = segment.iparepltoposegmentrightnode[0];
+
+ // check for invalid segments - can happen if there is
+ // some issue with topo plugin
+ if (node_map[source_cn] === undefined ||
+ node_map[target_cn] === undefined) {
+ window.console.log('dangling segment: ' + segment.cn[0]);
+ continue; // skip invalid segments
+ }
+
+ var link = {
+ source: node_map[source_cn],
+ target: node_map[target_cn],
+ left: true,
+ right: true,
+ suffix: suffix
+ };
+ if (direction === 'left') {
+ link.right = false;
+ } else if (direction === 'right') {
+ link.left = false;
+ }
+
+ links.push(link);
+ var src_node = nodes[link.source];
+ var target_node = nodes[link.target];
+ add_to_targets(src_node, target_node, link);
+ add_to_targets(target_node, src_node, link);
+ }
+ }
+
+ var data = {
+ nodes: nodes,
+ links: links,
+ suffices: suffices
+ };
+
+ return data;
+ },
+
+ _get_data: function() {
+
+ var deferred = new Deferred();
+
+ var segments = this._get_suffices();
+ var masters = this._get_servers();
+
+ all([masters, segments]).then(lang.hitch(this, function(raw) {
+ var data = this._transform_data(raw[0], raw[1]);
+ deferred.resolve(data);
+ }), function(error) {
+ deferred.reject(error);
+ });
+
+ return deferred.promise;
+ },
+
+ update: function() {
+ if (IPA.domain_level < topology.required_domain_level) return;
+
+ when(this._get_data()).then(lang.hitch(this, function(data) {
+ if (!this.graph) {
+ this.graph = new topology_graph.TopoGraph({
+ nodes: data.nodes,
+ links: data.links,
+ suffices: data.suffices
+ });
+ this._bind_graph_events(this.graph);
+ this.graph.initialize(this.visualization_cnt_el);
+
+ } else {
+ this.graph.update(data.nodes, data.links, data.suffices);
+ }
+ }), function(error) {
+ IPA.notify(error.message, 'error');
+ });
+ },
+
+ _bind_graph_events: function(graph) {
+ },
+
+ render: function() {
+ this.el = $('<div/>', { 'class': this.css_class });
+ if (IPA.domain_level < topology.required_domain_level) {
+ this._render_disabled_view().appendTo(this.el);
+ } else {
+ this._render_topology_view().appendTo(this.el);
+ }
+ if (this.container_node) {
+ this.el.appendTo(this.container_node);
+ }
+ return this.el;
+ },
+
+ _render_disabled_view: function() {
+ if (this.disabled_view_el) return this.disabled_view_el;
+
+ this.disabled_view_el = $('<div/>', { 'class': 'disabled-view' });
+ var msg = text.get('@i18n:objects.topology.insufficient_domain_level');
+ msg = msg.replace('${domainlevel}', topology.required_domain_level);
+ $('<div/>')
+ .append(
+ $('<p/>', {
+ text: msg
+ })
+ )
+ .appendTo(this.disabled_view_el);
+
+ return this.disabled_view_el;
+ },
+
+ _render_topology_view: function() {
+ if (this.topology_view_el) return this.topology_view_el;
+
+ this.topology_view_el = $('<div/>', { 'class': 'topology-view' });
+ this.visualization_cnt_el = $('<div/>', { 'class': 'visualization' }).
+ appendTo(this.topology_view_el);
+
+ return this.topology_view_el;
+ },
+
+ _init_widgets: function() {
+ },
+
+ constructor: function(spec) {
+ lang.mixin(this, spec);
+ this._init_widgets();
+ }
+});
/**
* Topology suffix entity specification object
@@ -363,6 +695,8 @@ topology.domainlevel_spec = make_domainlevel_spec();
topology.register = function() {
var e = reg.entity;
var a = reg.action;
+ var fa = reg.facet;
+ var w = reg.widget;
e.register({type: 'topologysuffix', spec: topology.suffix_spec});
e.register({type: 'topologysegment', spec: topology.segment_spec});
@@ -370,6 +704,13 @@ topology.register = function() {
e.register({type: 'domainlevel', spec: topology.domainlevel_spec});
a.register('domainlevel_set', topology.domainlevel_set_action);
+
+ w.register('topology-graph', topology.TopologyGraphWidget);
+ fa.register({
+ type: 'topology-graph',
+ ctor: topology.TopologyGraphFacet,
+ spec: topology.topology_graph_facet_spec
+ });
};
phases.on('registration', topology.register);