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.
Files changed (244) hide show
  1. checksums.yaml +7 -0
  2. data/bin/check-node +24 -0
  3. data/bin/deploy +12 -0
  4. data/bin/dump_nodes_json +12 -0
  5. data/bin/free_ips +23 -0
  6. data/bin/free_veids +17 -0
  7. data/bin/get_impacted_nodes +43 -0
  8. data/bin/last_deploys +56 -0
  9. data/bin/nodes_to_deploy +104 -0
  10. data/bin/report +10 -0
  11. data/bin/run +39 -0
  12. data/bin/setup +11 -0
  13. data/bin/ssh_config +14 -0
  14. data/bin/test +13 -0
  15. data/bin/topograph +54 -0
  16. data/lib/hybrid_platforms_conductor/action.rb +82 -0
  17. data/lib/hybrid_platforms_conductor/actions_executor.rb +307 -0
  18. data/lib/hybrid_platforms_conductor/bitbucket.rb +123 -0
  19. data/lib/hybrid_platforms_conductor/cmd_runner.rb +188 -0
  20. data/lib/hybrid_platforms_conductor/cmdb.rb +34 -0
  21. data/lib/hybrid_platforms_conductor/common_config_dsl/bitbucket.rb +78 -0
  22. data/lib/hybrid_platforms_conductor/common_config_dsl/confluence.rb +43 -0
  23. data/lib/hybrid_platforms_conductor/common_config_dsl/file_system_tests.rb +110 -0
  24. data/lib/hybrid_platforms_conductor/common_config_dsl/idempotence_tests.rb +38 -0
  25. data/lib/hybrid_platforms_conductor/config.rb +263 -0
  26. data/lib/hybrid_platforms_conductor/confluence.rb +119 -0
  27. data/lib/hybrid_platforms_conductor/connector.rb +84 -0
  28. data/lib/hybrid_platforms_conductor/credentials.rb +127 -0
  29. data/lib/hybrid_platforms_conductor/current_dir_monitor.rb +42 -0
  30. data/lib/hybrid_platforms_conductor/deployer.rb +598 -0
  31. data/lib/hybrid_platforms_conductor/executable.rb +145 -0
  32. data/lib/hybrid_platforms_conductor/hpc_plugins/action/bash.rb +44 -0
  33. data/lib/hybrid_platforms_conductor/hpc_plugins/action/interactive.rb +44 -0
  34. data/lib/hybrid_platforms_conductor/hpc_plugins/action/my_action.rb.sample +79 -0
  35. data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +63 -0
  36. data/lib/hybrid_platforms_conductor/hpc_plugins/action/ruby.rb +69 -0
  37. data/lib/hybrid_platforms_conductor/hpc_plugins/action/scp.rb +61 -0
  38. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/config.rb +78 -0
  39. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_ip.rb +104 -0
  40. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_keys.rb +114 -0
  41. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/my_cmdb.rb.sample +129 -0
  42. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/platform_handlers.rb +66 -0
  43. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +156 -0
  44. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +702 -0
  45. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/platform_handler_plugin.rb.sample +292 -0
  46. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/docker.rb +148 -0
  47. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/my_provisioner.rb.sample +103 -0
  48. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/podman.rb +125 -0
  49. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +522 -0
  50. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/proxmox_waiter.rb +707 -0
  51. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/reserve_proxmox_container +122 -0
  52. data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +69 -0
  53. data/lib/hybrid_platforms_conductor/hpc_plugins/report/mediawiki.rb +164 -0
  54. data/lib/hybrid_platforms_conductor/hpc_plugins/report/my_report_plugin.rb.sample +88 -0
  55. data/lib/hybrid_platforms_conductor/hpc_plugins/report/stdout.rb +61 -0
  56. data/lib/hybrid_platforms_conductor/hpc_plugins/report/templates/confluence_inventory.html.erb +33 -0
  57. data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +137 -0
  58. data/lib/hybrid_platforms_conductor/hpc_plugins/test/can_be_checked.rb +21 -0
  59. data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +112 -0
  60. data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_from_scratch.rb +35 -0
  61. data/lib/hybrid_platforms_conductor/hpc_plugins/test/connection.rb +28 -0
  62. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_freshness.rb +44 -0
  63. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_from_scratch.rb +36 -0
  64. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +49 -0
  65. data/lib/hybrid_platforms_conductor/hpc_plugins/test/divergence.rb +25 -0
  66. data/lib/hybrid_platforms_conductor/hpc_plugins/test/executables.rb +46 -0
  67. data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system.rb +45 -0
  68. data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system_hdfs.rb +45 -0
  69. data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +25 -0
  70. data/lib/hybrid_platforms_conductor/hpc_plugins/test/idempotence.rb +77 -0
  71. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +38 -0
  72. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +56 -0
  73. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +54 -0
  74. data/lib/hybrid_platforms_conductor/hpc_plugins/test/linear_strategy.rb +47 -0
  75. data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +82 -0
  76. data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +120 -0
  77. data/lib/hybrid_platforms_conductor/hpc_plugins/test/my_test_plugin.rb.sample +143 -0
  78. data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +74 -0
  79. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ports.rb +85 -0
  80. data/lib/hybrid_platforms_conductor/hpc_plugins/test/private_ips.rb +38 -0
  81. data/lib/hybrid_platforms_conductor/hpc_plugins/test/public_ips.rb +38 -0
  82. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre-meltdown-checker.sh +1930 -0
  83. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +56 -0
  84. data/lib/hybrid_platforms_conductor/hpc_plugins/test/veids.rb +31 -0
  85. data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +159 -0
  86. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +122 -0
  87. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/my_test_report.rb.sample +48 -0
  88. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/stdout.rb +120 -0
  89. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/_confluence_errors_status.html.erb +46 -0
  90. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/_confluence_gauge.html.erb +49 -0
  91. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/confluence.html.erb +242 -0
  92. data/lib/hybrid_platforms_conductor/io_router.rb +70 -0
  93. data/lib/hybrid_platforms_conductor/json_dumper.rb +88 -0
  94. data/lib/hybrid_platforms_conductor/logger_helpers.rb +319 -0
  95. data/lib/hybrid_platforms_conductor/mutex_dir +76 -0
  96. data/lib/hybrid_platforms_conductor/nodes_handler.rb +597 -0
  97. data/lib/hybrid_platforms_conductor/parallel_threads.rb +97 -0
  98. data/lib/hybrid_platforms_conductor/platform_handler.rb +188 -0
  99. data/lib/hybrid_platforms_conductor/platforms_handler.rb +118 -0
  100. data/lib/hybrid_platforms_conductor/plugin.rb +53 -0
  101. data/lib/hybrid_platforms_conductor/plugins.rb +101 -0
  102. data/lib/hybrid_platforms_conductor/provisioner.rb +181 -0
  103. data/lib/hybrid_platforms_conductor/report.rb +31 -0
  104. data/lib/hybrid_platforms_conductor/reports_handler.rb +84 -0
  105. data/lib/hybrid_platforms_conductor/services_handler.rb +274 -0
  106. data/lib/hybrid_platforms_conductor/test.rb +141 -0
  107. data/lib/hybrid_platforms_conductor/test_by_service.rb +22 -0
  108. data/lib/hybrid_platforms_conductor/test_report.rb +282 -0
  109. data/lib/hybrid_platforms_conductor/tests_runner.rb +590 -0
  110. data/lib/hybrid_platforms_conductor/thycotic.rb +92 -0
  111. data/lib/hybrid_platforms_conductor/topographer.rb +859 -0
  112. data/lib/hybrid_platforms_conductor/topographer/plugin.rb +20 -0
  113. data/lib/hybrid_platforms_conductor/topographer/plugins/graphviz.rb +127 -0
  114. data/lib/hybrid_platforms_conductor/topographer/plugins/json.rb +72 -0
  115. data/lib/hybrid_platforms_conductor/topographer/plugins/my_topographer_output_plugin.rb.sample +37 -0
  116. data/lib/hybrid_platforms_conductor/topographer/plugins/svg.rb +30 -0
  117. data/lib/hybrid_platforms_conductor/version.rb +5 -0
  118. data/spec/hybrid_platforms_conductor_test.rb +159 -0
  119. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/bash_spec.rb +43 -0
  120. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/interactive_spec.rb +18 -0
  121. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +102 -0
  122. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/ruby_spec.rb +108 -0
  123. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/scp_spec.rb +79 -0
  124. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions_spec.rb +199 -0
  125. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connection_spec.rb +212 -0
  126. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/cli_options_spec.rb +125 -0
  127. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/config_dsl_spec.rb +50 -0
  128. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connectable_nodes_spec.rb +28 -0
  129. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +448 -0
  130. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +313 -0
  131. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/node_helpers_spec.rb +32 -0
  132. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +134 -0
  133. data/spec/hybrid_platforms_conductor_test/api/actions_executor/logging_spec.rb +256 -0
  134. data/spec/hybrid_platforms_conductor_test/api/actions_executor/parallel_spec.rb +338 -0
  135. data/spec/hybrid_platforms_conductor_test/api/actions_executor/timeout_spec.rb +101 -0
  136. data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +165 -0
  137. data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +238 -0
  138. data/spec/hybrid_platforms_conductor_test/api/deployer/check_spec.rb +9 -0
  139. data/spec/hybrid_platforms_conductor_test/api/deployer/deploy_spec.rb +243 -0
  140. data/spec/hybrid_platforms_conductor_test/api/deployer/parse_deploy_output_spec.rb +104 -0
  141. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioner_spec.rb +131 -0
  142. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/docker/Dockerfile +10 -0
  143. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/docker_spec.rb +123 -0
  144. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/podman_spec.rb +211 -0
  145. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/config_dsl_spec.rb +126 -0
  146. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/create_spec.rb +290 -0
  147. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/destroy_spec.rb +43 -0
  148. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/ip_spec.rb +60 -0
  149. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/proxmox.json +3 -0
  150. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/destroy_vm_spec.rb +82 -0
  151. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/expired_containers_spec.rb +786 -0
  152. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/ips_assignment_spec.rb +112 -0
  153. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/other_lxc_containers_resources_spec.rb +190 -0
  154. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/pve_node_resources_spec.rb +200 -0
  155. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/retries_spec.rb +35 -0
  156. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/vm_ids_assignment_spec.rb +67 -0
  157. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/start_spec.rb +79 -0
  158. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/state_spec.rb +28 -0
  159. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/stop_spec.rb +41 -0
  160. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/config_spec.rb +33 -0
  161. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_ip_spec.rb +64 -0
  162. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_keys_spec.rb +133 -0
  163. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/platform_handlers_spec.rb +19 -0
  164. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs_plugins_api_spec.rb +446 -0
  165. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/common_spec.rb +127 -0
  166. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/git_diff_impacts_spec.rb +318 -0
  167. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/nodes_selectors_spec.rb +132 -0
  168. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/platform_handlers_plugins_api_spec.rb +60 -0
  169. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/several_platforms_spec.rb +58 -0
  170. data/spec/hybrid_platforms_conductor_test/api/platform_handler_spec.rb +97 -0
  171. data/spec/hybrid_platforms_conductor_test/api/platforms_handler_spec.rb +104 -0
  172. data/spec/hybrid_platforms_conductor_test/api/plugins_spec.rb +243 -0
  173. data/spec/hybrid_platforms_conductor_test/api/reports_handler_spec.rb +44 -0
  174. data/spec/hybrid_platforms_conductor_test/api/services_handler/actions_to_deploy_spec.rb +121 -0
  175. data/spec/hybrid_platforms_conductor_test/api/services_handler/deploy_allowed_spec.rb +142 -0
  176. data/spec/hybrid_platforms_conductor_test/api/services_handler/log_info_spec.rb +101 -0
  177. data/spec/hybrid_platforms_conductor_test/api/services_handler/package_spec.rb +388 -0
  178. data/spec/hybrid_platforms_conductor_test/api/services_handler/parse_deploy_output_spec.rb +274 -0
  179. data/spec/hybrid_platforms_conductor_test/api/services_handler/prepare_for_deploy_spec.rb +264 -0
  180. data/spec/hybrid_platforms_conductor_test/api/tests_runner/common_spec.rb +194 -0
  181. data/spec/hybrid_platforms_conductor_test/api/tests_runner/global_spec.rb +37 -0
  182. data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_check_spec.rb +194 -0
  183. data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_spec.rb +137 -0
  184. data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_ssh_spec.rb +257 -0
  185. data/spec/hybrid_platforms_conductor_test/api/tests_runner/platform_spec.rb +110 -0
  186. data/spec/hybrid_platforms_conductor_test/api/tests_runner/reports_spec.rb +367 -0
  187. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/bitbucket_conf_spec.rb +111 -0
  188. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_reports_plugins/confluence_spec.rb +29 -0
  189. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb.rb +166 -0
  190. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb2.rb +93 -0
  191. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb_others.rb +60 -0
  192. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb_others2.rb +58 -0
  193. data/spec/hybrid_platforms_conductor_test/executables/check-node_spec.rb +35 -0
  194. data/spec/hybrid_platforms_conductor_test/executables/deploy_spec.rb +35 -0
  195. data/spec/hybrid_platforms_conductor_test/executables/get_impacted_nodes_spec.rb +158 -0
  196. data/spec/hybrid_platforms_conductor_test/executables/last_deploys_spec.rb +173 -0
  197. data/spec/hybrid_platforms_conductor_test/executables/nodes_to_deploy_spec.rb +283 -0
  198. data/spec/hybrid_platforms_conductor_test/executables/options/actions_executor_spec.rb +28 -0
  199. data/spec/hybrid_platforms_conductor_test/executables/options/cmd_runner_spec.rb +28 -0
  200. data/spec/hybrid_platforms_conductor_test/executables/options/common_spec.rb +67 -0
  201. data/spec/hybrid_platforms_conductor_test/executables/options/deployer_spec.rb +251 -0
  202. data/spec/hybrid_platforms_conductor_test/executables/options/nodes_handler_spec.rb +111 -0
  203. data/spec/hybrid_platforms_conductor_test/executables/options/nodes_selectors_spec.rb +71 -0
  204. data/spec/hybrid_platforms_conductor_test/executables/options/reports_handler_spec.rb +54 -0
  205. data/spec/hybrid_platforms_conductor_test/executables/options/tests_runner_spec.rb +139 -0
  206. data/spec/hybrid_platforms_conductor_test/executables/report_spec.rb +60 -0
  207. data/spec/hybrid_platforms_conductor_test/executables/run_spec.rb +173 -0
  208. data/spec/hybrid_platforms_conductor_test/executables/ssh_config_spec.rb +35 -0
  209. data/spec/hybrid_platforms_conductor_test/executables/test_spec.rb +41 -0
  210. data/spec/hybrid_platforms_conductor_test/helpers/actions_executor_helpers.rb +98 -0
  211. data/spec/hybrid_platforms_conductor_test/helpers/cmd_runner_helpers.rb +92 -0
  212. data/spec/hybrid_platforms_conductor_test/helpers/cmdb_helpers.rb +37 -0
  213. data/spec/hybrid_platforms_conductor_test/helpers/config_helpers.rb +20 -0
  214. data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +130 -0
  215. data/spec/hybrid_platforms_conductor_test/helpers/deployer_helpers.rb +149 -0
  216. data/spec/hybrid_platforms_conductor_test/helpers/deployer_test_helpers.rb +812 -0
  217. data/spec/hybrid_platforms_conductor_test/helpers/executables_helpers.rb +96 -0
  218. data/spec/hybrid_platforms_conductor_test/helpers/nodes_handler_helpers.rb +20 -0
  219. data/spec/hybrid_platforms_conductor_test/helpers/platform_handler_helpers.rb +35 -0
  220. data/spec/hybrid_platforms_conductor_test/helpers/platforms_handler_helpers.rb +127 -0
  221. data/spec/hybrid_platforms_conductor_test/helpers/plugins_helpers.rb +48 -0
  222. data/spec/hybrid_platforms_conductor_test/helpers/provisioner_proxmox_helpers.rb +789 -0
  223. data/spec/hybrid_platforms_conductor_test/helpers/reports_handler_helpers.rb +29 -0
  224. data/spec/hybrid_platforms_conductor_test/helpers/services_handler_helpers.rb +20 -0
  225. data/spec/hybrid_platforms_conductor_test/helpers/tests_runner_helpers.rb +38 -0
  226. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem/hpc_plugins/test_plugin_type/test_plugin_id1.rb +22 -0
  227. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem/hpc_plugins/test_plugin_type/test_plugin_id2.rb +22 -0
  228. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem2/sub_dir/hpc_plugins/test_plugin_type/test_plugin_id3.rb +26 -0
  229. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem2/sub_dir/hpc_plugins/test_plugin_type2/test_plugin_id4.rb +26 -0
  230. data/spec/hybrid_platforms_conductor_test/platform_handler_plugins/test.rb +225 -0
  231. data/spec/hybrid_platforms_conductor_test/platform_handler_plugins/test2.rb +11 -0
  232. data/spec/hybrid_platforms_conductor_test/report_plugin.rb +35 -0
  233. data/spec/hybrid_platforms_conductor_test/test_action.rb +66 -0
  234. data/spec/hybrid_platforms_conductor_test/test_connector.rb +151 -0
  235. data/spec/hybrid_platforms_conductor_test/test_plugins/global.rb +30 -0
  236. data/spec/hybrid_platforms_conductor_test/test_plugins/node.rb +53 -0
  237. data/spec/hybrid_platforms_conductor_test/test_plugins/node_check.rb +47 -0
  238. data/spec/hybrid_platforms_conductor_test/test_plugins/node_ssh.rb +42 -0
  239. data/spec/hybrid_platforms_conductor_test/test_plugins/platform.rb +50 -0
  240. data/spec/hybrid_platforms_conductor_test/test_plugins/several_checks.rb +50 -0
  241. data/spec/hybrid_platforms_conductor_test/test_provisioner.rb +95 -0
  242. data/spec/hybrid_platforms_conductor_test/tests_report_plugin.rb +49 -0
  243. data/spec/spec_helper.rb +111 -0
  244. 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