hybrid_platforms_conductor 32.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/check-node +24 -0
- data/bin/deploy +12 -0
- data/bin/dump_nodes_json +12 -0
- data/bin/free_ips +23 -0
- data/bin/free_veids +17 -0
- data/bin/get_impacted_nodes +43 -0
- data/bin/last_deploys +56 -0
- data/bin/nodes_to_deploy +104 -0
- data/bin/report +10 -0
- data/bin/run +39 -0
- data/bin/setup +11 -0
- data/bin/ssh_config +14 -0
- data/bin/test +13 -0
- data/bin/topograph +54 -0
- data/lib/hybrid_platforms_conductor/action.rb +82 -0
- data/lib/hybrid_platforms_conductor/actions_executor.rb +307 -0
- data/lib/hybrid_platforms_conductor/bitbucket.rb +123 -0
- data/lib/hybrid_platforms_conductor/cmd_runner.rb +188 -0
- data/lib/hybrid_platforms_conductor/cmdb.rb +34 -0
- data/lib/hybrid_platforms_conductor/common_config_dsl/bitbucket.rb +78 -0
- data/lib/hybrid_platforms_conductor/common_config_dsl/confluence.rb +43 -0
- data/lib/hybrid_platforms_conductor/common_config_dsl/file_system_tests.rb +110 -0
- data/lib/hybrid_platforms_conductor/common_config_dsl/idempotence_tests.rb +38 -0
- data/lib/hybrid_platforms_conductor/config.rb +263 -0
- data/lib/hybrid_platforms_conductor/confluence.rb +119 -0
- data/lib/hybrid_platforms_conductor/connector.rb +84 -0
- data/lib/hybrid_platforms_conductor/credentials.rb +127 -0
- data/lib/hybrid_platforms_conductor/current_dir_monitor.rb +42 -0
- data/lib/hybrid_platforms_conductor/deployer.rb +598 -0
- data/lib/hybrid_platforms_conductor/executable.rb +145 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/bash.rb +44 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/interactive.rb +44 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/my_action.rb.sample +79 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +63 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/ruby.rb +69 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/scp.rb +61 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/config.rb +78 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_ip.rb +104 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_keys.rb +114 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/my_cmdb.rb.sample +129 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/platform_handlers.rb +66 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +156 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +702 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/platform_handler_plugin.rb.sample +292 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/docker.rb +148 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/my_provisioner.rb.sample +103 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/podman.rb +125 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +522 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/proxmox_waiter.rb +707 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/reserve_proxmox_container +122 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +69 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/mediawiki.rb +164 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/my_report_plugin.rb.sample +88 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/stdout.rb +61 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/templates/confluence_inventory.html.erb +33 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +137 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/can_be_checked.rb +21 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +112 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_from_scratch.rb +35 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/connection.rb +28 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_freshness.rb +44 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_from_scratch.rb +36 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +49 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/divergence.rb +25 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/executables.rb +46 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system.rb +45 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system_hdfs.rb +45 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +25 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/idempotence.rb +77 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +38 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +56 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +54 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/linear_strategy.rb +47 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +82 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +120 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/my_test_plugin.rb.sample +143 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +74 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/ports.rb +85 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/private_ips.rb +38 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/public_ips.rb +38 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre-meltdown-checker.sh +1930 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +56 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/veids.rb +31 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +159 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +122 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/my_test_report.rb.sample +48 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/stdout.rb +120 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/_confluence_errors_status.html.erb +46 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/_confluence_gauge.html.erb +49 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/confluence.html.erb +242 -0
- data/lib/hybrid_platforms_conductor/io_router.rb +70 -0
- data/lib/hybrid_platforms_conductor/json_dumper.rb +88 -0
- data/lib/hybrid_platforms_conductor/logger_helpers.rb +319 -0
- data/lib/hybrid_platforms_conductor/mutex_dir +76 -0
- data/lib/hybrid_platforms_conductor/nodes_handler.rb +597 -0
- data/lib/hybrid_platforms_conductor/parallel_threads.rb +97 -0
- data/lib/hybrid_platforms_conductor/platform_handler.rb +188 -0
- data/lib/hybrid_platforms_conductor/platforms_handler.rb +118 -0
- data/lib/hybrid_platforms_conductor/plugin.rb +53 -0
- data/lib/hybrid_platforms_conductor/plugins.rb +101 -0
- data/lib/hybrid_platforms_conductor/provisioner.rb +181 -0
- data/lib/hybrid_platforms_conductor/report.rb +31 -0
- data/lib/hybrid_platforms_conductor/reports_handler.rb +84 -0
- data/lib/hybrid_platforms_conductor/services_handler.rb +274 -0
- data/lib/hybrid_platforms_conductor/test.rb +141 -0
- data/lib/hybrid_platforms_conductor/test_by_service.rb +22 -0
- data/lib/hybrid_platforms_conductor/test_report.rb +282 -0
- data/lib/hybrid_platforms_conductor/tests_runner.rb +590 -0
- data/lib/hybrid_platforms_conductor/thycotic.rb +92 -0
- data/lib/hybrid_platforms_conductor/topographer.rb +859 -0
- data/lib/hybrid_platforms_conductor/topographer/plugin.rb +20 -0
- data/lib/hybrid_platforms_conductor/topographer/plugins/graphviz.rb +127 -0
- data/lib/hybrid_platforms_conductor/topographer/plugins/json.rb +72 -0
- data/lib/hybrid_platforms_conductor/topographer/plugins/my_topographer_output_plugin.rb.sample +37 -0
- data/lib/hybrid_platforms_conductor/topographer/plugins/svg.rb +30 -0
- data/lib/hybrid_platforms_conductor/version.rb +5 -0
- data/spec/hybrid_platforms_conductor_test.rb +159 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/bash_spec.rb +43 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/interactive_spec.rb +18 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +102 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/ruby_spec.rb +108 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/scp_spec.rb +79 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions_spec.rb +199 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connection_spec.rb +212 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/cli_options_spec.rb +125 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/config_dsl_spec.rb +50 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connectable_nodes_spec.rb +28 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +448 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +313 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/node_helpers_spec.rb +32 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +134 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/logging_spec.rb +256 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/parallel_spec.rb +338 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/timeout_spec.rb +101 -0
- data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +165 -0
- data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +238 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/check_spec.rb +9 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/deploy_spec.rb +243 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/parse_deploy_output_spec.rb +104 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioner_spec.rb +131 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/docker/Dockerfile +10 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/docker_spec.rb +123 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/podman_spec.rb +211 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/config_dsl_spec.rb +126 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/create_spec.rb +290 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/destroy_spec.rb +43 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/ip_spec.rb +60 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/proxmox.json +3 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/destroy_vm_spec.rb +82 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/expired_containers_spec.rb +786 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/ips_assignment_spec.rb +112 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/other_lxc_containers_resources_spec.rb +190 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/pve_node_resources_spec.rb +200 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/retries_spec.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/vm_ids_assignment_spec.rb +67 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/start_spec.rb +79 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/state_spec.rb +28 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/stop_spec.rb +41 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/config_spec.rb +33 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_ip_spec.rb +64 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_keys_spec.rb +133 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/platform_handlers_spec.rb +19 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs_plugins_api_spec.rb +446 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/common_spec.rb +127 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/git_diff_impacts_spec.rb +318 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/nodes_selectors_spec.rb +132 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/platform_handlers_plugins_api_spec.rb +60 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/several_platforms_spec.rb +58 -0
- data/spec/hybrid_platforms_conductor_test/api/platform_handler_spec.rb +97 -0
- data/spec/hybrid_platforms_conductor_test/api/platforms_handler_spec.rb +104 -0
- data/spec/hybrid_platforms_conductor_test/api/plugins_spec.rb +243 -0
- data/spec/hybrid_platforms_conductor_test/api/reports_handler_spec.rb +44 -0
- data/spec/hybrid_platforms_conductor_test/api/services_handler/actions_to_deploy_spec.rb +121 -0
- data/spec/hybrid_platforms_conductor_test/api/services_handler/deploy_allowed_spec.rb +142 -0
- data/spec/hybrid_platforms_conductor_test/api/services_handler/log_info_spec.rb +101 -0
- data/spec/hybrid_platforms_conductor_test/api/services_handler/package_spec.rb +388 -0
- data/spec/hybrid_platforms_conductor_test/api/services_handler/parse_deploy_output_spec.rb +274 -0
- data/spec/hybrid_platforms_conductor_test/api/services_handler/prepare_for_deploy_spec.rb +264 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/common_spec.rb +194 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/global_spec.rb +37 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_check_spec.rb +194 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_spec.rb +137 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_ssh_spec.rb +257 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/platform_spec.rb +110 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/reports_spec.rb +367 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/bitbucket_conf_spec.rb +111 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_reports_plugins/confluence_spec.rb +29 -0
- data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb.rb +166 -0
- data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb2.rb +93 -0
- data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb_others.rb +60 -0
- data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb_others2.rb +58 -0
- data/spec/hybrid_platforms_conductor_test/executables/check-node_spec.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/executables/deploy_spec.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/executables/get_impacted_nodes_spec.rb +158 -0
- data/spec/hybrid_platforms_conductor_test/executables/last_deploys_spec.rb +173 -0
- data/spec/hybrid_platforms_conductor_test/executables/nodes_to_deploy_spec.rb +283 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/actions_executor_spec.rb +28 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/cmd_runner_spec.rb +28 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/common_spec.rb +67 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/deployer_spec.rb +251 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/nodes_handler_spec.rb +111 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/nodes_selectors_spec.rb +71 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/reports_handler_spec.rb +54 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/tests_runner_spec.rb +139 -0
- data/spec/hybrid_platforms_conductor_test/executables/report_spec.rb +60 -0
- data/spec/hybrid_platforms_conductor_test/executables/run_spec.rb +173 -0
- data/spec/hybrid_platforms_conductor_test/executables/ssh_config_spec.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/executables/test_spec.rb +41 -0
- data/spec/hybrid_platforms_conductor_test/helpers/actions_executor_helpers.rb +98 -0
- data/spec/hybrid_platforms_conductor_test/helpers/cmd_runner_helpers.rb +92 -0
- data/spec/hybrid_platforms_conductor_test/helpers/cmdb_helpers.rb +37 -0
- data/spec/hybrid_platforms_conductor_test/helpers/config_helpers.rb +20 -0
- data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +130 -0
- data/spec/hybrid_platforms_conductor_test/helpers/deployer_helpers.rb +149 -0
- data/spec/hybrid_platforms_conductor_test/helpers/deployer_test_helpers.rb +812 -0
- data/spec/hybrid_platforms_conductor_test/helpers/executables_helpers.rb +96 -0
- data/spec/hybrid_platforms_conductor_test/helpers/nodes_handler_helpers.rb +20 -0
- data/spec/hybrid_platforms_conductor_test/helpers/platform_handler_helpers.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/helpers/platforms_handler_helpers.rb +127 -0
- data/spec/hybrid_platforms_conductor_test/helpers/plugins_helpers.rb +48 -0
- data/spec/hybrid_platforms_conductor_test/helpers/provisioner_proxmox_helpers.rb +789 -0
- data/spec/hybrid_platforms_conductor_test/helpers/reports_handler_helpers.rb +29 -0
- data/spec/hybrid_platforms_conductor_test/helpers/services_handler_helpers.rb +20 -0
- data/spec/hybrid_platforms_conductor_test/helpers/tests_runner_helpers.rb +38 -0
- data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem/hpc_plugins/test_plugin_type/test_plugin_id1.rb +22 -0
- data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem/hpc_plugins/test_plugin_type/test_plugin_id2.rb +22 -0
- data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem2/sub_dir/hpc_plugins/test_plugin_type/test_plugin_id3.rb +26 -0
- data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem2/sub_dir/hpc_plugins/test_plugin_type2/test_plugin_id4.rb +26 -0
- data/spec/hybrid_platforms_conductor_test/platform_handler_plugins/test.rb +225 -0
- data/spec/hybrid_platforms_conductor_test/platform_handler_plugins/test2.rb +11 -0
- data/spec/hybrid_platforms_conductor_test/report_plugin.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/test_action.rb +66 -0
- data/spec/hybrid_platforms_conductor_test/test_connector.rb +151 -0
- data/spec/hybrid_platforms_conductor_test/test_plugins/global.rb +30 -0
- data/spec/hybrid_platforms_conductor_test/test_plugins/node.rb +53 -0
- data/spec/hybrid_platforms_conductor_test/test_plugins/node_check.rb +47 -0
- data/spec/hybrid_platforms_conductor_test/test_plugins/node_ssh.rb +42 -0
- data/spec/hybrid_platforms_conductor_test/test_plugins/platform.rb +50 -0
- data/spec/hybrid_platforms_conductor_test/test_plugins/several_checks.rb +50 -0
- data/spec/hybrid_platforms_conductor_test/test_provisioner.rb +95 -0
- data/spec/hybrid_platforms_conductor_test/tests_report_plugin.rb +49 -0
- data/spec/spec_helper.rb +111 -0
- metadata +566 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
require 'colorize'
|
|
2
|
+
require 'logger'
|
|
3
|
+
require 'ruby-progressbar'
|
|
4
|
+
|
|
5
|
+
module HybridPlatformsConductor
|
|
6
|
+
|
|
7
|
+
# Gives easy logging methods to any class including this module, such as log_error, log_debug...
|
|
8
|
+
# Also define methods for UI (meaning text that should be displayed as interface, and not as logging).
|
|
9
|
+
module LoggerHelpers
|
|
10
|
+
|
|
11
|
+
# Small custom log device that can use a progress bar currently in use.
|
|
12
|
+
class ProgressBarLogDevice
|
|
13
|
+
|
|
14
|
+
# Constructor
|
|
15
|
+
#
|
|
16
|
+
# Parameters::
|
|
17
|
+
# * *progress_bar* (ProgressBar): The progress bar to be used for logging
|
|
18
|
+
# * *stream* (IO): Stream to be used for logging (like $stdout...)
|
|
19
|
+
def initialize(progress_bar, stream)
|
|
20
|
+
@progress_bar = progress_bar
|
|
21
|
+
@stream = stream
|
|
22
|
+
# Store the current line in case it wasn't finished by \n
|
|
23
|
+
@current_line = nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Write a message
|
|
27
|
+
#
|
|
28
|
+
# Parameters::
|
|
29
|
+
# * *msg* (String): Message to log
|
|
30
|
+
def write(msg)
|
|
31
|
+
if msg.end_with?("\n")
|
|
32
|
+
@progress_bar.clear
|
|
33
|
+
if @current_line.nil?
|
|
34
|
+
# New messages to be displayed
|
|
35
|
+
@stream << msg
|
|
36
|
+
else
|
|
37
|
+
# Ending previous line
|
|
38
|
+
@stream << (@current_line + msg)
|
|
39
|
+
@current_line = nil
|
|
40
|
+
end
|
|
41
|
+
@progress_bar.refresh(force: true) unless @progress_bar.stopped?
|
|
42
|
+
elsif @current_line.nil?
|
|
43
|
+
# Beginning new line
|
|
44
|
+
@current_line = msg
|
|
45
|
+
else
|
|
46
|
+
# Continuing current line
|
|
47
|
+
@current_line << msg
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Close the log device
|
|
52
|
+
# This method is needed for Ruby loggers to detect this class as a log device.
|
|
53
|
+
def close
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Make sure if the current line is not flushed we still do it
|
|
57
|
+
def flush
|
|
58
|
+
unless @current_line.nil?
|
|
59
|
+
@stream << @current_line
|
|
60
|
+
@current_line = nil
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class << self
|
|
67
|
+
attr_reader :progress_bar_semaphore
|
|
68
|
+
end
|
|
69
|
+
# Make sure the progress bar setting is protected by a Mutex
|
|
70
|
+
@progress_bar_semaphore = Mutex.new
|
|
71
|
+
|
|
72
|
+
# Sorted list of levels and their corresponding modifiers.
|
|
73
|
+
LEVELS_MODIFIERS = {
|
|
74
|
+
fatal: [:red, :bold],
|
|
75
|
+
error: [:red, :bold],
|
|
76
|
+
warn: [:yellow, :bold],
|
|
77
|
+
info: [:white],
|
|
78
|
+
debug: [:white],
|
|
79
|
+
unknown: [:white]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# List of levels that will output on stderr
|
|
83
|
+
LEVELS_TO_STDERR = %i[warn error fatal]
|
|
84
|
+
|
|
85
|
+
LEVELS_MODIFIERS.keys.each do |level|
|
|
86
|
+
define_method("log_#{level}") do |message|
|
|
87
|
+
(LEVELS_TO_STDERR.include?(level) ? @logger_stderr : @logger).send(
|
|
88
|
+
level,
|
|
89
|
+
defined?(@log_component) ? @log_component : self.class.name.split('::').last
|
|
90
|
+
) { message }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Initialize loggers
|
|
95
|
+
#
|
|
96
|
+
# Parameters::
|
|
97
|
+
# * *logger* (Logger): Logger used for stdout
|
|
98
|
+
# * *logger_stderr* (Logger): Logger used for stderr
|
|
99
|
+
def init_loggers(logger, logger_stderr)
|
|
100
|
+
@logger = logger
|
|
101
|
+
@logger_stderr = logger_stderr
|
|
102
|
+
set_loggers_format
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Set loggers to the desired format
|
|
106
|
+
def set_loggers_format
|
|
107
|
+
[@logger, @logger_stderr].each do |logger|
|
|
108
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
|
109
|
+
# If the message already has control characters, don't colorize it
|
|
110
|
+
keep_original_color = msg.include? "\u001b"
|
|
111
|
+
message = "[#{Time.now.utc.strftime('%F %T')} (PID #{$$} / TID #{Thread.current.object_id})] #{severity.rjust(5)} - [ #{progname} ] - "
|
|
112
|
+
message << "#{msg}\n" unless keep_original_color
|
|
113
|
+
LEVELS_MODIFIERS[severity.downcase.to_sym].each do |modifier|
|
|
114
|
+
message = message.send(modifier)
|
|
115
|
+
end
|
|
116
|
+
message << "#{msg}\n" if keep_original_color
|
|
117
|
+
message
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Set log level
|
|
123
|
+
#
|
|
124
|
+
# Parameters::
|
|
125
|
+
# * *level* (Symbol): Log level (used directly with the logger)
|
|
126
|
+
def log_level=(level)
|
|
127
|
+
@logger.level = level
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Are we in debug level?
|
|
131
|
+
#
|
|
132
|
+
# Result::
|
|
133
|
+
# * Boolean: Are we in debug level?
|
|
134
|
+
def log_debug?
|
|
135
|
+
@logger.debug?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Set the logging component name, to be prepend in any log message, or nil if none.
|
|
139
|
+
# By default the component is the class name.
|
|
140
|
+
#
|
|
141
|
+
# Parameters::
|
|
142
|
+
# * *component* (String or nil): Logging component, or nil for none
|
|
143
|
+
def log_component=(component)
|
|
144
|
+
@log_component = component
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Print a string to the command-line UI.
|
|
148
|
+
# This is different from logging because this is the UI of the CLI.
|
|
149
|
+
# Use sections indentation for better clarity.
|
|
150
|
+
#
|
|
151
|
+
# Parameters::
|
|
152
|
+
# * *message* (String): Message to be printed out [default = '']
|
|
153
|
+
def out(message = '')
|
|
154
|
+
@out_sections = [] unless defined?(@out_sections)
|
|
155
|
+
message = "#{' ' * @out_sections.size}#{message}"
|
|
156
|
+
# log_debug "<Output> - #{message}"
|
|
157
|
+
message = "#{message}\n" unless message.end_with?("\n")
|
|
158
|
+
@logger << message
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Print an error string to the command-line UI.
|
|
162
|
+
# This is different from logging because this is the UI of the CLI.
|
|
163
|
+
# Use sections indentation for better clarity.
|
|
164
|
+
#
|
|
165
|
+
# Parameters::
|
|
166
|
+
# * *message* (String): Message to be printed out [default = '']
|
|
167
|
+
def err(message = '')
|
|
168
|
+
@out_sections = [] unless defined?(@out_sections)
|
|
169
|
+
message = "#{' ' * @out_sections.size}#{message}"
|
|
170
|
+
# log_debug "<Output> - #{message}"
|
|
171
|
+
message = "#{message}\n" unless message.end_with?("\n")
|
|
172
|
+
@logger_stderr << message
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Display a new section in the UI, used to group a set of operations
|
|
176
|
+
#
|
|
177
|
+
# Parameters::
|
|
178
|
+
# * *name* (String): Section name
|
|
179
|
+
# * Proc: Code called in the section
|
|
180
|
+
def section(name)
|
|
181
|
+
out "===== #{name} ==== Begin..."
|
|
182
|
+
@out_sections = [] unless defined?(@out_sections)
|
|
183
|
+
@out_sections << name
|
|
184
|
+
begin
|
|
185
|
+
yield
|
|
186
|
+
ensure
|
|
187
|
+
@out_sections.pop
|
|
188
|
+
out "===== #{name} ==== ...End"
|
|
189
|
+
out
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Get the stdout device
|
|
194
|
+
#
|
|
195
|
+
# Result::
|
|
196
|
+
# * Object: The stdout log device
|
|
197
|
+
def stdout_device
|
|
198
|
+
# TODO: Find a more elegant way to access the internal log device
|
|
199
|
+
@logger.instance_variable_get(:@logdev).dev
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Set the stdout device
|
|
203
|
+
#
|
|
204
|
+
# Parameters::
|
|
205
|
+
# * *log_device* (Object): The stdout log device to set
|
|
206
|
+
def stdout_device=(log_device)
|
|
207
|
+
# TODO: Find a more elegant way to access the internal log device
|
|
208
|
+
@logger.instance_variable_get(:@logdev).send(:set_dev, log_device)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Get the stderr device
|
|
212
|
+
#
|
|
213
|
+
# Result::
|
|
214
|
+
# * IO or String: The stdout IO or file name
|
|
215
|
+
def stderr_device
|
|
216
|
+
# TODO: Find a more elegant way to access the internal log device
|
|
217
|
+
@logger_stderr.instance_variable_get(:@logdev).dev
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Set the stderr device
|
|
221
|
+
#
|
|
222
|
+
# Parameters::
|
|
223
|
+
# * *log_device* (Object): The stdout log device to set
|
|
224
|
+
def stderr_device=(log_device)
|
|
225
|
+
# TODO: Find a more elegant way to access the internal log device
|
|
226
|
+
@logger_stderr.instance_variable_get(:@logdev).send(:set_dev, log_device)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Is stdout really getting to the terminal display?
|
|
230
|
+
#
|
|
231
|
+
# Result::
|
|
232
|
+
# * Boolean: Is stdout really getting to the terminal stdout?
|
|
233
|
+
def stdout_displayed?
|
|
234
|
+
log_device = stdout_device
|
|
235
|
+
log_device == $stdout || log_device == $stderr || log_device.is_a?(ProgressBarLogDevice)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Is stderr really getting to the terminal display?
|
|
239
|
+
#
|
|
240
|
+
# Result::
|
|
241
|
+
# * Boolean: Is stderr really getting to the terminal stdout?
|
|
242
|
+
def stderr_displayed?
|
|
243
|
+
log_device = stderr_device
|
|
244
|
+
log_device == $stderr || log_device == $stdout || log_device.is_a?(ProgressBarLogDevice)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Create a UI progress bar and call some code with it.
|
|
248
|
+
# Ensure logging done with the progress bar does not conflict in stdout.
|
|
249
|
+
#
|
|
250
|
+
# Parameters::
|
|
251
|
+
# * *nbr_total* (Integer): Total value of the progress bar
|
|
252
|
+
# * *name* (String or nil): Name to put on the progress bar, or nil for no name [default: nil]
|
|
253
|
+
# * Proc: Code block called with the progress bar
|
|
254
|
+
# * Parameters::
|
|
255
|
+
# * *progress_bar* (ProgressBar): The progress bar
|
|
256
|
+
def with_progress_bar(nbr_total, name: nil)
|
|
257
|
+
previous_stdout_device = nil
|
|
258
|
+
previous_stderr_device = nil
|
|
259
|
+
progress_bar = nil
|
|
260
|
+
LoggerHelpers.progress_bar_semaphore.synchronize do
|
|
261
|
+
stdout_log_device = stdout_device
|
|
262
|
+
progress_bar = ProgressBar.create(
|
|
263
|
+
output: stdout_log_device.is_a?(ProgressBarLogDevice) ? $stdout : stdout_log_device,
|
|
264
|
+
title: 'Initializing...',
|
|
265
|
+
total: nbr_total,
|
|
266
|
+
format: "#{name ? "#{name} " : ''}[%j%%] - |%bC%i| - [ %t ]",
|
|
267
|
+
progress_mark: ' ',
|
|
268
|
+
remainder_mark: '-'
|
|
269
|
+
)
|
|
270
|
+
if stdout_displayed? && !stdout_log_device.is_a?(ProgressBarLogDevice)
|
|
271
|
+
# Change the current logger so that when its logdev calls write it redirects to our ProgressBar#log
|
|
272
|
+
previous_stdout_device = stdout_device
|
|
273
|
+
self.stdout_device = ProgressBarLogDevice.new(progress_bar, previous_stdout_device)
|
|
274
|
+
end
|
|
275
|
+
if stderr_displayed? && !stderr_device.is_a?(ProgressBarLogDevice)
|
|
276
|
+
# Change the current logger so that when its logdev calls write it redirects to our ProgressBar#log
|
|
277
|
+
previous_stderr_device = stderr_device
|
|
278
|
+
self.stderr_device = ProgressBarLogDevice.new(progress_bar, previous_stderr_device)
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
begin
|
|
282
|
+
yield progress_bar
|
|
283
|
+
ensure
|
|
284
|
+
LoggerHelpers.progress_bar_semaphore.synchronize do
|
|
285
|
+
self.stdout_device.flush
|
|
286
|
+
self.stderr_device.flush
|
|
287
|
+
self.stdout_device = previous_stdout_device unless previous_stdout_device.nil?
|
|
288
|
+
self.stderr_device = previous_stderr_device unless previous_stderr_device.nil?
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Return a string describing the stdout and stderr if they were logged into files or StringIO.
|
|
294
|
+
# Useful for debugging.
|
|
295
|
+
#
|
|
296
|
+
# Result::
|
|
297
|
+
# * String: The corresponding stdout and stderr info, or nil if none
|
|
298
|
+
def stdouts_to_s
|
|
299
|
+
messages = []
|
|
300
|
+
{
|
|
301
|
+
'STDOUT' => self.stdout_device,
|
|
302
|
+
'STDERR' => self.stderr_device
|
|
303
|
+
}.each do |name, device|
|
|
304
|
+
if device.is_a?(File)
|
|
305
|
+
if File.exist?(device.path)
|
|
306
|
+
content = File.read(device.path).strip
|
|
307
|
+
messages << "----- #{name} BEGIN - #{device.path} -----\n#{content}\n----- #{name} END - #{device.path} -----" unless content.empty?
|
|
308
|
+
end
|
|
309
|
+
elsif device.is_a?(StringIO)
|
|
310
|
+
content = device.string
|
|
311
|
+
messages << "----- #{name} BEGIN -----\n#{content}\n----- #{name} END -----" unless content.empty?
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
messages.empty? ? nil : messages.join("\n")
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Grzegorz Wierzowiecki
|
|
3
|
+
# "mutex_dir.sh"
|
|
4
|
+
# GNU GPL or BSD - as you like
|
|
5
|
+
# Script inspiered by:
|
|
6
|
+
# http://www.bash-hackers.org/wiki/doku.php/howto/mutex
|
|
7
|
+
|
|
8
|
+
function print_help(){
|
|
9
|
+
cat << HELP_INFO
|
|
10
|
+
Usage:
|
|
11
|
+
$0 lock lock-dir calling_app_pid
|
|
12
|
+
$0 unlock lock-dir
|
|
13
|
+
Return:
|
|
14
|
+
0 - on success
|
|
15
|
+
1 - on general fail
|
|
16
|
+
2 - when locking failed
|
|
17
|
+
3 - when received signal
|
|
18
|
+
HELP_INFO
|
|
19
|
+
exit
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
# lock dirs/files
|
|
23
|
+
lock_dir="$2"
|
|
24
|
+
app_pid="$3"
|
|
25
|
+
pid_file="${lock_dir}/pid"
|
|
26
|
+
user_file="${lock_dir}/user"
|
|
27
|
+
|
|
28
|
+
# exit codes
|
|
29
|
+
ENO_SUCCESS=0
|
|
30
|
+
ENO_GENERAL=1
|
|
31
|
+
ENO_LOCKFAIL=2
|
|
32
|
+
ENO_RECVSIG=3
|
|
33
|
+
|
|
34
|
+
function lock(){
|
|
35
|
+
if ! kill -0 $app_pid &>/dev/null; then
|
|
36
|
+
echo 'Calling app pid (='$app_pid')is not responding.'
|
|
37
|
+
return 1
|
|
38
|
+
fi
|
|
39
|
+
local lock_dir="$1"
|
|
40
|
+
if mkdir "${lock_dir}" &>/dev/null; then
|
|
41
|
+
# lock succeeded, store the PID
|
|
42
|
+
echo "$app_pid" >"${pid_file}"
|
|
43
|
+
# Dump the user having this lock in another file
|
|
44
|
+
echo "${SUDO_USER}" >"${user_file}"
|
|
45
|
+
return ${ENO_SUCCESS}
|
|
46
|
+
else
|
|
47
|
+
# lock failed, now check if the other PID is alive
|
|
48
|
+
other_pid="$(cat "${pid_file}" 2>/dev/null)"
|
|
49
|
+
if [ $? != 0 ]; then
|
|
50
|
+
# Pid file does not exists - propably directory is beeing deleted
|
|
51
|
+
return ${ENO_LOCKFAIL}
|
|
52
|
+
fi
|
|
53
|
+
if ! kill -0 $other_pid &>/dev/null; then
|
|
54
|
+
# lock is stale, remove it and restart
|
|
55
|
+
unlock "$lock_dir"
|
|
56
|
+
lock "$lock_dir"
|
|
57
|
+
return $?
|
|
58
|
+
else
|
|
59
|
+
# lock is valid and OTHERPID is active - exit, we're locked!
|
|
60
|
+
#echo "lock failed, PID ${OTHERPID} is active" >&2
|
|
61
|
+
return ${ENO_LOCKFAIL}
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
return 0
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function unlock(){
|
|
68
|
+
rm -r "$1" &>/dev/null
|
|
69
|
+
return $?
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
case "$1" in
|
|
73
|
+
lock) lock "$lock_dir" ; exit $? ;;
|
|
74
|
+
unlock) unlock "$lock_dir"; exit $? ;;
|
|
75
|
+
*) print_help ;;
|
|
76
|
+
esac
|
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
require 'hybrid_platforms_conductor/cmd_runner'
|
|
2
|
+
require 'hybrid_platforms_conductor/cmdb'
|
|
3
|
+
require 'hybrid_platforms_conductor/logger_helpers'
|
|
4
|
+
require 'hybrid_platforms_conductor/parallel_threads'
|
|
5
|
+
require 'hybrid_platforms_conductor/platform_handler'
|
|
6
|
+
|
|
7
|
+
module HybridPlatformsConductor
|
|
8
|
+
|
|
9
|
+
# API to get information on our inventory: nodes and their metadata
|
|
10
|
+
class NodesHandler
|
|
11
|
+
|
|
12
|
+
# Extend the Config DSL
|
|
13
|
+
module ConfigDSLExtension
|
|
14
|
+
|
|
15
|
+
# List of CMDB masters. Each info has the following properties:
|
|
16
|
+
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule.
|
|
17
|
+
# * *cmdb_masters* (Hash< Symbol, Array<Symbol> >): List of metadata properties per CMDB name considered as master for those properties.
|
|
18
|
+
# Array< Hash<Symbol, Object> >
|
|
19
|
+
attr_reader :cmdb_masters
|
|
20
|
+
|
|
21
|
+
# Mixin initializer
|
|
22
|
+
def init_nodes_handler_config
|
|
23
|
+
@cmdb_masters = []
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Set CMDB masters
|
|
27
|
+
#
|
|
28
|
+
# Parameters::
|
|
29
|
+
# * *master_cmdbs_info* (Hash< Symbol, Symbol or Array<Symbol> >): List of metadata properties (or single one) per CMDB name considered as master for those properties.
|
|
30
|
+
def master_cmdbs(master_cmdbs_info)
|
|
31
|
+
@cmdb_masters << {
|
|
32
|
+
cmdb_masters: Hash[master_cmdbs_info.map { |cmdb, properties| [cmdb, properties.is_a?(Array) ? properties : [properties]] }],
|
|
33
|
+
nodes_selectors_stack: current_nodes_selectors_stack
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Config.extend_config_dsl_with ConfigDSLExtension, :init_nodes_handler_config
|
|
40
|
+
|
|
41
|
+
include LoggerHelpers, ParallelThreads
|
|
42
|
+
|
|
43
|
+
# Constructor
|
|
44
|
+
#
|
|
45
|
+
# Parameters::
|
|
46
|
+
# * *logger* (Logger): Logger to be used [default: Logger.new(STDOUT)]
|
|
47
|
+
# * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
|
|
48
|
+
# * *config* (Config): Config to be used. [default: Config.new]
|
|
49
|
+
# * *cmd_runner* (CmdRunner): Command executor to be used. [default: CmdRunner.new]
|
|
50
|
+
# * *platforms_handler* (PlatformsHandler): Platforms Handler to be used. [default: PlatformsHandler.new]
|
|
51
|
+
def initialize(
|
|
52
|
+
logger: Logger.new(STDOUT),
|
|
53
|
+
logger_stderr: Logger.new(STDERR),
|
|
54
|
+
config: Config.new,
|
|
55
|
+
cmd_runner: CmdRunner.new,
|
|
56
|
+
platforms_handler: PlatformsHandler.new
|
|
57
|
+
)
|
|
58
|
+
init_loggers(logger, logger_stderr)
|
|
59
|
+
@config = config
|
|
60
|
+
@cmd_runner = cmd_runner
|
|
61
|
+
@platforms_handler = platforms_handler
|
|
62
|
+
# List of platform handler per known node
|
|
63
|
+
# Hash<String, PlatformHandler>
|
|
64
|
+
@nodes_platform = {}
|
|
65
|
+
# List of platform handler per known nodes list
|
|
66
|
+
# Hash<String, PlatformHandler>
|
|
67
|
+
@nodes_list_platform = {}
|
|
68
|
+
# List of CMDBs getting a property, per property name
|
|
69
|
+
# Hash<Symbol, Array<Cmdb> >
|
|
70
|
+
@cmdbs_per_property = {}
|
|
71
|
+
# List of CMDBs having the get_others method
|
|
72
|
+
# Array< Cmdb >
|
|
73
|
+
@cmdbs_others = []
|
|
74
|
+
@cmdbs = Plugins.new(
|
|
75
|
+
:cmdb,
|
|
76
|
+
logger: @logger,
|
|
77
|
+
logger_stderr: @logger_stderr,
|
|
78
|
+
init_plugin: proc do |plugin_class|
|
|
79
|
+
cmdb = plugin_class.new(
|
|
80
|
+
logger: @logger,
|
|
81
|
+
logger_stderr: @logger_stderr,
|
|
82
|
+
config: @config,
|
|
83
|
+
cmd_runner: @cmd_runner,
|
|
84
|
+
platforms_handler: @platforms_handler,
|
|
85
|
+
nodes_handler: self
|
|
86
|
+
)
|
|
87
|
+
@cmdbs_others << cmdb if cmdb.respond_to?(:get_others)
|
|
88
|
+
cmdb.methods.each do |method|
|
|
89
|
+
if method.to_s =~ /^get_(.*)$/
|
|
90
|
+
property = $1.to_sym
|
|
91
|
+
@cmdbs_per_property[property] = [] unless @cmdbs_per_property.key?(property)
|
|
92
|
+
@cmdbs_per_property[property] << cmdb
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
cmdb
|
|
96
|
+
end
|
|
97
|
+
)
|
|
98
|
+
# Cache of metadata per node
|
|
99
|
+
# Hash<String, Hash<Symbol, Object> >
|
|
100
|
+
@metadata = {}
|
|
101
|
+
# The metadata update is protected by a mutex to make it thread-safe
|
|
102
|
+
@metadata_mutex = Mutex.new
|
|
103
|
+
# Cache of CMDB masters, per property, per node
|
|
104
|
+
# Hash< String, Hash< Symbol, Cmdb > >
|
|
105
|
+
@cmdb_masters_cache = {}
|
|
106
|
+
# Read all platforms from the config
|
|
107
|
+
@platforms_handler.known_platforms.each do |platform|
|
|
108
|
+
# Register all known nodes for this platform
|
|
109
|
+
platform.known_nodes.each do |node|
|
|
110
|
+
raise "Can't register #{node} to platform #{platform.repository_path}, as it is already defined in platform #{@nodes_platform[node].repository_path}." if @nodes_platform.key?(node)
|
|
111
|
+
@nodes_platform[node] = platform
|
|
112
|
+
end
|
|
113
|
+
# Register all known nodes lists
|
|
114
|
+
platform.known_nodes_lists.each do |nodes_list|
|
|
115
|
+
raise "Can't register nodes list #{nodes_list} to platform #{platform.repository_path}, as it is already defined in platform #{@nodes_list_platform[nodes_list].repository_path}." if @nodes_list_platform.key?(nodes_list)
|
|
116
|
+
@nodes_list_platform[nodes_list] = platform
|
|
117
|
+
end if platform.respond_to?(:known_nodes_lists)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Complete an option parser with options meant to control this Nodes Handler
|
|
122
|
+
#
|
|
123
|
+
# Parameters::
|
|
124
|
+
# * *options_parser* (OptionParser): The option parser to complete
|
|
125
|
+
def options_parse(options_parser, parallel: true)
|
|
126
|
+
options_parser.separator ''
|
|
127
|
+
options_parser.separator 'Nodes handler options:'
|
|
128
|
+
options_parser.on('-o', '--show-nodes', 'Display the list of possible nodes and exit') do
|
|
129
|
+
out "* Known platforms:\n#{
|
|
130
|
+
@platforms_handler.known_platforms.map do |platform|
|
|
131
|
+
"#{platform.name} - Type: #{platform.platform_type} - Location: #{platform.repository_path}"
|
|
132
|
+
end.sort.join("\n")
|
|
133
|
+
}"
|
|
134
|
+
out
|
|
135
|
+
out "* Known nodes lists:\n#{known_nodes_lists.sort.join("\n")}"
|
|
136
|
+
out
|
|
137
|
+
out "* Known services:\n#{known_services.sort.join("\n")}"
|
|
138
|
+
out
|
|
139
|
+
out "* Known nodes:\n#{known_nodes.sort.join("\n")}"
|
|
140
|
+
out
|
|
141
|
+
out "* Known nodes with description:\n#{
|
|
142
|
+
prefetch_metadata_of known_nodes, %i[hostname host_ip private_ips services description]
|
|
143
|
+
known_nodes.map do |node|
|
|
144
|
+
"#{node} (#{
|
|
145
|
+
if get_hostname_of node
|
|
146
|
+
get_hostname_of node
|
|
147
|
+
elsif get_host_ip_of node
|
|
148
|
+
get_host_ip_of node
|
|
149
|
+
elsif get_private_ips_of node
|
|
150
|
+
get_private_ips_of(node).first
|
|
151
|
+
else
|
|
152
|
+
'No connection'
|
|
153
|
+
end
|
|
154
|
+
}) - #{(get_services_of(node) || []).join(', ')} - #{get_description_of(node) || ''}"
|
|
155
|
+
end.sort.join("\n")
|
|
156
|
+
}"
|
|
157
|
+
out
|
|
158
|
+
exit 0
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Complete an option parser with ways to select nodes in parameters
|
|
163
|
+
#
|
|
164
|
+
# Parameters::
|
|
165
|
+
# * *options_parser* (OptionParser): The option parser to complete
|
|
166
|
+
# * *nodes_selectors* (Array): The list of nodes selectors that will be populated by parsing the options
|
|
167
|
+
def options_parse_nodes_selectors(options_parser, nodes_selectors)
|
|
168
|
+
platform_names = @platforms_handler.known_platforms.map(&:name).sort
|
|
169
|
+
options_parser.separator ''
|
|
170
|
+
options_parser.separator 'Nodes selection options:'
|
|
171
|
+
options_parser.on('-a', '--all-nodes', 'Select all nodes') do
|
|
172
|
+
nodes_selectors << { all: true }
|
|
173
|
+
end
|
|
174
|
+
options_parser.on('-b', '--nodes-platform PLATFORM', "Select nodes belonging to a given platform name. Available platforms are: #{platform_names.join(', ')} (can be used several times)") do |platform|
|
|
175
|
+
nodes_selectors << { platform: platform }
|
|
176
|
+
end
|
|
177
|
+
options_parser.on('-l', '--nodes-list LIST', 'Select nodes defined in a nodes list (can be used several times)') do |nodes_list|
|
|
178
|
+
nodes_selectors << { list: nodes_list }
|
|
179
|
+
end
|
|
180
|
+
options_parser.on('-n', '--node NODE', 'Select a specific node. Can be a regular expression to select several nodes if used with enclosing "/" characters. (can be used several times).') do |node|
|
|
181
|
+
nodes_selectors << node
|
|
182
|
+
end
|
|
183
|
+
options_parser.on('-r', '--nodes-service SERVICE', 'Select nodes implementing a given service (can be used several times)') do |service|
|
|
184
|
+
nodes_selectors << { service: service }
|
|
185
|
+
end
|
|
186
|
+
options_parser.on(
|
|
187
|
+
'--nodes-git-impact GIT_IMPACT',
|
|
188
|
+
'Select nodes impacted by a git diff from a platform (can be used several times).',
|
|
189
|
+
'GIT_IMPACT has the format PLATFORM:FROM_COMMIT:TO_COMMIT:FLAGS',
|
|
190
|
+
"* PLATFORM: Name of the platform to check git diff from. Available platforms are: #{platform_names.join(', ')}",
|
|
191
|
+
'* FROM_COMMIT: Commit ID or refspec from which we perform the diff. If ommitted, defaults to master',
|
|
192
|
+
'* TO_COMMIT: Commit ID ot refspec to which we perform the diff. If ommitted, defaults to the currently checked-out files',
|
|
193
|
+
'* FLAGS: Extra comma-separated flags. The following flags are supported:',
|
|
194
|
+
' - min: If specified then each impacted service will select only 1 node implementing this service. If not specified then all nodes implementing the impacted services will be selected.'
|
|
195
|
+
) do |nodes_git_impact|
|
|
196
|
+
platform_name, from_commit, to_commit, flags = nodes_git_impact.split(':')
|
|
197
|
+
flags = (flags || '').split(',')
|
|
198
|
+
raise "Invalid platform in --nodes-git-impact: #{platform_name}. Possible values are: #{platform_names.join(', ')}." unless platform_names.include?(platform_name)
|
|
199
|
+
nodes_selector = { platform: platform_name }
|
|
200
|
+
nodes_selector[:from_commit] = from_commit if from_commit && !from_commit.empty?
|
|
201
|
+
nodes_selector[:to_commit] = to_commit if to_commit && !to_commit.empty?
|
|
202
|
+
nodes_selector[:smallest_set] = true if flags.include?('min')
|
|
203
|
+
nodes_selectors << { git_diff: nodes_selector }
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Get the list of known nodes
|
|
208
|
+
#
|
|
209
|
+
# Result::
|
|
210
|
+
# * Array<String>: List of nodes
|
|
211
|
+
def known_nodes
|
|
212
|
+
@nodes_platform.keys
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Get the list of known nodes lists
|
|
216
|
+
#
|
|
217
|
+
# Result::
|
|
218
|
+
# * Array<String>: List of nodes lists' names
|
|
219
|
+
def known_nodes_lists
|
|
220
|
+
@nodes_list_platform.keys
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Get the list of nodes (resolved) belonging to a nodes list
|
|
224
|
+
#
|
|
225
|
+
# Parameters::
|
|
226
|
+
# * *nodes_list* (String): Nodes list name
|
|
227
|
+
# * *ignore_unknowns* (Boolean): Do we ignore unknown nodes? [default = false]
|
|
228
|
+
# Result::
|
|
229
|
+
# * Array<String>: List of nodes
|
|
230
|
+
def nodes_from_list(nodes_list, ignore_unknowns: false)
|
|
231
|
+
select_nodes(@nodes_list_platform[nodes_list].nodes_selectors_from_nodes_list(nodes_list), ignore_unknowns: ignore_unknowns)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Get the list of known service names
|
|
235
|
+
#
|
|
236
|
+
# Result::
|
|
237
|
+
# * Array<String>: List of service names
|
|
238
|
+
def known_services
|
|
239
|
+
prefetch_metadata_of known_nodes, :services
|
|
240
|
+
known_nodes.map { |node| get_services_of node }.flatten.compact.uniq.sort
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Get a metadata property for a given node
|
|
244
|
+
#
|
|
245
|
+
# Parameters::
|
|
246
|
+
# * *node* (String): Node
|
|
247
|
+
# * *property* (Symbol): The property name
|
|
248
|
+
# Result::
|
|
249
|
+
# * Object or nil: The node's metadata value for this property, or nil if none
|
|
250
|
+
def metadata_of(node, property)
|
|
251
|
+
prefetch_metadata_of([node], property) unless @metadata.key?(node) && @metadata[node].key?(property)
|
|
252
|
+
@metadata[node][property]
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Override a metadata property for a given node
|
|
256
|
+
#
|
|
257
|
+
# Parameters::
|
|
258
|
+
# * *node* (String): Node
|
|
259
|
+
# * *property* (Symbol): The property name
|
|
260
|
+
# * *value* (Object): The property value
|
|
261
|
+
def override_metadata_of(node, property, value)
|
|
262
|
+
@metadata_mutex.synchronize do
|
|
263
|
+
@metadata[node] = {} unless @metadata.key?(node)
|
|
264
|
+
@metadata[node][property] = value
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Invalidate a metadata property for a given node
|
|
269
|
+
#
|
|
270
|
+
# Parameters::
|
|
271
|
+
# * *node* (String): Node
|
|
272
|
+
# * *property* (Symbol): The property name
|
|
273
|
+
def invalidate_metadata_of(node, property)
|
|
274
|
+
@metadata_mutex.synchronize do
|
|
275
|
+
@metadata[node].delete(property) if @metadata.key?(node)
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Define a method to get a metadata property of a node.
|
|
280
|
+
# This is like a factory of method shortcuts for properties.
|
|
281
|
+
# The method will be named get_<property>_of.
|
|
282
|
+
# This way instead of calling
|
|
283
|
+
# metadata_of node, :host_ip
|
|
284
|
+
# we can call
|
|
285
|
+
# get_host_ip_of node
|
|
286
|
+
# Readability wins :D
|
|
287
|
+
#
|
|
288
|
+
# Parameters::
|
|
289
|
+
# * *property* (Symbol): The property name
|
|
290
|
+
def define_property_method_for(property)
|
|
291
|
+
define_singleton_method("get_#{property}_of".to_sym) { |node| metadata_of(node, property) }
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Accept any method of name get_<property>_of to get the metadata property of a given node.
|
|
295
|
+
# Here is the magic of accepting method names that are not statically defined.
|
|
296
|
+
#
|
|
297
|
+
# Parameters::
|
|
298
|
+
# * *method* (Symbol): The missing method name
|
|
299
|
+
# * *args* (Array<Object>): Arguments given to the call
|
|
300
|
+
# * *block* (Proc): Code block given to the call
|
|
301
|
+
def method_missing(method, *args, &block)
|
|
302
|
+
if method.to_s =~ /^get_(.*)_of$/
|
|
303
|
+
property = $1.to_sym
|
|
304
|
+
# Define the method so that we don't go trough method_missing next time (more efficient).
|
|
305
|
+
define_property_method_for(property)
|
|
306
|
+
# Then call it
|
|
307
|
+
send("get_#{property}_of".to_sym, *args, &block)
|
|
308
|
+
else
|
|
309
|
+
# We really don't know this method.
|
|
310
|
+
# Call original implementation of method_missing that will raise an exception.
|
|
311
|
+
super
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Prefetch some metadata properties for a given list of nodes.
|
|
316
|
+
# Useful for performance reasons when clients know they will need to use a lot of properties on nodes.
|
|
317
|
+
# Keep a thread-safe memory cache of it.
|
|
318
|
+
#
|
|
319
|
+
# Parameters::
|
|
320
|
+
# * *nodes* (Array<String>): Nodes to read metadata for
|
|
321
|
+
# * *properties* (Symbol or Array<Symbol>): Metadata properties (or single one) to read
|
|
322
|
+
def prefetch_metadata_of(nodes, properties)
|
|
323
|
+
(properties.is_a?(Symbol) ? [properties] : properties).each do |property|
|
|
324
|
+
# Gather the list of nodes missing this property
|
|
325
|
+
missing_nodes = nodes.select { |node| !@metadata.key?(node) || !@metadata[node].key?(property) }
|
|
326
|
+
unless missing_nodes.empty?
|
|
327
|
+
# Query the CMDBs having first the get_<property> method, then the ones having the get_others method till we have our property set for all missing nodes
|
|
328
|
+
# Metadata being retrieved by the different CMDBs, per node
|
|
329
|
+
# Hash< String, Object >
|
|
330
|
+
updated_metadata = {}
|
|
331
|
+
(
|
|
332
|
+
(@cmdbs_per_property.key?(property) ? @cmdbs_per_property[property] : []).map { |cmdb| [cmdb, property] } +
|
|
333
|
+
@cmdbs_others.map { |cmdb| [cmdb, :others] }
|
|
334
|
+
).each do |(cmdb, cmdb_property)|
|
|
335
|
+
# If among the missing nodes some of them have some master CMDB declared for this property, filter them out unless we are dealing with their master CMDB.
|
|
336
|
+
nodes_to_query = missing_nodes.select do |node|
|
|
337
|
+
master_cmdb = cmdb_master_for(node, property)
|
|
338
|
+
master_cmdb.nil? || master_cmdb == cmdb
|
|
339
|
+
end
|
|
340
|
+
unless nodes_to_query.empty?
|
|
341
|
+
# Check first if this property depends on other ones for this cmdb
|
|
342
|
+
if cmdb.respond_to?(:property_dependencies)
|
|
343
|
+
property_deps = cmdb.property_dependencies
|
|
344
|
+
prefetch_metadata_of nodes_to_query, property_deps[property] if property_deps.key?(property)
|
|
345
|
+
end
|
|
346
|
+
# Property values, per node name
|
|
347
|
+
# Hash< String, Object >
|
|
348
|
+
metadata_from_cmdb = Hash[
|
|
349
|
+
cmdb.send("get_#{cmdb_property}".to_sym, nodes_to_query, @metadata.slice(*nodes_to_query)).map do |node, cmdb_result|
|
|
350
|
+
[node, cmdb_property == :others ? cmdb_result[property] : cmdb_result]
|
|
351
|
+
end
|
|
352
|
+
].compact
|
|
353
|
+
cmdb_log_header = "[CMDB #{cmdb.class.name.split('::').last}.#{cmdb_property}] -"
|
|
354
|
+
log_debug "#{cmdb_log_header} Query property #{property} for #{nodes_to_query.size} nodes (#{nodes_to_query[0..7].join(', ')}...) => Found metadata for #{metadata_from_cmdb.size} nodes."
|
|
355
|
+
updated_metadata.merge!(metadata_from_cmdb) do |node, existing_value, new_value|
|
|
356
|
+
raise "#{cmdb_log_header} Returned a conflicting value for metadata #{property} of node #{node}: #{new_value} whereas the value was already set to #{existing_value}" if !existing_value.nil? && new_value != existing_value
|
|
357
|
+
new_value
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
# Avoid conflicts in metadata while merging and make sure this update is thread-safe
|
|
362
|
+
# As @metadata is only appending data and never deleting it, protecting the update only is enough.
|
|
363
|
+
# At worst several threads will query several times the same CMDBs to update the same data several times.
|
|
364
|
+
# If we also want to be thread-safe in this regard, we should protect the whole CMDB call with mutexes, at the granularity of the node + property bein read.
|
|
365
|
+
@metadata_mutex.synchronize do
|
|
366
|
+
missing_nodes.each do |node|
|
|
367
|
+
@metadata[node] = {} unless @metadata.key?(node)
|
|
368
|
+
# Here, explicitely store nil if nothing has been found for a node because we know there is no value to be fetched.
|
|
369
|
+
# This way we won't query again all CMDBs thanks to the cache.
|
|
370
|
+
@metadata[node][property] = updated_metadata[node]
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Resolve a list of nodes selectors into a real list of known nodes.
|
|
378
|
+
# A node selector can be:
|
|
379
|
+
# * String: Node name, or a node regexp if enclosed within '/' character (ex: '/.+worker.+/')
|
|
380
|
+
# * Hash<Symbol,Object>: More complete information that can contain the following keys:
|
|
381
|
+
# * *all* (Boolean): If true, specify that we want all known nodes.
|
|
382
|
+
# * *list* (String): Name of a nodes list.
|
|
383
|
+
# * *platform* (String): Name of a platform containing nodes.
|
|
384
|
+
# * *service* (String): Name of a service implemented by nodes.
|
|
385
|
+
# * *git_diff* (Hash<Symbol,Object>): Info about a git diff that impacts nodes:
|
|
386
|
+
# * *platform* (String): Name of the platform on which checking the git diff
|
|
387
|
+
# * *from_commit* (String): Commit ID to check from [default: 'master']
|
|
388
|
+
# * *to_commit* (String or nil): Commit ID to check to, or nil for currently checked-out files [default: nil]
|
|
389
|
+
# * *smallest_set* (Boolean): Smallest set of impacted nodes? [default: false]
|
|
390
|
+
#
|
|
391
|
+
# Parameters::
|
|
392
|
+
# * *nodes_selectors* (Array<Object>): List of node selectors (can be a single element).
|
|
393
|
+
# * *ignore_unknowns* (Boolean): Do we ignore unknown nodes? [default = false]
|
|
394
|
+
# Result::
|
|
395
|
+
# * Array<String>: List of nodes
|
|
396
|
+
def select_nodes(*nodes_selectors, ignore_unknowns: false)
|
|
397
|
+
nodes_selectors = nodes_selectors.flatten
|
|
398
|
+
# 1. Check for the presence of all
|
|
399
|
+
return known_nodes if nodes_selectors.any? { |nodes_selector| nodes_selector.is_a?(Hash) && nodes_selector.key?(:all) && nodes_selector[:all] }
|
|
400
|
+
# 2. Expand the nodes lists, platforms and services contents
|
|
401
|
+
string_nodes = []
|
|
402
|
+
nodes_selectors.each do |nodes_selector|
|
|
403
|
+
if nodes_selector.is_a?(String)
|
|
404
|
+
string_nodes << nodes_selector
|
|
405
|
+
else
|
|
406
|
+
if nodes_selector.key?(:list)
|
|
407
|
+
platform = @nodes_list_platform[nodes_selector[:list]]
|
|
408
|
+
raise "Unknown nodes list: #{nodes_selector[:list]}" if platform.nil?
|
|
409
|
+
string_nodes.concat(platform.nodes_selectors_from_nodes_list(nodes_selector[:list]))
|
|
410
|
+
end
|
|
411
|
+
string_nodes.concat(@platforms_handler.platform(nodes_selector[:platform]).known_nodes) if nodes_selector.key?(:platform)
|
|
412
|
+
if nodes_selector.key?(:service)
|
|
413
|
+
prefetch_metadata_of known_nodes, :services
|
|
414
|
+
string_nodes.concat(known_nodes.select { |node| (get_services_of(node) || []).include?(nodes_selector[:service]) })
|
|
415
|
+
end
|
|
416
|
+
if nodes_selector.key?(:git_diff)
|
|
417
|
+
# Default values
|
|
418
|
+
git_diff_info = {
|
|
419
|
+
from_commit: 'master',
|
|
420
|
+
to_commit: nil,
|
|
421
|
+
smallest_set: false
|
|
422
|
+
}.merge(nodes_selector[:git_diff])
|
|
423
|
+
all_impacted_nodes, _impacted_nodes, _impacted_services, _impact_global = impacted_nodes_from_git_diff(
|
|
424
|
+
git_diff_info[:platform],
|
|
425
|
+
from_commit: git_diff_info[:from_commit],
|
|
426
|
+
to_commit: git_diff_info[:to_commit],
|
|
427
|
+
smallest_set: git_diff_info[:smallest_set]
|
|
428
|
+
)
|
|
429
|
+
string_nodes.concat(all_impacted_nodes)
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
# 3. Expand the Regexps
|
|
434
|
+
real_nodes = []
|
|
435
|
+
string_nodes.each do |node|
|
|
436
|
+
if node =~ /^\/(.+)\/$/
|
|
437
|
+
node_regexp = Regexp.new($1)
|
|
438
|
+
real_nodes.concat(known_nodes.select { |known_node| known_node[node_regexp] })
|
|
439
|
+
else
|
|
440
|
+
real_nodes << node
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
# 4. Sort them unique
|
|
444
|
+
real_nodes.uniq!
|
|
445
|
+
real_nodes.sort!
|
|
446
|
+
# Some sanity checks
|
|
447
|
+
unless ignore_unknowns
|
|
448
|
+
unknown_nodes = real_nodes - known_nodes
|
|
449
|
+
raise "Unknown nodes: #{unknown_nodes.join(', ')}" unless unknown_nodes.empty?
|
|
450
|
+
end
|
|
451
|
+
real_nodes
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
# Iterate over a list of nodes.
|
|
455
|
+
# Provide a mechanism to multithread this iteration (in such case the iterating code has to be thread-safe).
|
|
456
|
+
# In case of multithreaded run, a progress bar is being displayed.
|
|
457
|
+
#
|
|
458
|
+
# Parameters::
|
|
459
|
+
# * *nodes* (Array<String>): List of nodes to iterate over
|
|
460
|
+
# * *parallel* (Boolean): Iterate in a multithreaded way? [default: false]
|
|
461
|
+
# * *nbr_threads_max* (Integer or nil): Maximum number of threads to be used in case of parallel, or nil for no limit [default: nil]
|
|
462
|
+
# * *progress* (String or nil): Name of a progress bar to follow the progression, or nil for no progress bar [default: 'Processing nodes']
|
|
463
|
+
# * Proc: The code called for each node being iterated on.
|
|
464
|
+
# * Parameters::
|
|
465
|
+
# * *node* (String): The node name
|
|
466
|
+
def for_each_node_in(nodes, parallel: false, nbr_threads_max: nil, progress: 'Processing nodes')
|
|
467
|
+
for_each_element_in(nodes.sort, parallel: parallel, nbr_threads_max: nbr_threads_max, progress: progress) do |node|
|
|
468
|
+
yield node
|
|
469
|
+
end
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
# Get the list of impacted nodes from a git diff on a platform
|
|
473
|
+
#
|
|
474
|
+
# Parameters::
|
|
475
|
+
# * *platform_name* (String): The platform's name
|
|
476
|
+
# * *from_commit* (String): Commit ID to check from [default: 'master']
|
|
477
|
+
# * *to_commit* (String or nil): Commit ID to check to, or nil for currently checked-out files [default: nil]
|
|
478
|
+
# * *smallest_set* (Boolean): Smallest set of impacted nodes? [default: false]
|
|
479
|
+
# Result::
|
|
480
|
+
# * Array<String>: The list of nodes impacted by this diff (counting direct impacts, services and global files impacted)
|
|
481
|
+
# * Array<String>: The list of nodes directly impacted by this diff
|
|
482
|
+
# * Array<String>: The list of services impacted by this diff
|
|
483
|
+
# * Boolean: Are there some files that have a global impact (meaning all nodes are potentially impacted by this diff)?
|
|
484
|
+
def impacted_nodes_from_git_diff(platform_name, from_commit: 'master', to_commit: nil, smallest_set: false)
|
|
485
|
+
platform = @platforms_handler.platform(platform_name)
|
|
486
|
+
raise "Unkown platform #{platform_name}. Possible platforms are #{@platforms_handler.known_platforms.map(&:name).sort.join(', ')}" if platform.nil?
|
|
487
|
+
_exit_status, stdout, _stderr = @cmd_runner.run_cmd "cd #{platform.repository_path} && git --no-pager diff --no-color #{from_commit} #{to_commit.nil? ? '' : to_commit}", log_to_stdout: log_debug?
|
|
488
|
+
# Parse the git diff output to create a structured diff
|
|
489
|
+
# Hash< String, Hash< Symbol, Object > >: List of diffs info, per file name having a diff. Diffs info have the following properties:
|
|
490
|
+
# * *moved_to* (String): The new file path, in case it has been moved [optional]
|
|
491
|
+
# * *diff* (String): The diff content
|
|
492
|
+
files_diffs = {}
|
|
493
|
+
current_file_diff = nil
|
|
494
|
+
stdout.split("\n").each do |line|
|
|
495
|
+
case line
|
|
496
|
+
when /^diff --git a\/(.+) b\/(.+)$/
|
|
497
|
+
# A new file diff
|
|
498
|
+
from, to = $1, $2
|
|
499
|
+
current_file_diff = {
|
|
500
|
+
diff: ''
|
|
501
|
+
}
|
|
502
|
+
current_file_diff[:moved_to] = to unless from == to
|
|
503
|
+
files_diffs[from] = current_file_diff
|
|
504
|
+
else
|
|
505
|
+
current_file_diff[:diff] << "#{current_file_diff[:diff].empty? ? '' : "\n"}#{line}" unless current_file_diff.nil?
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
impacted_nodes, impacted_services, impact_global = platform.impacts_from files_diffs
|
|
509
|
+
impacted_services.sort!
|
|
510
|
+
impacted_services.uniq!
|
|
511
|
+
impacted_nodes.sort!
|
|
512
|
+
impacted_nodes.uniq!
|
|
513
|
+
[
|
|
514
|
+
if impact_global
|
|
515
|
+
platform.known_nodes.sort
|
|
516
|
+
else
|
|
517
|
+
(
|
|
518
|
+
impacted_nodes + impacted_services.map do |service|
|
|
519
|
+
service_nodes = select_nodes([{ service: service }])
|
|
520
|
+
smallest_set ? [service_nodes.first].compact : service_nodes
|
|
521
|
+
end
|
|
522
|
+
).flatten.sort.uniq
|
|
523
|
+
end,
|
|
524
|
+
impacted_nodes,
|
|
525
|
+
impacted_services,
|
|
526
|
+
impact_global
|
|
527
|
+
]
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
# Select the configs applicable to a given node.
|
|
531
|
+
#
|
|
532
|
+
# Parameters::
|
|
533
|
+
# * *node* (String): The node for which we select configurations
|
|
534
|
+
# * *configs* (Array< Hash<Symbol,Object> >): Configuration properties. Each configuration is selected based on the nodes_selectors_stack property.
|
|
535
|
+
# Result::
|
|
536
|
+
# * Array< Hash<Symbol,Object> >: The selected configurations
|
|
537
|
+
def select_confs_for_node(node, configs)
|
|
538
|
+
configs.select { |config_info| select_from_nodes_selector_stack(config_info[:nodes_selectors_stack]).include?(node) }
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
# Select the configs applicable to a given platform.
|
|
542
|
+
#
|
|
543
|
+
# Parameters::
|
|
544
|
+
# * *platform_name* (String): The platform for which we select configurations
|
|
545
|
+
# * *configs* (Array< Hash<Symbol,Object> >): Configuration properties. Each configuration is selected based on the nodes_selectors_stack property.
|
|
546
|
+
# Result::
|
|
547
|
+
# * Array< Hash<Symbol,Object> >: The selected configurations
|
|
548
|
+
def select_confs_for_platform(platform_name, configs)
|
|
549
|
+
platform_nodes = @platforms_handler.platform(platform_name).known_nodes
|
|
550
|
+
configs.select { |config_info| (platform_nodes - select_from_nodes_selector_stack(config_info[:nodes_selectors_stack])).empty? }
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
# Get the list of nodes impacted by a nodes selector stack.
|
|
554
|
+
# The result is the intersection of every nodes set in the stack.
|
|
555
|
+
#
|
|
556
|
+
# Parameters::
|
|
557
|
+
# * *nodes_selector_stack* (Array): The nodes selector stack
|
|
558
|
+
# Result::
|
|
559
|
+
# * Array<String>: List of nodes
|
|
560
|
+
def select_from_nodes_selector_stack(nodes_selector_stack)
|
|
561
|
+
nodes_selector_stack.inject(known_nodes) { |selected_nodes, nodes_selector| selected_nodes & select_nodes(nodes_selector) }
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
private
|
|
565
|
+
|
|
566
|
+
# Get the CMDB master for a given property.
|
|
567
|
+
# Keep a cache of the masters for performance, as this can be called very often and multi-threaded.
|
|
568
|
+
#
|
|
569
|
+
# Parameters::
|
|
570
|
+
# * *node* (String): Node for which we want the CMDB master
|
|
571
|
+
# * *property* (Symbol): The property for which we search a CMDB master
|
|
572
|
+
# Result::
|
|
573
|
+
# * Cmdb or nil: CMDB, or nil if none
|
|
574
|
+
def cmdb_master_for(node, property)
|
|
575
|
+
unless @cmdb_masters_cache.key?(node)
|
|
576
|
+
# CMDB master per property name
|
|
577
|
+
# Hash< Symbol, Cmdb >
|
|
578
|
+
cmdb_masters_cache = {}
|
|
579
|
+
select_confs_for_node(node, @config.cmdb_masters).each do |cmdb_masters_info|
|
|
580
|
+
cmdb_masters_info[:cmdb_masters].each do |cmdb, properties|
|
|
581
|
+
properties.each do |property|
|
|
582
|
+
raise "Property #{property} have conflicting CMDB masters for #{node} declared in the configuration: #{cmdb_masters_cache[property].class.name} and #{@cmdbs[cmdb].class.name}" if cmdb_masters_cache.key?(property) && cmdb_masters_cache[property] != @cmdbs[cmdb]
|
|
583
|
+
log_debug "CMDB master for #{node} / #{property}: #{cmdb}"
|
|
584
|
+
raise "CMDB #{cmdb} is configured as a master for property #{property} on node #{node} but it does not implement the needed API to retrieve it" unless (@cmdbs_per_property[property] || []).include?(@cmdbs[cmdb]) || @cmdbs_others.include?(@cmdbs[cmdb])
|
|
585
|
+
cmdb_masters_cache[property] = @cmdbs[cmdb]
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
# Set the instance variable as an atomic operation to ensure multi-threading here.
|
|
590
|
+
@cmdb_masters_cache[node] = cmdb_masters_cache
|
|
591
|
+
end
|
|
592
|
+
@cmdb_masters_cache[node][property]
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
end
|