Class: HybridPlatformsConductor::Topographer
- Inherits:
-
Object
- Object
- HybridPlatformsConductor::Topographer
- Includes:
- LoggerHelpers
- Defined in:
- lib/hybrid_platforms_conductor/topographer.rb,
lib/hybrid_platforms_conductor/topographer/plugin.rb,
lib/hybrid_platforms_conductor/topographer/plugins/svg.rb,
lib/hybrid_platforms_conductor/topographer/plugins/json.rb,
lib/hybrid_platforms_conductor/topographer/plugins/graphviz.rb
Overview
Class giving an API to parse the graph of the TI network
Defined Under Namespace
Modules: Plugins Classes: Plugin
Constant Summary
Constants included from LoggerHelpers
LoggerHelpers::LEVELS_MODIFIERS, LoggerHelpers::LEVELS_TO_STDERR
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Some getters that can be useful for clients of the Topographer.
-
#node_metadata ⇒ Object
readonly
Some getters that can be useful for clients of the Topographer.
-
#nodes_graph ⇒ Object
readonly
Some getters that can be useful for clients of the Topographer.
Class Method Summary collapse
-
.default_config ⇒ Object
Give a default configuration.
Instance Method Summary collapse
-
#ancestor_nodes(nodes_list) ⇒ Object
Return the list of nodes and ancestors of a given list of nodes, recursively.
-
#available_plugins ⇒ Object
Get the list of available plugins.
-
#children_nodes(nodes_list) ⇒ Object
Return the list of nodes and children of a given list of nodes, recursively.
-
#cluster_nodes ⇒ Object
Return the list of nodes that are clusters.
-
#collapse_nodes(nodes_list) ⇒ Object
Collapse a given list of nodes.
-
#define_clusters_ip_24 ⇒ Object
Define clusters of ips with 24 bits ranges.
-
#description_for(node_name) ⇒ Object
Get the description of a given node.
-
#dump_outputs ⇒ Object
Dump the graph in the desired outputs.
-
#filter_in_nodes(nodes_list) ⇒ Object
Remove from the graph any node that is not part of a given list.
-
#filter_out_nodes(nodes_list) ⇒ Object
Remove from the graph any node that is part of a given list.
-
#force_cluster_strict_hierarchy ⇒ Object
Make sure clusters follow a strict hierarchy and that 1 node belongs to at most 1 cluster.
-
#get_json_files ⇒ Object
Generate the JSON files to be used.
-
#graph_for(hostnames) ⇒ Object
Add to the graph a given set of hostnames and their connected nodes.
-
#graph_for_nodes_lists(nodes_lists, only_add_cluster: false) ⇒ Object
Add to the graph a given set of nodes lists and their connected nodes.
-
#initialize(logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR), nodes_handler: NodesHandler.new, json_dumper: JsonDumper.new, config: {}) ⇒ Topographer
constructor
Constructor.
-
#is_node_cluster?(node_name) ⇒ Boolean
Is the node represented as a cluster?.
-
#is_node_physical?(node_name) ⇒ Boolean
Is the node a physical node?.
-
#options_parse(options_parser) ⇒ Object
Complete an option parser with ways to tune the topographer.
-
#remove_empty_clusters ⇒ Object
Remove empty clusters.
-
#remove_self_connections ⇒ Object
Remove self connections.
-
#replace_nodes(nodes_to_be_replaced, replacement_node) ⇒ Object
Replace a list of nodes by a given node.
-
#resolve_from_to ⇒ Object
Resolve the from and to hosts descriptions.
-
#title_for(node_name) ⇒ Object
Get the title of a given node.
-
#validate_params ⇒ Object
Validate that parsed parameters are valid.
-
#write_graph(file_name, output_format) ⇒ Object
Output the graph to a given file at a given format.
Methods included from LoggerHelpers
#err, #init_loggers, #log_component=, #log_debug?, #log_level=, #out, #section, #set_loggers_format, #stderr_device, #stderr_device=, #stderr_displayed?, #stdout_device, #stdout_device=, #stdout_displayed?, #stdouts_to_s, #with_progress_bar
Constructor Details
#initialize(logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR), nodes_handler: NodesHandler.new, json_dumper: JsonDumper.new, config: {}) ⇒ Topographer
Constructor
- Parameters
-
logger (Logger): Logger to be used [default = Logger.new(STDOUT)]
-
logger_stderr (Logger): Logger to be used for stderr [default = Logger.new(STDERR)]
-
nodes_handler (NodesHandler): The nodes handler to be used [default = NodesHandler.new]
-
json_dumper (JsonDumper): The JSON Dumper to be used [default = JsonDumper.new]
-
config (Hash<Symbol,Object>): Some configuration parameters that can override defaults. [default = {}] Here are the possible keys:
-
json_files_dir (String): Directory from which JSON files are taken. [default = nodes_json]
-
connections_max_level (Integer or nil): Number maximal of recursive passes to get hostname connections (nil means no limit). [default = nil]
-
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 81 def initialize(logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR), nodes_handler: NodesHandler.new, json_dumper: JsonDumper.new, config: {}) init_loggers(logger, logger_stderr) @nodes_handler = nodes_handler @json_dumper = json_dumper @config = Topographer.default_config.merge(config) # Get the metadata of each node, per hostname # Hash<String,Hash> @node_metadata = {} # Know for each IP what is the hostname it belongs to # Hash<String,String> @ips_to_host = {} # Get the connection information per node name. A node reprensents 1 element that can be connected to other elements in the graph. # Hash< String, Hash<Symbol,Object> > # Here are the possible information keys: # * *type* (Symbol): Type of the node. Can be one of: :node, :cluster, :unknown. # * *connections* (Hash< String, Array<String> >): List of labels per connected node. # * *includes* (Array<String>): List of nodes included in this one. # * *includes_proc* (Proc): Proc called to know if a node belongs to this cluster [only if type == :cluster]: # * Parameters:: # * *node_name* (String): Name of the node for the inclusion test # * Result:: # * Boolean: Does the node belongs to this cluster? # * *ipv4* (IPAddress::IPv4): Corresponding IPv4 object [only if type == :node and a private IP exists, or type == :unknown, or type == :cluster and the cluster name is an IP range] @nodes_graph = {} # Default values @from_hosts = [] @to_hosts = [] @outputs = [] @skip_run = false # Parse plugins @plugins = Hash[Dir. glob("#{File.dirname(__FILE__)}/topographer/plugins/*.rb"). map do |file_name| plugin_name = File.basename(file_name)[0..-4].to_sym require file_name [ plugin_name, Topographer::Plugins.const_get(plugin_name.to_s.split('_').collect(&:capitalize).join.to_sym) ] end] @ips_to_host = known_ips.clone # Fill info from the metadata = %i[ description physical_node private_ips ] @nodes_handler. @nodes_handler.known_nodes, @nodes_handler.known_nodes.each do |hostname| @node_metadata[hostname] = Hash[.map { |property| [property, @nodes_handler.(hostname, property)] }] end # Small cache of hostnames used a lot to parse JSON @known_nodes = Hash[@nodes_handler.known_nodes.map { |hostname| [hostname, nil] }] # Cache of objects being used a lot in parsing for performance @non_word_regexp = /\W+/ @ip_regexp = /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(\/(\d{1,2})|[^\d\/]|$)/ # Cache of ignored IPs @ips_ignored = {} end |
Instance Attribute Details
#config ⇒ Object (readonly)
Some getters that can be useful for clients of the Topographer
69 70 71 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 69 def config @config end |
#node_metadata ⇒ Object (readonly)
Some getters that can be useful for clients of the Topographer
69 70 71 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 69 def @node_metadata end |
#nodes_graph ⇒ Object (readonly)
Some getters that can be useful for clients of the Topographer
69 70 71 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 69 def nodes_graph @nodes_graph end |
Class Method Details
.default_config ⇒ Object
Give a default configuration
- Result
-
Hash<Symbol,Object>: Default configuration
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 19 def self.default_config { # Directory from which the complete JSON files are to be read json_files_dir: 'nodes_json', # JSON keys to ignore when reading complete JSON files. Only leafs of this tree structure are ignored. ignore_json_keys: { # This should only duplicate the real configuration from the recipes, and it adds a lot of IP ranges that can be ignored. 'network' => nil, # Contains simple network definition. Not a connection in itself. 'policy_xae_outproxy' => { 'local_network' => nil }, # Contains DNS entries. Not a connection in itself. 'policy_xae_xx_cdh' => { 'dns' => nil }, # This contains firewall rules, therefore representing who connects on the host, and not who the host connects to. 'policy_xae_xx_iptables' => nil, # Contains the allowed network range. Not a connection in itself. 'postfix' => { 'main' => { 'mynetworks' => nil } }, # This contains sometime IP addresses in the key comments 'site_directory' => nil, # This contains firewall rules, therefore representing who connects on the host, and not who the host connects to. 'site_iptables' => nil, # This contains some user names having IP addresses inside 'site_xx_roles' => nil, # This stores routes for all Proxmox instances. 'pve' => { 'vlan' => { 'routes' => nil } } }, # JSON keys to ignore when reading complete JSON files, whatever their position ignore_any_json_keys: [ # Those contain cache of MAC addresses to IP addresses 'arp', # Those contain broadcast IP addresses 'broadcast', # Those contain firewall rules, therefore representing who connects on the host, and not who the host connects to. 'firewall', # Those contain version numbers with same format as IP addresses 'version' ], # IPs to ignore while parsing complete JSON files ignore_ips: [ /^0\./, /^127\./, /^255\./ ], # Maximum level of recursion while building the graph of connected nodes (nil = no limit). connections_max_level: nil, # Maximum label length for a link max_link_label_length: 128 } end |
Instance Method Details
#ancestor_nodes(nodes_list) ⇒ Object
Return the list of nodes and ancestors of a given list of nodes, recursively. An ancestor of a node is another node connected to it, or to a group including it. An ancestor of a node can be:
-
Another node connected to it.
-
Another node including it.
- Parameters
-
nodes_list (Array<String>): List of nodes for which we look for ancestors.
- Result
-
Array<String>: List of ancestor nodes.
321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 321 def ancestor_nodes(nodes_list) ancestor_nodes_list = [] @nodes_graph.each do |node_name, node_info| ancestor_nodes_list << node_name if !nodes_list.include?(node_name) && (!(node_info[:connections].keys & nodes_list).empty? || !(node_info[:includes] & nodes_list).empty?) end if ancestor_nodes_list.empty? nodes_list else ancestor_nodes(nodes_list + ancestor_nodes_list) end end |
#available_plugins ⇒ Object
Get the list of available plugins
- Result
-
Array<Symbol>: List of plugins
221 222 223 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 221 def available_plugins @plugins.keys end |
#children_nodes(nodes_list) ⇒ Object
Return the list of nodes and children of a given list of nodes, recursively. A child of a node is another node connected to it, or to a group including it. A child of a node can be:
-
Another node that it connects to.
-
Another node that it includes.
- Parameters
-
nodes_list (Array<String>): List of nodes for which we look for children.
- Result
-
Array<String>: List of children nodes.
343 344 345 346 347 348 349 350 351 352 353 354 355 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 343 def children_nodes(nodes_list) children_nodes_list = [] nodes_list.each do |node_name| children_nodes_list.concat(@nodes_graph[node_name][:connections].keys + @nodes_graph[node_name][:includes]) end children_nodes_list.uniq! new_children_nodes = children_nodes_list - nodes_list if new_children_nodes.empty? children_nodes_list else children_nodes(children_nodes_list) end end |
#cluster_nodes ⇒ Object
Return the list of nodes that are clusters
- Result
-
Array<String>: List of cluster nodes
361 362 363 364 365 366 367 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 361 def cluster_nodes cluster_nodes_list = [] @nodes_graph.each do |node_name, node_info| cluster_nodes_list << node_name if node_info[:type] == :cluster end cluster_nodes_list end |
#collapse_nodes(nodes_list) ⇒ Object
Collapse a given list of nodes.
- Parameters
-
nodes_list (Array<String>): List of nodes to collapse
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 268 def collapse_nodes(nodes_list) nodes_list.each do |node_name_to_collapse| included_nodes = @nodes_graph[node_name_to_collapse][:includes] # First collapse its included nodes if any collapse_nodes(included_nodes) # Then collapse this one collapsed_connections = {} included_nodes.each do |included_node_name| collapsed_connections.merge!(@nodes_graph[included_node_name][:connections]) { |_connected_node, labels1, labels2| (labels1 + labels2).uniq } end @nodes_graph[node_name_to_collapse][:connections] = collapsed_connections @nodes_graph[node_name_to_collapse][:includes] = [] replace_nodes(included_nodes, node_name_to_collapse) end end |
#define_clusters_ip_24 ⇒ Object
Define clusters of ips with 24 bits ranges.
301 302 303 304 305 306 307 308 309 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 301 def define_clusters_ip_24 @nodes_graph.keys.each do |node_name| if @nodes_graph[node_name][:type] == :node && !@node_metadata[node_name][:private_ips].nil? && !@node_metadata[node_name][:private_ips].empty? ip_24 = "#{@node_metadata[node_name][:private_ips].first.split('.')[0..2].join('.')}.0/24" @nodes_graph[ip_24] = ip_range_graph_info(ip_24) unless @nodes_graph.key?(ip_24) @nodes_graph[ip_24][:includes] << node_name unless @nodes_graph[ip_24][:includes].include?(node_name) end end end |
#description_for(node_name) ⇒ Object
Get the description of a given node
- Parameters
-
node_name (String): Node name
- Result
-
String: Node description, or nil if none
545 546 547 548 549 550 551 552 553 554 555 556 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 545 def description_for(node_name) require 'byebug' byebug if node_name == 'xaesbghad51' case @nodes_graph[node_name][:type] when :node @node_metadata[node_name][:description] when :cluster nil when :unknown nil end end |
#dump_outputs ⇒ Object
Dump the graph in the desired outputs
209 210 211 212 213 214 215 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 209 def dump_outputs @outputs.each do |(format, file_name)| section "Write #{format} file #{file_name}" do write_graph(file_name, format) end end end |
#filter_in_nodes(nodes_list) ⇒ Object
Remove from the graph any node that is not part of a given list
- Parameters
-
nodes_list (Array<String>): List of nodes to keep
373 374 375 376 377 378 379 380 381 382 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 373 def filter_in_nodes(nodes_list) new_nodes_graph = {} @nodes_graph.each do |node_name, node_info| new_nodes_graph[node_name] = node_info.merge( connections: node_info[:connections].select { |connected_hostname, _labels| nodes_list.include?(connected_hostname) }, includes: node_info[:includes] & nodes_list ) if nodes_list.include?(node_name) end @nodes_graph = new_nodes_graph end |
#filter_out_nodes(nodes_list) ⇒ Object
Remove from the graph any node that is part of a given list
- Parameters
-
nodes_list (Array<String>): List of nodes to remove
388 389 390 391 392 393 394 395 396 397 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 388 def filter_out_nodes(nodes_list) new_nodes_graph = {} @nodes_graph.each do |node_name, node_info| new_nodes_graph[node_name] = node_info.merge( connections: node_info[:connections].select { |connected_hostname, _labels| !nodes_list.include?(connected_hostname) }, includes: node_info[:includes] - nodes_list ) unless nodes_list.include?(node_name) end @nodes_graph = new_nodes_graph end |
#force_cluster_strict_hierarchy ⇒ Object
Make sure clusters follow a strict hierarchy and that 1 node belongs to at most 1 cluster.
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 425 def force_cluster_strict_hierarchy # Find the nodes belonging to several clusters. loop do # First cluster found each node name # Hash<String, String > cluster_per_node = {} conflicting_clusters = nil @nodes_graph.each do |node_name, node_info| node_info[:includes].each do |included_node_name| if cluster_per_node.key?(included_node_name) # Found a conflict between 2 clusters conflicting_clusters = [node_name, cluster_per_node[included_node_name]] log_error "Node #{included_node_name} found in both clusters #{node_name} and #{cluster_per_node[included_node_name]}" break else cluster_per_node[included_node_name] = node_name end end break unless conflicting_clusters.nil? end if conflicting_clusters.nil? break else # We have conflicting clusters to resolve cluster_1, cluster_2 = conflicting_clusters c1_belongs_to_c2 = @nodes_graph[cluster_1][:includes].all? { |cluster_1_node_name| @nodes_graph[cluster_2][:includes_proc].call(cluster_1_node_name) } c2_belongs_to_c1 = @nodes_graph[cluster_2][:includes].all? { |cluster_2_node_name| @nodes_graph[cluster_1][:includes_proc].call(cluster_2_node_name) } if c1_belongs_to_c2 if c2_belongs_to_c1 # Both clusters have the same nodes if @nodes_graph[cluster_1][:includes_proc].call(cluster_2) @nodes_graph[cluster_2][:includes] = (@nodes_graph[cluster_1][:includes] + @nodes_graph[cluster_2][:includes]).uniq @nodes_graph[cluster_1][:includes] = [cluster_2] else @nodes_graph[cluster_1][:includes] = (@nodes_graph[cluster_1][:includes] + @nodes_graph[cluster_2][:includes]).uniq @nodes_graph[cluster_2][:includes] = [cluster_1] end else # All nodes of cluster_1 belong to cluster_2, but some nodes of cluster_2 don't belong to cluster_1 @nodes_graph[cluster_2][:includes] = @nodes_graph[cluster_2][:includes] - @nodes_graph[cluster_1][:includes] + [cluster_1] end elsif c2_belongs_to_c1 # All nodes of cluster_2 belong to cluster_1, but some nodes of cluster_1 don't belong to cluster_2 @nodes_graph[cluster_1][:includes] = @nodes_graph[cluster_1][:includes] - @nodes_graph[cluster_2][:includes] + [cluster_2] else # cluster_1 and cluster_2 have to be merged new_cluster_name = "#{cluster_1}_&_#{cluster_2}" # Store thos proc in those variables as the cluster_1 and cluster_2 references are going to be removed includes_proc_1 = @nodes_graph[cluster_1][:includes_proc] includes_proc_2 = @nodes_graph[cluster_2][:includes_proc] @nodes_graph[new_cluster_name] = { type: :cluster, includes: (@nodes_graph[cluster_1][:includes] + @nodes_graph[cluster_2][:includes]).uniq, connections: @nodes_graph[cluster_1][:connections].merge!(@nodes_graph[cluster_2][:connections]) { |_connected_node, labels1, labels2| (labels1 + labels2).uniq }, includes_proc: proc do |hostname| includes_proc_1.call(hostname) || includes_proc_2.call(hostname) end } replace_nodes([cluster_1, cluster_2], new_cluster_name) end end end end |
#get_json_files ⇒ Object
Generate the JSON files to be used
200 201 202 203 204 205 206 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 200 def get_json_files unless @skip_run @json_dumper.dump_dir = @config[:json_files_dir] # Generate all the jsons, even if 1 hostname is given, as it might be useful for the rest of the graph. @json_dumper.dump_json_for(@nodes_handler.known_nodes) end end |
#graph_for(hostnames) ⇒ Object
Add to the graph a given set of hostnames and their connected nodes.
- Parameters
-
hostnames (Array<String>): List of hostnames
229 230 231 232 233 234 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 229 def graph_for(hostnames) # Parse connections from JSON files hostnames.each do |hostname| parse_connections_for(hostname, @config[:connections_max_level]) end end |
#graph_for_nodes_lists(nodes_lists, only_add_cluster: false) ⇒ Object
Add to the graph a given set of nodes lists and their connected nodes.
- Parameters
-
nodes_lists (Array<String>): List of nodes lists
-
only_add_cluster (Boolean): If true, then don’t add missing nodes from this graph to the graph [default = false]
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 241 def graph_for_nodes_lists(nodes_lists, only_add_cluster: false) nodes_lists.each do |nodes_list| hosts_list = @nodes_handler.select_nodes(@nodes_handler.nodes_from_list(nodes_list)) if only_add_cluster # Select only the hosts list we know about hosts_list.select! { |hostname| @nodes_graph.key?(hostname) } else # Parse JSON for all the hosts of this cluster hosts_list.each do |hostname| parse_connections_for(hostname, @config[:connections_max_level]) end end @nodes_graph[nodes_list] = { type: :cluster, connections: {}, includes: [], includes_proc: proc { |node_name| hosts_list.include?(node_name) } } unless @nodes_graph.key?(nodes_list) @nodes_graph[nodes_list][:includes].concat(hosts_list) @nodes_graph[nodes_list][:includes].uniq! end end |
#is_node_cluster?(node_name) ⇒ Boolean
Is the node represented as a cluster?
- Parameters
-
node_name (String): Node name
- Result
-
Boolean: Is the node represented as a cluster?
495 496 497 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 495 def is_node_cluster?(node_name) @nodes_graph[node_name][:type] == :cluster || !@nodes_graph[node_name][:includes].empty? end |
#is_node_physical?(node_name) ⇒ Boolean
Is the node a physical node?
- Parameters
-
node_name (String): Node name
- Result
-
Boolean: Is the node a physical node?
505 506 507 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 505 def is_node_physical?(node_name) @nodes_graph[node_name][:type] == :node && @node_metadata[node_name][:physical_node] end |
#options_parse(options_parser) ⇒ Object
Complete an option parser with ways to tune the topographer
- Parameters
-
options_parser (OptionParser): The option parser to complete
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 150 def () from_hosts_opts_parser = OptionParser.new do |opts| @nodes_handler.(opts, @from_hosts) end to_hosts_opts_parser = OptionParser.new do |opts| @nodes_handler.(opts, @to_hosts) end .separator '' .separator 'Topographer options:' .on('-F', '--from HOSTS_OPTIONS', 'Specify options for the set of nodes to start from (enclose them with ""). Default: all nodes. HOSTS_OPTIONS follows the following:', *from_hosts_opts_parser.to_s.split("\n")[3..-1]) do || args = .split(' ') from_hosts_opts_parser.parse!(args) raise "Unknown --from options: #{args.join(' ')}" unless args.empty? end .on('-k', '--skip-run', "Skip the actual gathering of JSON node files. If set, the current files in #{@config[:json_files_dir]} will be used.") do @skip_run = true end .on('-p', '--output FORMAT:FILE_NAME', "Specify a format and file name. Can be used several times. FORMAT can be one of #{available_plugins.sort.join(', ')}. Ex.: graphviz:graph.gv") do |output| format_str, file_name = output.split(':') format = format_str.to_sym raise "Unknown format: #{format}." unless available_plugins.include?(format) @outputs << [format, file_name] end .on('-T', '--to HOSTS_OPTIONS', 'Specify options for the set of nodes to get to (enclose them with ""). Default: all nodes. HOSTS_OPTIONS follows the following:', *to_hosts_opts_parser.to_s.split("\n")[3..-1]) do || args = .split(' ') to_hosts_opts_parser.parse!(args) raise "Unknown --to options: #{args.join(' ')}" unless args.empty? end end |
#remove_empty_clusters ⇒ Object
Remove empty clusters
292 293 294 295 296 297 298 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 292 def remove_empty_clusters loop do empty_clusters = @nodes_graph.keys.select { |node_name| @nodes_graph[node_name][:type] == :cluster && @nodes_graph[node_name][:includes].empty? } break if empty_clusters.empty? filter_out_nodes(empty_clusters) end end |
#remove_self_connections ⇒ Object
Remove self connections.
285 286 287 288 289 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 285 def remove_self_connections @nodes_graph.each do |node_name, node_info| node_info[:connections].delete_if { |connected_node_name, _labels| connected_node_name == node_name } end end |
#replace_nodes(nodes_to_be_replaced, replacement_node) ⇒ Object
Replace a list of nodes by a given node.
- Parameters
-
nodes_to_be_replaced (Array<String>): Nodes to be replaced
-
replacement_node (String): Node that is used for replacement
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 404 def replace_nodes(nodes_to_be_replaced, replacement_node) # Delete references to the nodes to be replaced @nodes_graph.delete_if { |node_name, _node_info| nodes_to_be_replaced.include?(node_name) } # Change any connection or inclusions using nodes to be replaced @nodes_graph.each do |node_name, node_info| node_info[:includes] = node_info[:includes].map { |included_node_name| nodes_to_be_replaced.include?(included_node_name) ? replacement_node : included_node_name }.uniq new_connections = {} node_info[:connections].each do |connected_node_name, labels| if nodes_to_be_replaced.include?(connected_node_name) new_connections[replacement_node] = [] unless new_connections.key?(replacement_node) new_connections[replacement_node].concat(labels) new_connections[replacement_node].uniq! else new_connections[connected_node_name] = labels end end node_info[:connections] = new_connections end end |
#resolve_from_to ⇒ Object
Resolve the from and to hosts descriptions
- Result
-
Array<String>: The from hostnames
-
Array<String>: The to hostnames
190 191 192 193 194 195 196 197 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 190 def resolve_from_to @from_hosts << { all: true } if @from_hosts.empty? @to_hosts << { all: true } if @to_hosts.empty? [ @nodes_handler.select_nodes(@from_hosts), @nodes_handler.select_nodes(@to_hosts) ] end |
#title_for(node_name) ⇒ Object
Get the title of a given node
- Parameters
-
node_name (String): Node name
- Result
-
String: Node title
528 529 530 531 532 533 534 535 536 537 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 528 def title_for(node_name) case @nodes_graph[node_name][:type] when :node "#{node_name} - #{@node_metadata[node_name][:private_ips].nil? || @node_metadata[node_name][:private_ips].empty? ? 'No IP' : @node_metadata[node_name][:private_ips].first}" when :cluster "#{node_name} (#{@nodes_graph[node_name][:includes].size} nodes)" when :unknown "#{node_name} - Unknown node" end end |
#validate_params ⇒ Object
Validate that parsed parameters are valid
181 182 183 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 181 def validate_params raise 'No output defined. Please use --output option.' if @outputs.empty? end |
#write_graph(file_name, output_format) ⇒ Object
Output the graph to a given file at a given format
- Parameters
-
file_name (String): File name to output to.
-
output_format (Symbol): Output format to use (should be part of the plugins).
514 515 516 517 518 519 520 |
# File 'lib/hybrid_platforms_conductor/topographer.rb', line 514 def write_graph(file_name, output_format) if @plugins.key?(output_format) @plugins[output_format].new(self).write_graph(file_name) else raise "Unknown topographer plugin #{output_format}" end end |