diff options
author | Petr Vobornik <pvoborni@redhat.com> | 2015-11-12 18:28:01 +0100 |
---|---|---|
committer | Petr Vobornik <pvoborni@redhat.com> | 2015-11-27 15:50:56 +0100 |
commit | 68f6c2c7dc1277d9efd3e729a924d4c9cadc90ec (patch) | |
tree | fdc7e6e227ef4484af317be6e2a1620d6ef7a872 /install/ui/src/freeipa | |
parent | 24fead79cbe5ef168a42f08da6fd99b4ec2b1bab (diff) | |
download | freeipa-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.js | 12 | ||||
-rw-r--r-- | install/ui/src/freeipa/navigation/menu_spec.js | 4 | ||||
-rw-r--r-- | install/ui/src/freeipa/topology.js | 347 |
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); |