hybrid_platforms_conductor 32.3.6

Sign up to get free protection for your applications and to get access to all the features.
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,96 @@
1
+ module HybridPlatformsConductorTest
2
+
3
+ module Helpers
4
+
5
+ module ExecutablesHelpers
6
+
7
+ # Run an executable and get its output
8
+ #
9
+ # Parameters::
10
+ # * *executable* (String): Executable name
11
+ # * *args* (Array<String>): Arguments to give the executable
12
+ # Result::
13
+ # * Integer: Exit code
14
+ # * String: stdout
15
+ # * String: stderr
16
+ def run(executable, *args)
17
+ stdout_file = "#{Dir.tmpdir}/hpc_test/run.stdout"
18
+ stderr_file = "#{Dir.tmpdir}/hpc_test/run.stderr"
19
+ File.open(stdout_file, 'w') { |f| f.truncate(0) }
20
+ File.open(stderr_file, 'w') { |f| f.truncate(0) }
21
+ logger_stdout = Logger.new(stdout_file, level: :info)
22
+ logger_stderr = Logger.new(stderr_file, level: :info)
23
+ # Mock the Executable creation to redirect stdout and stderr correctly
24
+ expect(HybridPlatformsConductor::Executable).to receive(:new).once.and_wrap_original do |original_method,
25
+ check_options: true,
26
+ nodes_selection_options: true,
27
+ parallel_options: true,
28
+ timeout_options: true,
29
+ deploy_options: true,
30
+ original_logger: Logger.new(STDOUT, level: :info),
31
+ original_logger_stderr: Logger.new(STDERR, level: :info),
32
+ &opts_block|
33
+ original_method.call(
34
+ check_options: check_options,
35
+ nodes_selection_options: nodes_selection_options,
36
+ parallel_options: parallel_options,
37
+ timeout_options: timeout_options,
38
+ deploy_options: deploy_options,
39
+ logger: logger_stdout,
40
+ logger_stderr: logger_stderr,
41
+ &opts_block
42
+ )
43
+ end
44
+ # Get a simple list of all the components that should be mocked when being used by the executables, per class to be mocked.
45
+ components_to_mock = {
46
+ HybridPlatformsConductor::ActionsExecutor => test_actions_executor,
47
+ HybridPlatformsConductor::CmdRunner => test_cmd_runner,
48
+ HybridPlatformsConductor::Config => test_config,
49
+ HybridPlatformsConductor::Deployer => test_deployer,
50
+ HybridPlatformsConductor::NodesHandler => test_nodes_handler,
51
+ HybridPlatformsConductor::PlatformsHandler => test_platforms_handler,
52
+ HybridPlatformsConductor::ReportsHandler => test_reports_handler,
53
+ HybridPlatformsConductor::TestsRunner => test_tests_runner
54
+ }
55
+ # Make sure the tested components use the same loggers as the executable.
56
+ components_to_mock.values.each do |component|
57
+ component.stdout_device = stdout_file
58
+ component.stderr_device = stderr_file
59
+ end
60
+ # Make sure that when the executable creates components it uses ours
61
+ components_to_mock.each do |component_class, component|
62
+ allow(component_class).to receive(:new).once { component }
63
+ end
64
+ # Run the executable
65
+ args.concat(['--debug']) if ENV['TEST_DEBUG'] == '1'
66
+ ARGV.replace(args)
67
+ old_0 = $0
68
+ $0 = executable
69
+ begin
70
+ exit_code = nil
71
+ begin
72
+ load "#{__dir__}/../../../bin/#{executable}"
73
+ exit_code = 0
74
+ rescue SystemExit
75
+ exit_code = $!.status
76
+ end
77
+ ensure
78
+ $0 = old_0
79
+ end
80
+ stdout = File.read(stdout_file)
81
+ stderr = File.read(stderr_file)
82
+ if ENV['TEST_DEBUG'] == '1'
83
+ puts "> #{executable} #{args.join(' ')}"
84
+ puts '===== STDOUT ====='
85
+ puts stdout
86
+ puts '===== STDERR ====='
87
+ puts stderr
88
+ end
89
+ [exit_code, stdout, stderr]
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,20 @@
1
+ module HybridPlatformsConductorTest
2
+
3
+ module Helpers
4
+
5
+ module NodesHandlerHelpers
6
+
7
+ # Get a test NodesHandler
8
+ #
9
+ # Result::
10
+ # * NodesHandler: NodesHandler on which we can do testing
11
+ def test_nodes_handler
12
+ @nodes_handler = HybridPlatformsConductor::NodesHandler.new logger: logger, logger_stderr: logger, config: test_config, cmd_runner: test_cmd_runner, platforms_handler: test_platforms_handler unless @nodes_handler
13
+ @nodes_handler
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,35 @@
1
+ module HybridPlatformsConductorTest
2
+
3
+ module Helpers
4
+
5
+ module PlatformHandlerHelpers
6
+
7
+ # Return the test platforms info, used by the test PlatformHandler
8
+ #
9
+ # Result::
10
+ # * *Hash<String, Hash>: Platforms info, per platform name (see TestPlatformHandler#platforms_info for details)
11
+ def test_platforms_info
12
+ HybridPlatformsConductorTest::PlatformHandlerPlugins::Test.platforms_info
13
+ end
14
+
15
+ # Set the test platforms info, used by the test PlatformHandler
16
+ #
17
+ # Parameters::
18
+ # * *platforms_info* (Hash<String, Hash>): Platforms info, per platform name (see TestPlatformHandler#platforms_info for details)
19
+ def test_platforms_info=(platforms_info)
20
+ HybridPlatformsConductorTest::PlatformHandlerPlugins::Test.platforms_info = platforms_info
21
+ end
22
+
23
+ # Register the given platform handler classes
24
+ #
25
+ # Parameters::
26
+ # * *platform_handlers* (Hash<Symbol,Class>): The platform handler classes, per platform type name
27
+ def register_platform_handlers(platform_handlers)
28
+ register_plugins(:platform_handler, platform_handlers)
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,127 @@
1
+ module HybridPlatformsConductorTest
2
+
3
+ module Helpers
4
+
5
+ module PlatformsHandlerHelpers
6
+
7
+ # Setup several test repositories.
8
+ # Clean-up at the end.
9
+ #
10
+ # Parameters::
11
+ # * *names* (Array<String>): Name of the directories to be used [default = []]
12
+ # * *as_git* (Boolean): Do we initialize those repositories as Git repositories? [default: false]
13
+ # * Proc: Code called with the repositories created.
14
+ # * Parameters::
15
+ # * *repositories* (Hash<String,String>): Path to the repositories, per repository name
16
+ def with_repositories(names = [], as_git: false)
17
+ repositories = Hash[names.map { |name| [name, "#{Dir.tmpdir}/hpc_test/#{name}"] }]
18
+ repositories.values.each do |dir|
19
+ FileUtils.rm_rf dir
20
+ FileUtils.mkdir_p dir
21
+ if as_git
22
+ git = Git.init(dir)
23
+ FileUtils.touch("#{dir}/test_file")
24
+ git.add('test_file')
25
+ git.config('user.name', 'Thats Me')
26
+ git.config('user.email', 'email@email.com')
27
+ git.commit('Test commit')
28
+ git.add_remote('origin', "https://my_remote.com/path/to/#{File.basename(dir)}.git")
29
+ end
30
+ end
31
+ begin
32
+ yield repositories
33
+ ensure
34
+ repositories.values.each do |dir|
35
+ FileUtils.rm_rf dir
36
+ end
37
+ end
38
+ end
39
+
40
+ # Setup a test repository.
41
+ # Clean-up at the end.
42
+ #
43
+ # Parameters::
44
+ # * *name* (String): Name of the directory to be used [default = 'platform_repo']
45
+ # * *as_git* (Boolean): Do we initialize those repositories as Git repositories? [default: false]
46
+ # * Proc: Code called with the repository created.
47
+ # * Parameters::
48
+ # * *repository* (String): Path to the repository
49
+ def with_repository(name = 'platform_repo', as_git: false)
50
+ with_repositories([name], as_git: as_git) do |repositories|
51
+ yield repositories[name]
52
+ end
53
+ end
54
+
55
+ # Setup a hpc_config.rb with a given content and call code when it's ready.
56
+ # Automatically sets the hpc_platforms env variable so that processes can then use it.
57
+ # Clean-up at the end.
58
+ #
59
+ # Parameters::
60
+ # * *content* (String): hpc_config.rb's content
61
+ # * Proc: Code called with the platforms.rb file created.
62
+ # * Parameters::
63
+ # * *hybrid_platforms_dir* (String): The hybrid-platforms directory
64
+ def with_platforms(content)
65
+ with_repository('hybrid-platforms') do |hybrid_platforms_dir|
66
+ File.write("#{hybrid_platforms_dir}/hpc_config.rb", content)
67
+ ENV['hpc_platforms'] = hybrid_platforms_dir
68
+ yield hybrid_platforms_dir
69
+ end
70
+ end
71
+
72
+ # Instantiate a test environment with several test platforms, ready to run tests
73
+ # Clean-up at the end.
74
+ #
75
+ # Parameters::
76
+ # * *platforms_info* (Hash<String,Object>): Platforms info for the test platform [default = {}]
77
+ # * *as_git* (Boolean): Do we initialize those repositories as Git repositories? [default = false]
78
+ # * *additional_platforms_content* (String): Additional platforms content to be added [default = '']
79
+ # * Proc: Code called with the environment ready
80
+ # * Parameters::
81
+ # * *repositories* (Hash<String,String>): Path to the repositories, per repository name
82
+ def with_test_platforms(platforms_info = {}, as_git = false, additional_platforms_content = '')
83
+ with_repositories(platforms_info.keys, as_git: as_git) do |repositories|
84
+ platform_types = []
85
+ with_platforms(repositories.map do |platform, dir|
86
+ platform_type = platforms_info[platform].key?(:platform_type) ? platforms_info[platform][:platform_type] : :test
87
+ platform_types << platform_type unless platform_types.include?(platform_type)
88
+ "#{platform_type}_platform path: '#{dir}'"
89
+ end.join("\n") + "\n#{additional_platforms_content}") do
90
+ register_platform_handlers(Hash[platform_types.map { |platform_type| [platform_type, HybridPlatformsConductorTest::PlatformHandlerPlugins.const_get(platform_type.to_s.split('_').collect(&:capitalize).join.to_sym)] }])
91
+ self.test_platforms_info = platforms_info
92
+ yield repositories
93
+ end
94
+ end
95
+ end
96
+
97
+ # Instantiate a test environment with a test platform handler, ready to run tests
98
+ # Clean-up at the end.
99
+ #
100
+ # Parameters::
101
+ # * *platform_info* (Hash<Symbol,Object>): Platform info for the test platform [default = {}]
102
+ # * *as_git* (Boolean): Do we initialize those repositories as Git repositories? [default = false]
103
+ # * *additional_platforms_content* (String): Additional platforms content to be added [default = '']
104
+ # * Proc: Code called with the environment ready
105
+ # * Parameters::
106
+ # * *repository* (String): Path to the repository
107
+ def with_test_platform(platform_info = {}, as_git = false, additional_platforms_content = '')
108
+ platform_name = as_git ? 'my_remote_platform' : 'platform'
109
+ with_test_platforms({ platform_name => platform_info }, as_git, additional_platforms_content) do |repositories|
110
+ yield repositories[platform_name]
111
+ end
112
+ end
113
+
114
+ # Get a test PlatformsHandler
115
+ #
116
+ # Result::
117
+ # * PlatformsHandler: PlatformsHandler on which we can do testing
118
+ def test_platforms_handler
119
+ @platforms_handler = HybridPlatformsConductor::PlatformsHandler.new logger: logger, logger_stderr: logger, config: test_config, cmd_runner: test_cmd_runner unless @platforms_handler
120
+ @platforms_handler
121
+ end
122
+
123
+ end
124
+
125
+ end
126
+
127
+ end
@@ -0,0 +1,48 @@
1
+ module HybridPlatformsConductorTest
2
+
3
+ module Helpers
4
+
5
+ module PluginsHelpers
6
+
7
+ # Register given plugins for a given plugin type
8
+ #
9
+ # Parameters::
10
+ # * *plugin_type* (Symbol): The plugin type
11
+ # * *plugins* (Hash<Symbol,Class>): The plugin classes, per plugin ID
12
+ # * *replace* (Boolean): Should we replace the plugins with the mocked ones, or only add them? [default: true]
13
+ def register_plugins(plugin_type, plugins, replace: true)
14
+ unless defined?(@plugins_to_mock)
15
+ # First time we invoke it: mock the call to Plugins
16
+ # List of plugins information to mock, per plugin type
17
+ # * *plugins* (Hash<Symbol,Class>): The mocked plugins
18
+ # * *replace* (Boolean): Should we replace the plugins or add them?
19
+ # Hash< Symbol, Hash<Symbol, Object> >
20
+ @plugins_to_mock = {}
21
+ allow(HybridPlatformsConductor::Plugins).to receive(:new).and_wrap_original do |original_new, plugins_type, init_plugin: nil, parse_gems: true, logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR)|
22
+ # If this plugin type is to be mocked, then don't parse gems and provide the mocked plugins instead
23
+ mocked_plugins = original_new.call(
24
+ plugins_type,
25
+ init_plugin: init_plugin,
26
+ parse_gems: @plugins_to_mock.key?(plugins_type) && @plugins_to_mock[plugins_type][:replace] ? false : parse_gems,
27
+ logger: logger,
28
+ logger_stderr: logger_stderr
29
+ )
30
+ if @plugins_to_mock.key?(plugins_type)
31
+ @plugins_to_mock[plugins_type][:plugins].each do |plugin_id, plugin_class|
32
+ mocked_plugins[plugin_id] = plugin_class
33
+ end
34
+ end
35
+ mocked_plugins
36
+ end
37
+ end
38
+ @plugins_to_mock[plugin_type] = {
39
+ plugins: plugins,
40
+ replace: replace
41
+ }
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,789 @@
1
+ module HybridPlatformsConductorTest
2
+
3
+ module Helpers
4
+
5
+ module ProvisionerProxmoxHelpers
6
+
7
+ # Setup a test platform for Proxmox tests
8
+ #
9
+ # Parameters::
10
+ # * *node_metadata* (Hash<Symbol,Object>): Extra node metadata [default: {}]
11
+ # * *environment* (String): Environment to be used [default: 'test']
12
+ # * Proc: Code called when everything is setup
13
+ # * Parameters::
14
+ # * *instance* (Provisioner): A new Provisioner instance targeting the Proxmox instance
15
+ # * *repository* (String): The platforms' repository
16
+ def with_test_proxmox_platform(node_metadata: {}, environment: 'test')
17
+ with_repository('platform') do |repository|
18
+ os_image_path = "#{repository}/os_image"
19
+ FileUtils.mkdir_p os_image_path
20
+ FileUtils.cp "#{__dir__}/../api/deployer/provisioners/proxmox/proxmox.json", "#{os_image_path}/proxmox.json"
21
+ with_platforms("
22
+ os_image :test_image, '#{os_image_path}'
23
+ test_platform path: '#{repository}'
24
+ proxmox(
25
+ api_url: 'https://my-proxmox.my-domain.com:8006',
26
+ sync_node: 'node',
27
+ test_config: {
28
+ pve_nodes: ['pve_node_name'],
29
+ vm_ips_list: %w[
30
+ 192.168.0.100
31
+ 192.168.0.101
32
+ ],
33
+ vm_ids_range: [1000, 1100],
34
+ coeff_ram_consumption: 10,
35
+ coeff_disk_consumption: 1,
36
+ expiration_period_secs: 24 * 60 * 60,
37
+ limits: {
38
+ nbr_vms_max: 5,
39
+ cpu_loads_thresholds: [10, 10, 10],
40
+ ram_percent_used_max: 0.75,
41
+ disk_percent_used_max: 0.75
42
+ }
43
+ },
44
+ vm_config: {
45
+ vm_dns_servers: ['8.8.8.8'],
46
+ vm_search_domain: 'my-domain.com',
47
+ vm_gateway: '192.168.0.1'
48
+ }
49
+ )
50
+ ") do
51
+ register_platform_handlers test: HybridPlatformsConductorTest::PlatformHandlerPlugins::Test
52
+ self.test_platforms_info = { 'platform' => {
53
+ nodes: { 'node' => { meta: { host_ip: '192.168.42.42', image: 'test_image' }.merge(node_metadata) } }
54
+ } }
55
+ instance = HybridPlatformsConductor::HpcPlugins::Provisioner::Proxmox.new(
56
+ 'node',
57
+ environment: environment,
58
+ logger: logger,
59
+ logger_stderr: logger,
60
+ config: test_config,
61
+ cmd_runner: test_cmd_runner,
62
+ nodes_handler: test_nodes_handler,
63
+ actions_executor: test_actions_executor
64
+ )
65
+ yield instance, repository
66
+ end
67
+ end
68
+ end
69
+
70
+ # Get a mocking code corresponding to a call to Proxmox.new.
71
+ # This code should mock Proxmox getting info about the existing nodes
72
+ #
73
+ # Parameters::
74
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
75
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
76
+ # * *proxmox_realm* (String or nil): Proxmox realm used to connect to Proxmox API [default: 'pam']
77
+ # * *nodes_info* (Array<Hash>): Nodes info returned by the Proxmox API [default: []]
78
+ # * *extra_expects* (Proc or nil): Code called for additional expectations on the proxmox instance, or nil if none [default: nil]
79
+ # * Parameters::
80
+ # * *proxmox* (Double): The mocked Proxmox instance
81
+ # Result::
82
+ # * Proc: Code called in place of Proxmox.new. Signature is the same as Proxmox.new.
83
+ def mock_proxmox_to_get_nodes_info(proxmox_user: nil, proxmox_password: nil, proxmox_realm: 'pam', nodes_info: [], extra_expects: nil)
84
+ proc do |url, pve_node, user, password, realm, options|
85
+ expect(url).to eq 'https://my-proxmox.my-domain.com:8006/api2/json/'
86
+ expect(pve_node).to eq 'my-proxmox'
87
+ expect(user).to eq proxmox_user
88
+ expect(password).to eq proxmox_password
89
+ expect(realm).to eq proxmox_realm
90
+ expect(options[:verify_ssl]).to eq false
91
+ proxmox = double 'Proxmox info instance'
92
+ # Mock initialization
93
+ expect(proxmox).to receive(:logger=) do
94
+ # Nothing
95
+ end
96
+ expect(proxmox).to receive(:logger_stderr=) do
97
+ # Nothing
98
+ end
99
+ # Mock checking existing nodes
100
+ expect(proxmox).to receive(:get).with('nodes') do
101
+ nodes_info
102
+ end
103
+ extra_expects.call(proxmox) unless extra_expects.nil?
104
+ proxmox
105
+ end
106
+ end
107
+
108
+ # Get a mocking code corresponding to a call to Proxmox.new.
109
+ # This code should mock Proxmox starting a node
110
+ #
111
+ # Parameters::
112
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
113
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
114
+ # * *task_name* (String): Proxmox start task name [default: 'UPID:pve_node_name:0000A504:6DEABF24:5F44669B:start::root@pam:']
115
+ # * *task_status* (String or nil): Proxmox start task status, or nil if no task status query is to be expected [default: 'OK']
116
+ # * *nbr_api_errors* (Integer): Number of API errors 500 to mock before getting a successful query [defaults: 0]
117
+ # Result::
118
+ # * Proc: Code called in place of Proxmox.new. Signature is the same as Proxmox.new.
119
+ def mock_proxmox_to_start_node(
120
+ proxmox_user: nil,
121
+ proxmox_password: nil,
122
+ task_name: 'UPID:pve_node_name:0000A504:6DEABF24:5F44669B:start::root@pam:',
123
+ task_status: 'OK',
124
+ nbr_api_errors: 0
125
+ )
126
+ proc do |url, pve_node, user, password, realm, options|
127
+ expect(url).to eq 'https://my-proxmox.my-domain.com:8006/api2/json/'
128
+ expect(pve_node).to eq 'my-proxmox'
129
+ expect(user).to eq proxmox_user
130
+ expect(password).to eq proxmox_password
131
+ expect(realm).to eq 'pam'
132
+ expect(options[:verify_ssl]).to eq false
133
+ proxmox = double 'Proxmox create instance'
134
+ # Mock initialization
135
+ expect(proxmox).to receive(:logger=) do
136
+ # Nothing
137
+ end
138
+ expect(proxmox).to receive(:logger_stderr=) do
139
+ # Nothing
140
+ end
141
+ # Mock start a container
142
+ idx_try = 0
143
+ expect(proxmox).to receive(:post).exactly(nbr_api_errors + (task_status.nil? ? 0 : 1)).times.with('nodes/pve_node_name/lxc/1024/status/start') do
144
+ idx_try += 1
145
+ idx_try <= nbr_api_errors ? 'NOK: error code = 500' : task_name
146
+ end
147
+ # Mock checking task status
148
+ unless task_status.nil?
149
+ # Mock checking task status
150
+ expect(proxmox).to receive(:get).with("nodes/pve_node_name/tasks/#{task_name}/status") do
151
+ { 'status' => task_status }
152
+ end
153
+ end
154
+ proxmox
155
+ end
156
+ end
157
+
158
+ # Get a mocking code corresponding to a call to Proxmox.new.
159
+ # This code should mock Proxmox stopping a node
160
+ #
161
+ # Parameters::
162
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
163
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
164
+ # * *task_status* (String): Proxmox stop task status [default: 'OK']
165
+ # Result::
166
+ # * Proc: Code called in place of Proxmox.new. Signature is the same as Proxmox.new.
167
+ def mock_proxmox_to_stop_node(
168
+ proxmox_user: nil,
169
+ proxmox_password: nil,
170
+ task_status: 'OK'
171
+ )
172
+ proc do |url, pve_node, user, password, realm, options|
173
+ expect(url).to eq 'https://my-proxmox.my-domain.com:8006/api2/json/'
174
+ expect(pve_node).to eq 'my-proxmox'
175
+ expect(user).to eq proxmox_user
176
+ expect(password).to eq proxmox_password
177
+ expect(realm).to eq 'pam'
178
+ expect(options[:verify_ssl]).to eq false
179
+ proxmox = double 'Proxmox create instance'
180
+ # Mock initialization
181
+ expect(proxmox).to receive(:logger=) do
182
+ # Nothing
183
+ end
184
+ expect(proxmox).to receive(:logger_stderr=) do
185
+ # Nothing
186
+ end
187
+ # Mock start a container
188
+ expect(proxmox).to receive(:post).with('nodes/pve_node_name/lxc/1024/status/stop') do
189
+ 'UPID:pve_node_name:0000A504:6DEABF24:5F44669B:stop::root@pam:'
190
+ end
191
+ # Mock checking task status
192
+ expect(proxmox).to receive(:get).with('nodes/pve_node_name/tasks/UPID:pve_node_name:0000A504:6DEABF24:5F44669B:stop::root@pam:/status') do
193
+ { 'status' => task_status }
194
+ end
195
+ proxmox
196
+ end
197
+ end
198
+
199
+ # Get a mocking code corresponding to a call to Proxmox.new.
200
+ # This code should mock Proxmox destroying a node
201
+ #
202
+ # Parameters::
203
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
204
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
205
+ # * *task_status* (String): Proxmox destroy task status [default: 'OK']
206
+ # Result::
207
+ # * Proc: Code called in place of Proxmox.new. Signature is the same as Proxmox.new.
208
+ def mock_proxmox_to_destroy_node(
209
+ proxmox_user: nil,
210
+ proxmox_password: nil,
211
+ task_status: 'OK'
212
+ )
213
+ proc do |url, pve_node, user, password, realm, options|
214
+ expect(url).to eq 'https://my-proxmox.my-domain.com:8006/api2/json/'
215
+ expect(pve_node).to eq 'my-proxmox'
216
+ expect(user).to eq proxmox_user
217
+ expect(password).to eq proxmox_password
218
+ expect(realm).to eq 'pam'
219
+ expect(options[:verify_ssl]).to eq false
220
+ proxmox = double 'Proxmox create instance'
221
+ # Mock initialization
222
+ expect(proxmox).to receive(:logger=) do
223
+ # Nothing
224
+ end
225
+ expect(proxmox).to receive(:logger_stderr=) do
226
+ # Nothing
227
+ end
228
+ # Mock start a container
229
+ expect(proxmox).to receive(:delete).with('nodes/pve_node_name/lxc/1024') do
230
+ 'UPID:pve_node_name:0000A504:6DEABF24:5F44669B:destroy::root@pam:'
231
+ end
232
+ # Mock checking task status
233
+ expect(proxmox).to receive(:get).with('nodes/pve_node_name/tasks/UPID:pve_node_name:0000A504:6DEABF24:5F44669B:destroy::root@pam:/status') do
234
+ { 'status' => task_status }
235
+ end
236
+ proxmox
237
+ end
238
+ end
239
+
240
+ # Get a mocking code corresponding to a call to Proxmox.new.
241
+ # This code should mock Proxmox getting the status a node
242
+ #
243
+ # Parameters::
244
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
245
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
246
+ # * *status* (String): Mocked status [default: 'created']
247
+ # Result::
248
+ # * Proc: Code called in place of Proxmox.new. Signature is the same as Proxmox.new.
249
+ def mock_proxmox_to_status_node(
250
+ proxmox_user: nil,
251
+ proxmox_password: nil,
252
+ task_status: 'OK'
253
+ )
254
+ proc do |url, pve_node, user, password, realm, options|
255
+ expect(url).to eq 'https://my-proxmox.my-domain.com:8006/api2/json/'
256
+ expect(pve_node).to eq 'my-proxmox'
257
+ expect(user).to eq proxmox_user
258
+ expect(password).to eq proxmox_password
259
+ expect(realm).to eq 'pam'
260
+ expect(options[:verify_ssl]).to eq false
261
+ proxmox = double 'Proxmox create instance'
262
+ # Mock initialization
263
+ expect(proxmox).to receive(:logger=) do
264
+ # Nothing
265
+ end
266
+ expect(proxmox).to receive(:logger_stderr=) do
267
+ # Nothing
268
+ end
269
+ # Mock getting status of a container
270
+ expect(proxmox).to receive(:get).with('nodes/pve_node_name/lxc') do
271
+ [
272
+ {
273
+ 'vmid' => '1024'
274
+ }
275
+ ]
276
+ end
277
+ expect(proxmox).to receive(:get).with('nodes/pve_node_name/lxc/1024/status/current') do
278
+ {
279
+ 'status' => 'created'
280
+ }
281
+ end
282
+ proxmox
283
+ end
284
+ end
285
+
286
+ # Mock a call to the reserve_proxmox_container sync node
287
+ #
288
+ # Parameters::
289
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
290
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
291
+ # * *proxmox_realm* (String): Proxmox realm used to connect to Proxmox API [default: 'pam']
292
+ # * *error_on_create* (String or nil): Error to be mocked by reserve_proxmox_container create, or nil in case of success [default: nil]
293
+ # * *error_on_destroy* (String or nil): Error to be mocked by reserve_proxmox_container destroy, or nil in case of success [default: nil]
294
+ # * *destroy_vm* (Boolean): Should we expect also a VM destruction? [default: false]
295
+ # * *expected_file_id* (String): The expected config file IDs used [default: 'node_test']
296
+ # * *expected_sudo* (Boolean): Is sudo to be expected? [default: true]
297
+ def mock_call_to_reserve_proxmox_container(
298
+ proxmox_user: nil,
299
+ proxmox_password: nil,
300
+ proxmox_realm: 'pam',
301
+ error_on_create: nil,
302
+ error_on_destroy: nil,
303
+ destroy_vm: false,
304
+ expected_file_id: 'node_test',
305
+ expected_sudo: true
306
+ )
307
+ runs = [
308
+ proc do |actions|
309
+ expect(actions.keys).to eq ['node']
310
+ expect(actions['node'].size).to eq 4
311
+ # First action should be to copy the reserve_proxmox_container code
312
+ expect(actions['node'][0].keys).to eq [:scp]
313
+ expect(actions['node'][0][:scp].first[0]).to match /^.+\/hpc_plugins\/provisioner\/proxmox\/$/
314
+ expect(actions['node'][0][:scp].first[1]).to eq '.'
315
+ # Second action should be to create directories
316
+ expect(actions['node'][1]).to eq({
317
+ remote_bash: "mkdir -p ./proxmox/config\nmkdir -p ./proxmox/create"
318
+ })
319
+ # Next actions should be to copy the config/create/destroy files
320
+ expect(actions['node'][2].keys).to eq [:scp]
321
+ expect(actions['node'][2][:scp].first[0]).to match /^.+\/create_#{Regexp.escape(expected_file_id)}\.json$/
322
+ expect(actions['node'][2][:scp].first[1]).to eq './proxmox/create'
323
+ expect(actions['node'][3].keys).to eq [:scp]
324
+ expect(actions['node'][3][:scp].first[0]).to match /^.+\/config_#{Regexp.escape(expected_file_id)}\.json$/
325
+ expect(actions['node'][3][:scp].first[1]).to eq './proxmox/config'
326
+ @proxmox_create_options = JSON.parse(File.read(actions['node'][2][:scp].first[0]))
327
+ { 'node' => [0, '', ''] }
328
+ end,
329
+ proc do |actions|
330
+ expect(actions).to eq({
331
+ 'node' => {
332
+ remote_bash: {
333
+ commands: "#{expected_sudo ? 'sudo -E ' : ''}./proxmox/reserve_proxmox_container --create ./proxmox/create/create_#{expected_file_id}.json --config ./proxmox/config/config_#{expected_file_id}.json",
334
+ env: {
335
+ 'hpc_user_for_proxmox' => proxmox_user,
336
+ 'hpc_password_for_proxmox' => proxmox_password,
337
+ 'hpc_realm_for_proxmox' => proxmox_realm
338
+ }
339
+ }
340
+ }
341
+ })
342
+ result =
343
+ if error_on_create
344
+ { error: error_on_create }
345
+ else
346
+ {
347
+ pve_node: 'pve_node_name',
348
+ vm_id: 1024,
349
+ vm_ip: '192.168.0.100'
350
+ }
351
+ end
352
+ { 'node' => [0, <<~EOS, ''] }
353
+ ===== JSON =====
354
+ #{JSON.pretty_generate(result)}
355
+ EOS
356
+ end
357
+ ]
358
+ if destroy_vm
359
+ runs.concat [
360
+ proc do |actions|
361
+ expect(actions.keys).to eq ['node']
362
+ expect(actions['node'].size).to eq 4
363
+ # First action should be to copy the reserve_proxmox_container code
364
+ expect(actions['node'][0].keys).to eq [:scp]
365
+ expect(actions['node'][0][:scp].first[0]).to match /^.+\/hpc_plugins\/provisioner\/proxmox\/$/
366
+ expect(actions['node'][0][:scp].first[1]).to eq '.'
367
+ # Second action should be to create directories
368
+ expect(actions['node'][1]).to eq({
369
+ remote_bash: "mkdir -p ./proxmox/config\nmkdir -p ./proxmox/destroy"
370
+ })
371
+ # Next actions should be to copy the config/create/destroy files
372
+ expect(actions['node'][2].keys).to eq [:scp]
373
+ expect(actions['node'][2][:scp].first[0]).to match /^.+\/destroy_#{Regexp.escape(expected_file_id)}\.json$/
374
+ expect(actions['node'][2][:scp].first[1]).to eq './proxmox/destroy'
375
+ expect(actions['node'][3].keys).to eq [:scp]
376
+ expect(actions['node'][3][:scp].first[0]).to match /^.+\/config_#{Regexp.escape(expected_file_id)}\.json$/
377
+ expect(actions['node'][3][:scp].first[1]).to eq './proxmox/config'
378
+ @proxmox_destroy_options = JSON.parse(File.read(actions['node'][2][:scp].first[0]))
379
+ { 'node' => [0, '', ''] }
380
+ end,
381
+ proc do |actions|
382
+ expect(actions).to eq({
383
+ 'node' => {
384
+ remote_bash: {
385
+ commands: "#{expected_sudo ? 'sudo -E ' : ''}./proxmox/reserve_proxmox_container --destroy ./proxmox/destroy/destroy_#{expected_file_id}.json --config ./proxmox/config/config_#{expected_file_id}.json",
386
+ env: {
387
+ 'hpc_user_for_proxmox' => proxmox_user,
388
+ 'hpc_password_for_proxmox' => proxmox_password,
389
+ 'hpc_realm_for_proxmox' => proxmox_realm
390
+ }
391
+ }
392
+ }
393
+ })
394
+ result =
395
+ if error_on_destroy
396
+ { error: error_on_destroy }
397
+ else
398
+ {
399
+ pve_node: 'pve_node_name',
400
+ vm_id: 1024,
401
+ vm_ip: '192.168.0.100'
402
+ }
403
+ end
404
+ { 'node' => [0, <<~EOS, ''] }
405
+ ===== JSON =====
406
+ #{JSON.pretty_generate(result)}
407
+ EOS
408
+ end
409
+ ]
410
+ end
411
+ expect_actions_executor_runs runs
412
+ end
413
+
414
+ # Mock a series of Proxmox calls, and expect them to occur.
415
+ #
416
+ # Parameters::
417
+ # * *calls* (Array<Proc>): List of mocked calls
418
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
419
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
420
+ # * *proxmox_realm* (String): Proxmox realm used to connect to Proxmox API [default: 'pam']
421
+ # * *error_on_create* (String or nil): Error to be mocked by reserve_proxmox_container create, or nil in case of success [default: nil]
422
+ # * *error_on_destroy* (String or nil): Error to be mocked by reserve_proxmox_container destroy, or nil in case of success [default: nil]
423
+ # * *reserve* (Boolean): Do we expect the resource reservation to occur? [default: true]
424
+ # * *destroy_vm* (Boolean): Should we expect also a VM destruction? [default: false]
425
+ # * *expected_file_id* (String): The expected config file IDs used [default: 'node_test']
426
+ # * *expected_sudo* (Boolean): Is sudo to be expected? [default: true]
427
+ def mock_proxmox_calls_with(
428
+ calls,
429
+ proxmox_user: nil,
430
+ proxmox_password: nil,
431
+ proxmox_realm: 'pam',
432
+ error_on_create: nil,
433
+ error_on_destroy: nil,
434
+ reserve: true,
435
+ destroy_vm: false,
436
+ expected_file_id: 'node_test',
437
+ expected_sudo: true
438
+ )
439
+ if reserve || destroy_vm
440
+ # Mock querying reserve_proxmox_container
441
+ mock_call_to_reserve_proxmox_container(
442
+ proxmox_user: proxmox_user,
443
+ proxmox_password: proxmox_password,
444
+ proxmox_realm: proxmox_realm,
445
+ error_on_create: error_on_create,
446
+ error_on_destroy: error_on_destroy,
447
+ destroy_vm: destroy_vm,
448
+ expected_file_id: expected_file_id,
449
+ expected_sudo: expected_sudo
450
+ )
451
+ end
452
+ expect(::Proxmox::Proxmox).to receive(:new).exactly(calls.size).times do |url, pve_node, user, password, realm, options|
453
+ calls.shift.call(url, pve_node, user, password, realm, options)
454
+ end
455
+ end
456
+
457
+ # Mock the Proxmox API calls to map a given Proxmox status.
458
+ # Mock any call to the paths of the API to serve a given nodes information.
459
+ # Mock also calls to stop and destroy containers, and log those actions so that they can be checked by the test case later.
460
+ #
461
+ # Parameters::
462
+ # * *proxmox_user* (String): Proxmox user to be used for the API, or nil if none [default: nil]
463
+ # * *proxmox_password* (String): Proxmox password to be used for the API, or nil if none [default: nil]
464
+ # * *proxmox_realm* (String): Proxmox realm to be used for the API, or nil if none [default: 'pam']
465
+ # * *mocked_pve_nodes* (Array< Hash< String, Hash<Symbol,Object> > > or Hash< String, Hash<Symbol,Object> >):
466
+ # List of (or single) PVE node information, per PVE node name. [default: { 'pve_node_name' => {} }]
467
+ # If used as a list, it is expected that the Proxmox API be used as many times as the number of items in the list, and the mocked info will follow the list order.
468
+ # Here are the properties of a PVE node:
469
+ # * *loadavg* ([Float, Float, Float]): Load average of the node [default: [0.1, 0.2, 0.3]]
470
+ # * *memory_total* (Integer): Bytes of RAM of this PVE node [default: 16 * 1024 * 1024 * 1024]
471
+ # * *storage_total* (Integer): Bytes of disk of this PVE node [default: 100 * 1024 * 1024 * 1024]
472
+ # * *lxc_containers* (Hash<Integer, Hash<Symbol,Object> >): LXC containers info, per VM ID [default: {}]
473
+ # * *maxdisk* (Integer): Bytes of disk allocated to this VM [default: 1024 * 1024 * 1024]
474
+ # * *maxmem* (Integer): Bytes of RAM allocated to this VM [default: 1024 * 1024 * 1024]
475
+ # * *cpus* (Integer): CPUs allocated to this VM [default: 1]
476
+ # * *ip* (String): IP allocated to this node [default: 192.168.0.<vmid % 254 + 1>]
477
+ # * *status* (String): Status of this node [default: 'running']
478
+ # * *debug* (Boolean): Do we mark the VM as debug? [default: false]
479
+ # * *node* (String): Node for which this given container has been created [default: 'test_node']
480
+ # * *environment* (String): Environment for which this given container has been created [default: 'test_env']
481
+ def mock_proxmox(
482
+ proxmox_user: nil,
483
+ proxmox_password: nil,
484
+ proxmox_realm: 'pam',
485
+ mocked_pve_nodes: { 'pve_node_name' => {} }
486
+ )
487
+ # List of proxmox actions that have been mocked and their corresponding properties
488
+ # Array< [Symbol, Object] >
489
+ @proxmox_actions = []
490
+ mocked_pve_nodes = [mocked_pve_nodes] unless mocked_pve_nodes.is_a?(Array)
491
+ mock_proxmox_calls_with(
492
+ mocked_pve_nodes.map do |pve_nodes|
493
+ # Complete pve_nodes with default values
494
+ pve_nodes = Hash[pve_nodes.map do |pve_node_name, pve_node_info|
495
+ pve_node_info[:lxc_containers] = Hash[(pve_node_info.key?(:lxc_containers) ? pve_node_info[:lxc_containers] : {}).map do |vm_id, vm_info|
496
+ [
497
+ vm_id,
498
+ {
499
+ maxdisk: 1024 * 1024 * 1024,
500
+ maxmem: 1024 * 1024 * 1024,
501
+ cpus: 1,
502
+ ip: "192.168.0.#{(vm_id % 254) + 1}",
503
+ status: 'running',
504
+ creation_date: (Time.now - 60).utc,
505
+ debug: false,
506
+ node: 'test_node',
507
+ environment: 'test_env'
508
+ }.merge(vm_info)
509
+ ]
510
+ end]
511
+ [
512
+ pve_node_name,
513
+ {
514
+ loadavg: [0.1, 0.2, 0.3],
515
+ memory_total: 16 * 1024 * 1024 * 1024,
516
+ storage_total: 100 * 1024 * 1024 * 1024
517
+ }.merge(pve_node_info)
518
+ ]
519
+ end]
520
+ proc do |url, pve_node, user, password, realm, options|
521
+ expect(url).to eq 'https://my-proxmox.my-domain.com:8006/api2/json/'
522
+ expect(pve_node).to eq 'my-proxmox'
523
+ expect(user).to eq proxmox_user
524
+ expect(password).to eq proxmox_password
525
+ expect(realm).to eq proxmox_realm
526
+ expect(options[:verify_ssl]).to eq false
527
+ proxmox = double 'Proxmox create instance'
528
+ # Mock getting status of a container
529
+ allow(proxmox).to receive(:get) do |path|
530
+ case path
531
+ when 'nodes'
532
+ pve_nodes.keys.map { |pve_node_name| { 'node' => pve_node_name } }
533
+ when /^nodes\/([^\/]+)\/status$/
534
+ pve_node_name = $1
535
+ {
536
+ 'loadavg' => pve_nodes[pve_node_name][:loadavg].map(&:to_s),
537
+ 'memory' => {
538
+ 'total' => pve_nodes[pve_node_name][:memory_total]
539
+ }
540
+ }
541
+ when /^nodes\/([^\/]+)\/storage$/
542
+ pve_node_name = $1
543
+ [
544
+ {
545
+ 'storage' => 'local-lvm',
546
+ 'total' => pve_nodes[pve_node_name][:storage_total]
547
+ }
548
+ ]
549
+ when /^nodes\/([^\/]+)\/lxc$/
550
+ pve_node_name = $1
551
+ pve_nodes[pve_node_name][:lxc_containers].map do |vm_id, vm_info|
552
+ {
553
+ 'vmid' => vm_id.to_s,
554
+ 'maxdisk' => vm_info[:maxdisk],
555
+ 'maxmem' => vm_info[:maxmem],
556
+ 'cpus' => vm_info[:cpus]
557
+ }
558
+ end
559
+ when /^nodes\/([^\/]+)\/lxc\/([^\/]+)\/config$/
560
+ pve_node_name = $1
561
+ vmid = $2
562
+ {
563
+ 'net0' => "ip=#{pve_nodes[pve_node_name][:lxc_containers][Integer(vmid)][:ip]}/32",
564
+ 'description' => <<~EOS
565
+ ===== HPC info =====
566
+ node: #{pve_nodes[pve_node_name][:lxc_containers][Integer(vmid)][:node]}
567
+ environment: #{pve_nodes[pve_node_name][:lxc_containers][Integer(vmid)][:environment]}
568
+ debug: #{pve_nodes[pve_node_name][:lxc_containers][Integer(vmid)][:debug] ? 'true' : 'false'}
569
+ creation_date: #{pve_nodes[pve_node_name][:lxc_containers][Integer(vmid)][:creation_date].strftime('%FT%T')}
570
+ EOS
571
+ }
572
+ when /^nodes\/([^\/]+)\/lxc\/([^\/]+)\/status\/current$/
573
+ pve_node_name = $1
574
+ vmid = $2
575
+ {
576
+ 'status' => pve_nodes[pve_node_name][:lxc_containers][Integer(vmid)][:status]
577
+ }
578
+ when /^nodes\/([^\/]+)\/tasks\/([^\/]+)\/status$/
579
+ pve_node_name = $1
580
+ task = $2
581
+ # Mock tasks completion
582
+ {
583
+ 'status' => 'OK'
584
+ }
585
+ else
586
+ raise "Unknown Proxmox API get call: #{path}. Please adapt the test framework."
587
+ end
588
+ end
589
+ # Mock some post actions
590
+ allow(proxmox).to receive(:post) do |path, args|
591
+ @proxmox_actions << [:post, path, args].compact
592
+ case path
593
+ when /^nodes\/([^\/]+)\/lxc$/
594
+ pve_node_name = $1
595
+ "UPID:#{pve_node_name}:0000A504:6DEABF24:5F44669B:create::root@pam:"
596
+ when /^nodes\/([^\/]+)\/lxc\/([^\/]+)\/status\/stop$/
597
+ pve_node_name = $1
598
+ vmid = $2
599
+ "UPID:#{pve_node_name}:0000A504:6DEABF24:5F44669B:stop_#{vmid}::root@pam:"
600
+ else
601
+ raise "Unknown Proxmox API post call: #{path}. Please adapt the test framework."
602
+ end
603
+ end
604
+ # Mock some delete actions
605
+ allow(proxmox).to receive(:delete) do |path|
606
+ @proxmox_actions << [:delete, path]
607
+ case path
608
+ when /^nodes\/([^\/]+)\/lxc\/([^\/]+)$/
609
+ pve_node_name = $1
610
+ vmid = $2
611
+ # Make sure we delete the mocked information as well
612
+ pve_nodes[pve_node_name][:lxc_containers].delete(Integer(vmid))
613
+ "UPID:#{pve_node_name}:0000A504:6DEABF24:5F44669B:destroy_#{vmid}::root@pam:"
614
+ else
615
+ raise "Unknown Proxmox API post call: #{path}. Please adapt the test framework."
616
+ end
617
+ end
618
+ proxmox
619
+ end
620
+ end,
621
+ reserve: false
622
+ )
623
+ end
624
+
625
+ # Prepare a repository to test reserve_proxmox_container
626
+ #
627
+ # Parameters::
628
+ # * Proc: Code to be called with repository setup
629
+ def with_sync_node
630
+ with_repository('sync_node') do |repository|
631
+ @repository = repository
632
+ yield
633
+ end
634
+ end
635
+
636
+ # Call the reserve_proxmox_container script using a given ARGV.
637
+ # Prerequisite: This is called within a with_sync_node session.
638
+ #
639
+ # Parameters::
640
+ # * *config* (Hash): Configuration overriding defaults to store in the config file [default: {}]
641
+ # * *max_retries* (Integer): Specify the max number of retries [default: 1]
642
+ # * *wait_before_retry* (Integer): Specify the number of seconds to wait before retry [default: 0]
643
+ # * *create* (Hash or nil): Create file content, or nil if none [default: nil]
644
+ # * *destroy* (Hash or nil): Destroy file content, or nil if none [default: nil]
645
+ # Result::
646
+ # * Hash: JSON result of the call
647
+ def call_reserve_proxmox_container_with(config: {}, max_retries: 1, wait_before_retry: 0, create: nil, destroy: nil)
648
+ # Make sure we set default values in the config
649
+ config = {
650
+ proxmox_api_url: 'https://my-proxmox.my-domain.com:8006',
651
+ futex_file: "#{@repository}/proxmox/allocations.futex",
652
+ logs_dir: "#{Dir.tmpdir}/hpc_test_proxmox_waiter_logs",
653
+ pve_nodes: ['pve_node_name'],
654
+ vm_ips_list: %w[
655
+ 192.168.0.100
656
+ 192.168.0.101
657
+ 192.168.0.102
658
+ ],
659
+ vm_ids_range: [1000, 1100],
660
+ coeff_ram_consumption: 10,
661
+ coeff_disk_consumption: 1,
662
+ expiration_period_secs: 24 * 60 * 60,
663
+ expire_stopped_vm_timeout_secs: 3,
664
+ limits: {
665
+ nbr_vms_max: 5,
666
+ cpu_loads_thresholds: [10, 10, 10],
667
+ ram_percent_used_max: 0.75,
668
+ disk_percent_used_max: 0.75
669
+ }
670
+ }.merge(config)
671
+ FileUtils.cp_r "#{__dir__}/../../../lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox", @repository
672
+ File.write("#{@repository}/proxmox/config.json", config.to_json)
673
+ script_args = [
674
+ '--max-retries', max_retries.to_s,
675
+ '--wait-before-retry', wait_before_retry.to_s
676
+ ]
677
+ unless create.nil?
678
+ create_file = "#{@repository}/proxmox/create_vm.json"
679
+ File.write(create_file, create.to_json)
680
+ script_args.concat(['--create', create_file])
681
+ end
682
+ unless destroy.nil?
683
+ destroy_file = "#{@repository}/proxmox/destroy_vm.json"
684
+ File.write(destroy_file, destroy.to_json)
685
+ script_args.concat(['--destroy', destroy_file])
686
+ end
687
+ # Call the script by loading the Ruby file mocking the ARGV and ENV variables
688
+ old_argv = ARGV.dup
689
+ old_stdout = $stdout
690
+ ARGV.replace(script_args)
691
+ $stdout = StringIO.new unless logger.debug?
692
+ begin
693
+ load "#{@repository}/proxmox/reserve_proxmox_container"
694
+ if logger.debug?
695
+ raise 'This test can\'t run in debug mode.'
696
+ else
697
+ @stdout = $stdout.string
698
+ end
699
+ ensure
700
+ ARGV.replace old_argv
701
+ $stdout = old_stdout
702
+ end
703
+ stdout_lines = @stdout.split("\n")
704
+ JSON.parse(stdout_lines[stdout_lines.index('===== JSON =====') + 1..-1].join("\n")).transform_keys(&:to_sym)
705
+ end
706
+
707
+ # Call the reserve_proxmox_container script and get its result as JSON.
708
+ # Prerequisite: This is called within a with_sync_node session.
709
+ #
710
+ # Parameters::
711
+ # * *cpus* (Integer): Required CPUs
712
+ # * *ram_mb* (Integer): Required RAM MB
713
+ # * *disk_gb* (Integer): Required Disk GB
714
+ # * *config* (Hash): Configuration overriding defaults to store in the config file [default: {}]
715
+ # * *max_retries* (Integer): Specify the max number of retries [default: 1]
716
+ # * *wait_before_retry* (Integer): Specify the number of seconds to wait before retry [default: 0]
717
+ # Result::
718
+ # * Hash: JSON result of the call
719
+ def call_reserve_proxmox_container(cpus, ram_mb, disk_gb, config: {}, max_retries: 1, wait_before_retry: 0)
720
+ call_reserve_proxmox_container_with(
721
+ config: config,
722
+ max_retries: max_retries,
723
+ wait_before_retry: wait_before_retry,
724
+ create: {
725
+ ostemplate: 'test_template.iso',
726
+ hostname: 'test.hostname.my-domain.com',
727
+ description: "===== HPC info =====\nnode: test_node\nenvironment: test_env\n",
728
+ cores: cpus,
729
+ cpulimit: cpus,
730
+ memory: ram_mb,
731
+ rootfs: "local-lvm:#{disk_gb}",
732
+ net0: 'name=eth0,bridge=vmbr0,gw=172.16.16.16'
733
+ }
734
+ )
735
+ end
736
+
737
+ # Call the reserve_proxmox_container script to release a VM and get its result as JSON.
738
+ # Prerequisite: This is called within a with_sync_node session.
739
+ #
740
+ # Parameters::
741
+ # * *vm_id* (Integer): VM ID to release
742
+ # * *node* (String): Node for which the VM has been reserved
743
+ # * *environment* (String): Environment for which the VM has been reserved
744
+ # * *config* (Hash): Configuration overriding defaults to store in the config file [default: {}]
745
+ # * *max_retries* (Integer): Specify the max number of retries [default: 1]
746
+ # Result::
747
+ # * Hash: JSON result of the call
748
+ def call_release_proxmox_container(vm_id, node, environment, config: {}, max_retries: 1)
749
+ call_reserve_proxmox_container_with(
750
+ config: config,
751
+ max_retries: max_retries,
752
+ destroy: {
753
+ vm_id: vm_id,
754
+ node: node,
755
+ environment: environment
756
+ }
757
+ )
758
+ end
759
+
760
+ # Expect a list of Proxmox API calls to match a given list.
761
+ # Handle Regexp in the expectation.
762
+ #
763
+ # Parameters::
764
+ # * *expected_proxmox_actions* (Array<Array>): Expected Proxmox actions
765
+ def expect_proxmox_actions_to_be(expected_proxmox_actions)
766
+ expect(@proxmox_actions.size).to eq expected_proxmox_actions.size
767
+ @proxmox_actions.zip(expected_proxmox_actions).each do |proxmox_action, expected_proxmox_action|
768
+ expect(proxmox_action.size).to eq expected_proxmox_action.size
769
+ expect(proxmox_action[0..1]).to eq expected_proxmox_action[0..1]
770
+ if proxmox_action.size >= 3
771
+ # The third argument is a Hash that might have Regexp in the expectation
772
+ expect(proxmox_action[2].keys.sort).to eq expected_proxmox_action[2].keys.sort
773
+ proxmox_action[2].each do |property, value|
774
+ expected_value = expected_proxmox_action[2][property]
775
+ if expected_value.is_a?(Regexp)
776
+ expect(value).to match expected_value
777
+ else
778
+ expect(value).to eq expected_value
779
+ end
780
+ end
781
+ end
782
+ end
783
+ end
784
+
785
+ end
786
+
787
+ end
788
+
789
+ end