mtncloud-cli 1.0.0

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 (499) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +20 -0
  3. data/.github/pull_request_template.md +14 -0
  4. data/.github/workflows/ci.yml +56 -0
  5. data/.github/workflows/release.yml +46 -0
  6. data/.gitignore +25 -0
  7. data/CLAUDE.md +24 -0
  8. data/CONTRIBUTING.md +91 -0
  9. data/Dockerfile +19 -0
  10. data/Gemfile +4 -0
  11. data/LICENSE.txt +23 -0
  12. data/README.md +187 -0
  13. data/Rakefile +26 -0
  14. data/bin/mcs +3 -0
  15. data/bin/mtncloud +27 -0
  16. data/lib/mtncloud/api/account_groups_interface.rb +48 -0
  17. data/lib/mtncloud/api/account_users_interface.rb +68 -0
  18. data/lib/mtncloud/api/accounts_interface.rb +9 -0
  19. data/lib/mtncloud/api/activity_interface.rb +9 -0
  20. data/lib/mtncloud/api/affinity_groups_interface.rb +46 -0
  21. data/lib/mtncloud/api/api_client.rb +1091 -0
  22. data/lib/mtncloud/api/appliance_settings_interface.rb +49 -0
  23. data/lib/mtncloud/api/approvals_interface.rb +32 -0
  24. data/lib/mtncloud/api/apps_interface.rb +180 -0
  25. data/lib/mtncloud/api/archive_buckets_interface.rb +116 -0
  26. data/lib/mtncloud/api/archive_files_interface.rb +173 -0
  27. data/lib/mtncloud/api/audit_interface.rb +9 -0
  28. data/lib/mtncloud/api/auth_interface.rb +58 -0
  29. data/lib/mtncloud/api/backup_jobs_interface.rb +13 -0
  30. data/lib/mtncloud/api/backup_restores_interface.rb +27 -0
  31. data/lib/mtncloud/api/backup_results_interface.rb +32 -0
  32. data/lib/mtncloud/api/backup_service_types_interface.rb +9 -0
  33. data/lib/mtncloud/api/backup_services_interface.rb +9 -0
  34. data/lib/mtncloud/api/backup_settings_interface.rb +20 -0
  35. data/lib/mtncloud/api/backup_types_interface.rb +9 -0
  36. data/lib/mtncloud/api/backups_interface.rb +21 -0
  37. data/lib/mtncloud/api/billing_interface.rb +34 -0
  38. data/lib/mtncloud/api/blueprints_interface.rb +70 -0
  39. data/lib/mtncloud/api/body_io.rb +22 -0
  40. data/lib/mtncloud/api/budgets_interface.rb +48 -0
  41. data/lib/mtncloud/api/catalog_item_types_interface.rb +29 -0
  42. data/lib/mtncloud/api/certificate_types_interface.rb +14 -0
  43. data/lib/mtncloud/api/certificates_interface.rb +9 -0
  44. data/lib/mtncloud/api/clients_interface.rb +41 -0
  45. data/lib/mtncloud/api/cloud_datastores_interface.rb +48 -0
  46. data/lib/mtncloud/api/cloud_folders_interface.rb +41 -0
  47. data/lib/mtncloud/api/cloud_policies_interface.rb +41 -0
  48. data/lib/mtncloud/api/cloud_resource_pools_interface.rb +66 -0
  49. data/lib/mtncloud/api/clouds_interface.rb +217 -0
  50. data/lib/mtncloud/api/clusters_interface.rb +471 -0
  51. data/lib/mtncloud/api/containers_interface.rb +145 -0
  52. data/lib/mtncloud/api/credential_types_interface.rb +9 -0
  53. data/lib/mtncloud/api/credentials_interface.rb +9 -0
  54. data/lib/mtncloud/api/custom_instance_types_interface.rb +99 -0
  55. data/lib/mtncloud/api/cypher_interface.rb +51 -0
  56. data/lib/mtncloud/api/dashboard_interface.rb +14 -0
  57. data/lib/mtncloud/api/datastores_interface.rb +21 -0
  58. data/lib/mtncloud/api/deploy_interface.rb +44 -0
  59. data/lib/mtncloud/api/deployments_interface.rb +79 -0
  60. data/lib/mtncloud/api/distributed_workers_interface.rb +41 -0
  61. data/lib/mtncloud/api/email_templates_interface.rb +53 -0
  62. data/lib/mtncloud/api/environments_interface.rb +48 -0
  63. data/lib/mtncloud/api/execute_schedules_interface.rb +71 -0
  64. data/lib/mtncloud/api/execution_request_interface.rb +27 -0
  65. data/lib/mtncloud/api/file_copy_request_interface.rb +68 -0
  66. data/lib/mtncloud/api/forgot_password_interface.rb +17 -0
  67. data/lib/mtncloud/api/group_policies_interface.rb +41 -0
  68. data/lib/mtncloud/api/groups_interface.rb +73 -0
  69. data/lib/mtncloud/api/guidance_interface.rb +44 -0
  70. data/lib/mtncloud/api/guidance_settings_interface.rb +17 -0
  71. data/lib/mtncloud/api/health_interface.rb +100 -0
  72. data/lib/mtncloud/api/host_vm_groups_interface.rb +37 -0
  73. data/lib/mtncloud/api/hub_interface.rb +25 -0
  74. data/lib/mtncloud/api/image_builder_boot_scripts_interface.rb +41 -0
  75. data/lib/mtncloud/api/image_builder_image_builds_interface.rb +73 -0
  76. data/lib/mtncloud/api/image_builder_interface.rb +20 -0
  77. data/lib/mtncloud/api/image_builder_preseed_scripts_interface.rb +41 -0
  78. data/lib/mtncloud/api/instance_types_interface.rb +20 -0
  79. data/lib/mtncloud/api/instances_interface.rb +453 -0
  80. data/lib/mtncloud/api/integration_types_interface.rb +14 -0
  81. data/lib/mtncloud/api/integrations_interface.rb +74 -0
  82. data/lib/mtncloud/api/invoice_line_items_interface.rb +9 -0
  83. data/lib/mtncloud/api/invoices_interface.rb +28 -0
  84. data/lib/mtncloud/api/jobs_interface.rb +83 -0
  85. data/lib/mtncloud/api/key_pairs_interface.rb +57 -0
  86. data/lib/mtncloud/api/library_cluster_layouts_interface.rb +47 -0
  87. data/lib/mtncloud/api/library_cluster_packages_interface.rb +60 -0
  88. data/lib/mtncloud/api/library_container_scripts_interface.rb +43 -0
  89. data/lib/mtncloud/api/library_container_templates_interface.rb +43 -0
  90. data/lib/mtncloud/api/library_container_types_interface.rb +59 -0
  91. data/lib/mtncloud/api/library_container_upgrades_interface.rb +60 -0
  92. data/lib/mtncloud/api/library_instance_types_interface.rb +74 -0
  93. data/lib/mtncloud/api/library_layouts_interface.rb +69 -0
  94. data/lib/mtncloud/api/library_operating_systems_interface.rb +63 -0
  95. data/lib/mtncloud/api/library_spec_template_types_interface.rb +43 -0
  96. data/lib/mtncloud/api/library_spec_templates_interface.rb +43 -0
  97. data/lib/mtncloud/api/license_interface.rb +30 -0
  98. data/lib/mtncloud/api/load_balancer_monitors_interface.rb +9 -0
  99. data/lib/mtncloud/api/load_balancer_pool_nodes_interface.rb +8 -0
  100. data/lib/mtncloud/api/load_balancer_pools_interface.rb +9 -0
  101. data/lib/mtncloud/api/load_balancer_pools_secondary_interface.rb +9 -0
  102. data/lib/mtncloud/api/load_balancer_profiles_interface.rb +9 -0
  103. data/lib/mtncloud/api/load_balancer_types_interface.rb +9 -0
  104. data/lib/mtncloud/api/load_balancer_virtual_servers_interface.rb +18 -0
  105. data/lib/mtncloud/api/load_balancers_interface.rb +14 -0
  106. data/lib/mtncloud/api/log_settings_interface.rb +44 -0
  107. data/lib/mtncloud/api/logs_interface.rb +37 -0
  108. data/lib/mtncloud/api/migrations_interface.rb +49 -0
  109. data/lib/mtncloud/api/monitoring_alerts_interface.rb +39 -0
  110. data/lib/mtncloud/api/monitoring_apps_interface.rb +69 -0
  111. data/lib/mtncloud/api/monitoring_checks_interface.rb +103 -0
  112. data/lib/mtncloud/api/monitoring_contacts_interface.rb +41 -0
  113. data/lib/mtncloud/api/monitoring_groups_interface.rb +77 -0
  114. data/lib/mtncloud/api/monitoring_incidents_interface.rb +107 -0
  115. data/lib/mtncloud/api/monitoring_interface.rb +35 -0
  116. data/lib/mtncloud/api/monitoring_settings_interface.rb +21 -0
  117. data/lib/mtncloud/api/network_dhcp_relays_interface.rb +36 -0
  118. data/lib/mtncloud/api/network_dhcp_servers_interface.rb +36 -0
  119. data/lib/mtncloud/api/network_domain_records_interface.rb +41 -0
  120. data/lib/mtncloud/api/network_domains_interface.rb +41 -0
  121. data/lib/mtncloud/api/network_edge_clusters_interface.rb +26 -0
  122. data/lib/mtncloud/api/network_floating_ips_interface.rb +40 -0
  123. data/lib/mtncloud/api/network_groups_interface.rb +41 -0
  124. data/lib/mtncloud/api/network_pool_ips_interface.rb +41 -0
  125. data/lib/mtncloud/api/network_pool_server_types_interface.rb +9 -0
  126. data/lib/mtncloud/api/network_pool_servers_interface.rb +48 -0
  127. data/lib/mtncloud/api/network_pools_interface.rb +41 -0
  128. data/lib/mtncloud/api/network_proxies_interface.rb +41 -0
  129. data/lib/mtncloud/api/network_routers_interface.rb +181 -0
  130. data/lib/mtncloud/api/network_security_server_types_interface.rb +9 -0
  131. data/lib/mtncloud/api/network_security_servers_interface.rb +27 -0
  132. data/lib/mtncloud/api/network_server_groups_interface.rb +7 -0
  133. data/lib/mtncloud/api/network_server_types_interface.rb +9 -0
  134. data/lib/mtncloud/api/network_servers_interface.rb +102 -0
  135. data/lib/mtncloud/api/network_services_interface.rb +63 -0
  136. data/lib/mtncloud/api/network_static_routes_interface.rb +36 -0
  137. data/lib/mtncloud/api/network_subnets_interface.rb +42 -0
  138. data/lib/mtncloud/api/network_types_interface.rb +20 -0
  139. data/lib/mtncloud/api/networks_interface.rb +48 -0
  140. data/lib/mtncloud/api/option_type_forms_interface.rb +9 -0
  141. data/lib/mtncloud/api/option_type_lists_interface.rb +53 -0
  142. data/lib/mtncloud/api/option_types_interface.rb +41 -0
  143. data/lib/mtncloud/api/options_interface.rb +17 -0
  144. data/lib/mtncloud/api/packages_interface.rb +112 -0
  145. data/lib/mtncloud/api/ping_interface.rb +22 -0
  146. data/lib/mtncloud/api/plugins_interface.rb +22 -0
  147. data/lib/mtncloud/api/policies_interface.rb +56 -0
  148. data/lib/mtncloud/api/power_schedules_interface.rb +71 -0
  149. data/lib/mtncloud/api/price_sets_interface.rb +39 -0
  150. data/lib/mtncloud/api/prices_interface.rb +71 -0
  151. data/lib/mtncloud/api/processes_interface.rb +40 -0
  152. data/lib/mtncloud/api/projects_interface.rb +33 -0
  153. data/lib/mtncloud/api/provision_types_interface.rb +24 -0
  154. data/lib/mtncloud/api/provisioning_license_types_interface.rb +31 -0
  155. data/lib/mtncloud/api/provisioning_licenses_interface.rb +60 -0
  156. data/lib/mtncloud/api/provisioning_settings_interface.rb +26 -0
  157. data/lib/mtncloud/api/read_interface.rb +24 -0
  158. data/lib/mtncloud/api/reports_interface.rb +70 -0
  159. data/lib/mtncloud/api/resource_pool_groups_interface.rb +51 -0
  160. data/lib/mtncloud/api/rest_interface.rb +37 -0
  161. data/lib/mtncloud/api/roles_interface.rb +151 -0
  162. data/lib/mtncloud/api/scale_thresholds_interface.rb +9 -0
  163. data/lib/mtncloud/api/search_interface.rb +13 -0
  164. data/lib/mtncloud/api/secondary_read_interface.rb +25 -0
  165. data/lib/mtncloud/api/secondary_rest_interface.rb +42 -0
  166. data/lib/mtncloud/api/security_group_rules_interface.rb +35 -0
  167. data/lib/mtncloud/api/security_groups_interface.rb +56 -0
  168. data/lib/mtncloud/api/security_package_types_interface.rb +9 -0
  169. data/lib/mtncloud/api/security_packages_interface.rb +9 -0
  170. data/lib/mtncloud/api/security_scans_interface.rb +9 -0
  171. data/lib/mtncloud/api/server_devices_interface.rb +30 -0
  172. data/lib/mtncloud/api/server_types_interface.rb +20 -0
  173. data/lib/mtncloud/api/servers_interface.rb +230 -0
  174. data/lib/mtncloud/api/service_catalog_interface.rb +89 -0
  175. data/lib/mtncloud/api/service_plans_interface.rb +62 -0
  176. data/lib/mtncloud/api/setup_interface.rb +46 -0
  177. data/lib/mtncloud/api/snapshots_interface.rb +19 -0
  178. data/lib/mtncloud/api/storage_datastores_interface.rb +44 -0
  179. data/lib/mtncloud/api/storage_providers_interface.rb +151 -0
  180. data/lib/mtncloud/api/storage_server_types_interface.rb +14 -0
  181. data/lib/mtncloud/api/storage_servers_interface.rb +9 -0
  182. data/lib/mtncloud/api/storage_volume_types_interface.rb +9 -0
  183. data/lib/mtncloud/api/storage_volumes_interface.rb +24 -0
  184. data/lib/mtncloud/api/subnet_types_interface.rb +20 -0
  185. data/lib/mtncloud/api/subnets_interface.rb +41 -0
  186. data/lib/mtncloud/api/support_bundles_interface.rb +46 -0
  187. data/lib/mtncloud/api/system_types_interface.rb +13 -0
  188. data/lib/mtncloud/api/systems_interface.rb +55 -0
  189. data/lib/mtncloud/api/task_sets_interface.rb +52 -0
  190. data/lib/mtncloud/api/tasks_interface.rb +66 -0
  191. data/lib/mtncloud/api/tokens_interface.rb +39 -0
  192. data/lib/mtncloud/api/usage_interface.rb +18 -0
  193. data/lib/mtncloud/api/user_groups_interface.rb +59 -0
  194. data/lib/mtncloud/api/user_settings_interface.rb +90 -0
  195. data/lib/mtncloud/api/user_sources_interface.rb +99 -0
  196. data/lib/mtncloud/api/users_interface.rb +33 -0
  197. data/lib/mtncloud/api/vdi_allocations_interface.rb +9 -0
  198. data/lib/mtncloud/api/vdi_apps_interface.rb +9 -0
  199. data/lib/mtncloud/api/vdi_gateways_interface.rb +9 -0
  200. data/lib/mtncloud/api/vdi_interface.rb +28 -0
  201. data/lib/mtncloud/api/vdi_pools_interface.rb +19 -0
  202. data/lib/mtncloud/api/virtual_images_interface.rb +210 -0
  203. data/lib/mtncloud/api/virtual_servers_interface.rb +9 -0
  204. data/lib/mtncloud/api/virtual_switches_interface.rb +9 -0
  205. data/lib/mtncloud/api/whitelabel_settings_interface.rb +57 -0
  206. data/lib/mtncloud/api/whoami_interface.rb +13 -0
  207. data/lib/mtncloud/api/wiki_interface.rb +48 -0
  208. data/lib/mtncloud/api.rb +12 -0
  209. data/lib/mtncloud/benchmarking.rb +259 -0
  210. data/lib/mtncloud/cli/cli_command.rb +2281 -0
  211. data/lib/mtncloud/cli/cli_registry.rb +337 -0
  212. data/lib/mtncloud/cli/commands/access_token_command.rb +201 -0
  213. data/lib/mtncloud/cli/commands/account_groups_command.rb +530 -0
  214. data/lib/mtncloud/cli/commands/activity_command.rb +149 -0
  215. data/lib/mtncloud/cli/commands/affinity_groups_command.rb +554 -0
  216. data/lib/mtncloud/cli/commands/alias_command.rb +278 -0
  217. data/lib/mtncloud/cli/commands/appliance_settings_command.rb +382 -0
  218. data/lib/mtncloud/cli/commands/approvals_command.rb +236 -0
  219. data/lib/mtncloud/cli/commands/apps.rb +2549 -0
  220. data/lib/mtncloud/cli/commands/archives_command.rb +2161 -0
  221. data/lib/mtncloud/cli/commands/audit.rb +188 -0
  222. data/lib/mtncloud/cli/commands/backup_jobs_command.rb +366 -0
  223. data/lib/mtncloud/cli/commands/backup_restores_command.rb +144 -0
  224. data/lib/mtncloud/cli/commands/backup_results_command.rb +149 -0
  225. data/lib/mtncloud/cli/commands/backup_services_command.rb +44 -0
  226. data/lib/mtncloud/cli/commands/backup_settings_command.rb +184 -0
  227. data/lib/mtncloud/cli/commands/backup_types_command.rb +53 -0
  228. data/lib/mtncloud/cli/commands/backups_command.rb +674 -0
  229. data/lib/mtncloud/cli/commands/benchmark_command.rb +458 -0
  230. data/lib/mtncloud/cli/commands/blueprints_command.rb +2297 -0
  231. data/lib/mtncloud/cli/commands/boot_scripts_command.rb +379 -0
  232. data/lib/mtncloud/cli/commands/budgets_command.rb +754 -0
  233. data/lib/mtncloud/cli/commands/cat_command.rb +45 -0
  234. data/lib/mtncloud/cli/commands/catalog_item_types_command.rb +858 -0
  235. data/lib/mtncloud/cli/commands/certificates_command.rb +575 -0
  236. data/lib/mtncloud/cli/commands/change_password_command.rb +132 -0
  237. data/lib/mtncloud/cli/commands/clients_command.rb +331 -0
  238. data/lib/mtncloud/cli/commands/cloud_datastores_command.rb +543 -0
  239. data/lib/mtncloud/cli/commands/cloud_folders_command.rb +512 -0
  240. data/lib/mtncloud/cli/commands/cloud_resource_pools_command.rb +870 -0
  241. data/lib/mtncloud/cli/commands/clouds.rb +2037 -0
  242. data/lib/mtncloud/cli/commands/clouds_types.rb +140 -0
  243. data/lib/mtncloud/cli/commands/cluster_types.rb +179 -0
  244. data/lib/mtncloud/cli/commands/clusters.rb +5855 -0
  245. data/lib/mtncloud/cli/commands/coloring_command.rb +82 -0
  246. data/lib/mtncloud/cli/commands/containers_command.rb +967 -0
  247. data/lib/mtncloud/cli/commands/credential_types_command.rb +36 -0
  248. data/lib/mtncloud/cli/commands/credentials_command.rb +124 -0
  249. data/lib/mtncloud/cli/commands/curl_command.rb +168 -0
  250. data/lib/mtncloud/cli/commands/cypher_command.rb +587 -0
  251. data/lib/mtncloud/cli/commands/dashboard_command.rb +300 -0
  252. data/lib/mtncloud/cli/commands/debug_command.rb +75 -0
  253. data/lib/mtncloud/cli/commands/deploy.rb +485 -0
  254. data/lib/mtncloud/cli/commands/deployments.rb +923 -0
  255. data/lib/mtncloud/cli/commands/deploys.rb +485 -0
  256. data/lib/mtncloud/cli/commands/distributed_workers_command.rb +282 -0
  257. data/lib/mtncloud/cli/commands/echo_command.rb +61 -0
  258. data/lib/mtncloud/cli/commands/edit_profile_command.rb +57 -0
  259. data/lib/mtncloud/cli/commands/edit_rc_command.rb +77 -0
  260. data/lib/mtncloud/cli/commands/email_templates_command.rb +373 -0
  261. data/lib/mtncloud/cli/commands/environments_command.rb +401 -0
  262. data/lib/mtncloud/cli/commands/execute_schedules_command.rb +735 -0
  263. data/lib/mtncloud/cli/commands/execution_request_command.rb +333 -0
  264. data/lib/mtncloud/cli/commands/exit_command.rb +34 -0
  265. data/lib/mtncloud/cli/commands/file_copy_request_command.rb +352 -0
  266. data/lib/mtncloud/cli/commands/forgot_password.rb +133 -0
  267. data/lib/mtncloud/cli/commands/get_prompt_command.rb +36 -0
  268. data/lib/mtncloud/cli/commands/groups.rb +818 -0
  269. data/lib/mtncloud/cli/commands/guidance_command.rb +529 -0
  270. data/lib/mtncloud/cli/commands/guidance_settings.rb +148 -0
  271. data/lib/mtncloud/cli/commands/health_command.rb +1046 -0
  272. data/lib/mtncloud/cli/commands/history_command.rb +60 -0
  273. data/lib/mtncloud/cli/commands/host_vm_groups_command.rb +423 -0
  274. data/lib/mtncloud/cli/commands/hosts.rb +2986 -0
  275. data/lib/mtncloud/cli/commands/hub.rb +215 -0
  276. data/lib/mtncloud/cli/commands/image_builder_command.rb +1197 -0
  277. data/lib/mtncloud/cli/commands/instance_types.rb +163 -0
  278. data/lib/mtncloud/cli/commands/instances.rb +5813 -0
  279. data/lib/mtncloud/cli/commands/integrations_command.rb +1412 -0
  280. data/lib/mtncloud/cli/commands/invoices_command.rb +1151 -0
  281. data/lib/mtncloud/cli/commands/jobs_command.rb +998 -0
  282. data/lib/mtncloud/cli/commands/key_pairs.rb +470 -0
  283. data/lib/mtncloud/cli/commands/library_cluster_layouts_command.rb +901 -0
  284. data/lib/mtncloud/cli/commands/library_cluster_packages_command.rb +552 -0
  285. data/lib/mtncloud/cli/commands/library_container_scripts_command.rb +446 -0
  286. data/lib/mtncloud/cli/commands/library_container_templates_command.rb +526 -0
  287. data/lib/mtncloud/cli/commands/library_container_types_command.rb +698 -0
  288. data/lib/mtncloud/cli/commands/library_forms_command.rb +625 -0
  289. data/lib/mtncloud/cli/commands/library_instance_types_command.rb +668 -0
  290. data/lib/mtncloud/cli/commands/library_layouts_command.rb +849 -0
  291. data/lib/mtncloud/cli/commands/library_operating_systems_command.rb +671 -0
  292. data/lib/mtncloud/cli/commands/library_option_lists_command.rb +503 -0
  293. data/lib/mtncloud/cli/commands/library_option_types_command.rb +329 -0
  294. data/lib/mtncloud/cli/commands/library_spec_templates_command.rb +547 -0
  295. data/lib/mtncloud/cli/commands/library_upgrades_command.rb +596 -0
  296. data/lib/mtncloud/cli/commands/license.rb +475 -0
  297. data/lib/mtncloud/cli/commands/load_balancer_monitors.rb +70 -0
  298. data/lib/mtncloud/cli/commands/load_balancer_pool_nodes.rb +87 -0
  299. data/lib/mtncloud/cli/commands/load_balancer_pools.rb +129 -0
  300. data/lib/mtncloud/cli/commands/load_balancer_profiles.rb +64 -0
  301. data/lib/mtncloud/cli/commands/load_balancer_types.rb +38 -0
  302. data/lib/mtncloud/cli/commands/load_balancer_virtual_servers.rb +147 -0
  303. data/lib/mtncloud/cli/commands/load_balancers.rb +189 -0
  304. data/lib/mtncloud/cli/commands/log_level_command.rb +54 -0
  305. data/lib/mtncloud/cli/commands/log_settings_command.rb +443 -0
  306. data/lib/mtncloud/cli/commands/login.rb +148 -0
  307. data/lib/mtncloud/cli/commands/logout.rb +63 -0
  308. data/lib/mtncloud/cli/commands/logs_command.rb +140 -0
  309. data/lib/mtncloud/cli/commands/man_command.rb +409 -0
  310. data/lib/mtncloud/cli/commands/migrations.rb +453 -0
  311. data/lib/mtncloud/cli/commands/monitoring_alerts_command.rb +791 -0
  312. data/lib/mtncloud/cli/commands/monitoring_apps_command.rb +716 -0
  313. data/lib/mtncloud/cli/commands/monitoring_checks_command.rb +766 -0
  314. data/lib/mtncloud/cli/commands/monitoring_contacts_command.rb +339 -0
  315. data/lib/mtncloud/cli/commands/monitoring_groups_command.rb +674 -0
  316. data/lib/mtncloud/cli/commands/monitoring_incidents_command.rb +835 -0
  317. data/lib/mtncloud/cli/commands/monitoring_settings.rb +212 -0
  318. data/lib/mtncloud/cli/commands/network_dhcp_relays_command.rb +375 -0
  319. data/lib/mtncloud/cli/commands/network_dhcp_servers_command.rb +366 -0
  320. data/lib/mtncloud/cli/commands/network_domains_command.rb +896 -0
  321. data/lib/mtncloud/cli/commands/network_edge_clusters_command.rb +288 -0
  322. data/lib/mtncloud/cli/commands/network_firewalls_command.rb +783 -0
  323. data/lib/mtncloud/cli/commands/network_floating_ips.rb +147 -0
  324. data/lib/mtncloud/cli/commands/network_groups_command.rb +654 -0
  325. data/lib/mtncloud/cli/commands/network_pool_server_types.rb +20 -0
  326. data/lib/mtncloud/cli/commands/network_pool_servers_command.rb +318 -0
  327. data/lib/mtncloud/cli/commands/network_pools_command.rb +903 -0
  328. data/lib/mtncloud/cli/commands/network_proxies_command.rb +578 -0
  329. data/lib/mtncloud/cli/commands/network_routers_command.rb +2628 -0
  330. data/lib/mtncloud/cli/commands/network_security_server_types.rb +20 -0
  331. data/lib/mtncloud/cli/commands/network_server_groups_command.rb +222 -0
  332. data/lib/mtncloud/cli/commands/network_server_types_command.rb +20 -0
  333. data/lib/mtncloud/cli/commands/network_servers_command.rb +398 -0
  334. data/lib/mtncloud/cli/commands/network_services_command.rb +198 -0
  335. data/lib/mtncloud/cli/commands/network_static_routes_command.rb +451 -0
  336. data/lib/mtncloud/cli/commands/network_transport_zones_command.rb +412 -0
  337. data/lib/mtncloud/cli/commands/networks_command.rb +1692 -0
  338. data/lib/mtncloud/cli/commands/open_command.rb +30 -0
  339. data/lib/mtncloud/cli/commands/options.rb +231 -0
  340. data/lib/mtncloud/cli/commands/packages_command.rb +822 -0
  341. data/lib/mtncloud/cli/commands/ping.rb +242 -0
  342. data/lib/mtncloud/cli/commands/plugins.rb +214 -0
  343. data/lib/mtncloud/cli/commands/policies_command.rb +990 -0
  344. data/lib/mtncloud/cli/commands/power_schedules_command.rb +731 -0
  345. data/lib/mtncloud/cli/commands/preseed_scripts_command.rb +378 -0
  346. data/lib/mtncloud/cli/commands/price_sets_command.rb +557 -0
  347. data/lib/mtncloud/cli/commands/prices_command.rb +692 -0
  348. data/lib/mtncloud/cli/commands/processes_command.rb +469 -0
  349. data/lib/mtncloud/cli/commands/projects_command.rb +537 -0
  350. data/lib/mtncloud/cli/commands/provisioning_licenses_command.rb +509 -0
  351. data/lib/mtncloud/cli/commands/provisioning_settings_command.rb +238 -0
  352. data/lib/mtncloud/cli/commands/recent_activity_command.rb +171 -0
  353. data/lib/mtncloud/cli/commands/remote.rb +1818 -0
  354. data/lib/mtncloud/cli/commands/reports_command.rb +724 -0
  355. data/lib/mtncloud/cli/commands/resource_pool_groups_command.rb +586 -0
  356. data/lib/mtncloud/cli/commands/rm_command.rb +14 -0
  357. data/lib/mtncloud/cli/commands/roles.rb +3120 -0
  358. data/lib/mtncloud/cli/commands/scale_thresholds.rb +103 -0
  359. data/lib/mtncloud/cli/commands/search_command.rb +184 -0
  360. data/lib/mtncloud/cli/commands/security_group_rules.rb +230 -0
  361. data/lib/mtncloud/cli/commands/security_groups.rb +1294 -0
  362. data/lib/mtncloud/cli/commands/security_package_types.rb +32 -0
  363. data/lib/mtncloud/cli/commands/security_packages.rb +84 -0
  364. data/lib/mtncloud/cli/commands/security_scans.rb +107 -0
  365. data/lib/mtncloud/cli/commands/self_service_command.rb +17 -0
  366. data/lib/mtncloud/cli/commands/server_devices_command.rb +207 -0
  367. data/lib/mtncloud/cli/commands/service_catalog_command.rb +1528 -0
  368. data/lib/mtncloud/cli/commands/service_plans_command.rb +895 -0
  369. data/lib/mtncloud/cli/commands/set_prompt_command.rb +51 -0
  370. data/lib/mtncloud/cli/commands/setup.rb +423 -0
  371. data/lib/mtncloud/cli/commands/shell.rb +717 -0
  372. data/lib/mtncloud/cli/commands/sleep_command.rb +43 -0
  373. data/lib/mtncloud/cli/commands/snapshots.rb +156 -0
  374. data/lib/mtncloud/cli/commands/source_command.rb +30 -0
  375. data/lib/mtncloud/cli/commands/ssl_verification_command.rb +47 -0
  376. data/lib/mtncloud/cli/commands/storage_datastores.rb +457 -0
  377. data/lib/mtncloud/cli/commands/storage_providers_command.rb +1401 -0
  378. data/lib/mtncloud/cli/commands/storage_server_types.rb +45 -0
  379. data/lib/mtncloud/cli/commands/storage_servers.rb +116 -0
  380. data/lib/mtncloud/cli/commands/storage_volume_types.rb +45 -0
  381. data/lib/mtncloud/cli/commands/storage_volumes.rb +236 -0
  382. data/lib/mtncloud/cli/commands/subnets_command.rb +819 -0
  383. data/lib/mtncloud/cli/commands/support_bundles_command.rb +606 -0
  384. data/lib/mtncloud/cli/commands/systems.rb +1203 -0
  385. data/lib/mtncloud/cli/commands/tasks.rb +1391 -0
  386. data/lib/mtncloud/cli/commands/tee_command.rb +14 -0
  387. data/lib/mtncloud/cli/commands/tenants_command.rb +315 -0
  388. data/lib/mtncloud/cli/commands/tokens_command.rb +391 -0
  389. data/lib/mtncloud/cli/commands/update_command.rb +46 -0
  390. data/lib/mtncloud/cli/commands/usage_command.rb +203 -0
  391. data/lib/mtncloud/cli/commands/user_groups_command.rb +512 -0
  392. data/lib/mtncloud/cli/commands/user_settings_command.rb +849 -0
  393. data/lib/mtncloud/cli/commands/user_sources_command.rb +1065 -0
  394. data/lib/mtncloud/cli/commands/users.rb +897 -0
  395. data/lib/mtncloud/cli/commands/vdi_allocations_command.rb +159 -0
  396. data/lib/mtncloud/cli/commands/vdi_apps_command.rb +317 -0
  397. data/lib/mtncloud/cli/commands/vdi_command.rb +359 -0
  398. data/lib/mtncloud/cli/commands/vdi_gateways_command.rb +290 -0
  399. data/lib/mtncloud/cli/commands/vdi_pools_command.rb +574 -0
  400. data/lib/mtncloud/cli/commands/version_command.rb +38 -0
  401. data/lib/mtncloud/cli/commands/view.rb +107 -0
  402. data/lib/mtncloud/cli/commands/virtual_images.rb +1362 -0
  403. data/lib/mtncloud/cli/commands/virtual_switches_command.rb +336 -0
  404. data/lib/mtncloud/cli/commands/whitelabel_settings_command.rb +653 -0
  405. data/lib/mtncloud/cli/commands/whoami.rb +323 -0
  406. data/lib/mtncloud/cli/commands/wiki_command.rb +448 -0
  407. data/lib/mtncloud/cli/commands/workflows.rb +1022 -0
  408. data/lib/mtncloud/cli/credentials.rb +482 -0
  409. data/lib/mtncloud/cli/dot_file.rb +180 -0
  410. data/lib/mtncloud/cli/error_handler.rb +307 -0
  411. data/lib/mtncloud/cli/errors.rb +53 -0
  412. data/lib/mtncloud/cli/expression_parser.rb +153 -0
  413. data/lib/mtncloud/cli/mixins/accounts_helper.rb +484 -0
  414. data/lib/mtncloud/cli/mixins/affinity_helper.rb +118 -0
  415. data/lib/mtncloud/cli/mixins/backups_helper.rb +200 -0
  416. data/lib/mtncloud/cli/mixins/deployments_helper.rb +163 -0
  417. data/lib/mtncloud/cli/mixins/execution_request_helper.rb +50 -0
  418. data/lib/mtncloud/cli/mixins/infrastructure_helper.rb +966 -0
  419. data/lib/mtncloud/cli/mixins/jobs_helper.rb +202 -0
  420. data/lib/mtncloud/cli/mixins/library_helper.rb +539 -0
  421. data/lib/mtncloud/cli/mixins/load_balancers_helper.rb +176 -0
  422. data/lib/mtncloud/cli/mixins/logs_helper.rb +131 -0
  423. data/lib/mtncloud/cli/mixins/monitoring_helper.rb +876 -0
  424. data/lib/mtncloud/cli/mixins/networks_helper.rb +62 -0
  425. data/lib/mtncloud/cli/mixins/operations_helper.rb +41 -0
  426. data/lib/mtncloud/cli/mixins/option_source_helper.rb +254 -0
  427. data/lib/mtncloud/cli/mixins/print_helper.rb +1631 -0
  428. data/lib/mtncloud/cli/mixins/processes_helper.rb +286 -0
  429. data/lib/mtncloud/cli/mixins/prompt_helper.rb +136 -0
  430. data/lib/mtncloud/cli/mixins/provisioning_helper.rb +2963 -0
  431. data/lib/mtncloud/cli/mixins/remote_helper.rb +139 -0
  432. data/lib/mtncloud/cli/mixins/rest_command.rb +994 -0
  433. data/lib/mtncloud/cli/mixins/secondary_rest_command.rb +700 -0
  434. data/lib/mtncloud/cli/mixins/storage_servers_helper.rb +93 -0
  435. data/lib/mtncloud/cli/mixins/storage_volumes_helper.rb +76 -0
  436. data/lib/mtncloud/cli/mixins/vdi_helper.rb +246 -0
  437. data/lib/mtncloud/cli/mixins/whoami_helper.rb +73 -0
  438. data/lib/mtncloud/cli/option_parser.rb +127 -0
  439. data/lib/mtncloud/cli/option_types.rb +1622 -0
  440. data/lib/mtncloud/cli/version.rb +6 -0
  441. data/lib/mtncloud/cli.rb +105 -0
  442. data/lib/mtncloud/currency.rb +184 -0
  443. data/lib/mtncloud/ext/hash.rb +109 -0
  444. data/lib/mtncloud/ext/nil_class.rb +5 -0
  445. data/lib/mtncloud/ext/rest_client.rb +37 -0
  446. data/lib/mtncloud/ext/string.rb +66 -0
  447. data/lib/mtncloud/formatters.rb +511 -0
  448. data/lib/mtncloud/logging.rb +185 -0
  449. data/lib/mtncloud/morpkg.rb +119 -0
  450. data/lib/mtncloud/rest_client.rb +104 -0
  451. data/lib/mtncloud/routes.rb +304 -0
  452. data/lib/mtncloud/terminal.rb +528 -0
  453. data/lib/mtncloud/util.rb +29 -0
  454. data/lib/mtncloud.rb +5 -0
  455. data/mtncloud-cli.gemspec +38 -0
  456. data/test/api/affinity_groups_interface_test.rb +43 -0
  457. data/test/api/clients_interface_test.rb +23 -0
  458. data/test/api/clouds_interface_test.rb +67 -0
  459. data/test/api/containers_interface_test.rb +68 -0
  460. data/test/api/host_vm_groups_interface_test.rb +35 -0
  461. data/test/api/instances_interface_test.rb +22 -0
  462. data/test/api/network_routers_interface_test.rb +22 -0
  463. data/test/api/systems_interface_test.rb +26 -0
  464. data/test/api/whoami_interface_test.rb +14 -0
  465. data/test/branding_test.rb +160 -0
  466. data/test/cli/access_token_test.rb +36 -0
  467. data/test/cli/affinity_groups_cloud_cluster_scope_test.rb +270 -0
  468. data/test/cli/affinity_groups_cluster_scope_test.rb +171 -0
  469. data/test/cli/affinity_groups_test.rb +57 -0
  470. data/test/cli/affinity_groups_violations_scope_test.rb +141 -0
  471. data/test/cli/affinity_helper_test.rb +367 -0
  472. data/test/cli/auth_test.rb +88 -0
  473. data/test/cli/backups_test.rb +212 -0
  474. data/test/cli/cli_test.rb +48 -0
  475. data/test/cli/clients_test.rb +45 -0
  476. data/test/cli/clusters_uuid_test.rb +146 -0
  477. data/test/cli/containers_test.rb +92 -0
  478. data/test/cli/distributed_workers_test.rb +68 -0
  479. data/test/cli/help_test.rb +25 -0
  480. data/test/cli/host_vm_groups_cluster_scope_test.rb +187 -0
  481. data/test/cli/host_vm_groups_test.rb +63 -0
  482. data/test/cli/infrastructure_helper_cluster_resolver_test.rb +250 -0
  483. data/test/cli/instances_test.rb +37 -0
  484. data/test/cli/man_test.rb +14 -0
  485. data/test/cli/migrations_test.rb +39 -0
  486. data/test/cli/network_routers_test.rb +65 -0
  487. data/test/cli/remote_test.rb +89 -0
  488. data/test/cli/roles_test.rb +47 -0
  489. data/test/cli/shell_test.rb +81 -0
  490. data/test/cli/storage_volumes_test.rb +79 -0
  491. data/test/cli/systems_test.rb +206 -0
  492. data/test/cli/version_test.rb +23 -0
  493. data/test/cli/view_test.rb +55 -0
  494. data/test/cli/whoami_test.rb +17 -0
  495. data/test/mtncloud_test.rb +16 -0
  496. data/test/test_case.rb +342 -0
  497. data/test/test_config.rb +137 -0
  498. data/test/test_data_helper.rb +97 -0
  499. metadata +807 -0
@@ -0,0 +1,2963 @@
1
+ require 'mtncloud/cli/mixins/print_helper'
2
+ require 'mtncloud/cli/option_types'
3
+ # Mixin for MtnCloud::Cli command classes
4
+ # Provides common methods for provisioning instances
5
+ module MtnCloud::Cli::ProvisioningHelper
6
+
7
+ def self.included(klass)
8
+ klass.send :include, MtnCloud::Cli::PrintHelper
9
+ end
10
+
11
+ def api_client
12
+ raise "#{self.class} has not defined @api_client" if @api_client.nil?
13
+ @api_client
14
+ end
15
+
16
+ def instances_interface
17
+ @api_client.instances
18
+ end
19
+
20
+ def apps_interface
21
+ @api_client.apps
22
+ end
23
+
24
+ def servers_interface
25
+ @api_client.servers
26
+ end
27
+
28
+ def options_interface
29
+ @api_client.options
30
+ end
31
+
32
+ def instance_types_interface
33
+ @api_client.instance_types
34
+ end
35
+
36
+ def instance_type_layouts_interface
37
+ @api_client.library_layouts
38
+ end
39
+
40
+ def provision_types_interface
41
+ api_client.provision_types
42
+ end
43
+
44
+ def clouds_interface
45
+ @api_client.clouds
46
+ end
47
+
48
+ def cloud_datastores_interface
49
+ @api_client.cloud_datastores
50
+ end
51
+
52
+ def datastores_interface
53
+ @api_client.datastores
54
+ end
55
+
56
+ def accounts_interface
57
+ @api_client.accounts
58
+ end
59
+
60
+ def datastores_interface
61
+ @api_client.datastores
62
+ end
63
+
64
+ def get_available_groups(params = {}, refresh=false)
65
+ if !@available_groups || refresh
66
+ option_results = options_interface.options_for_source('groups', params)
67
+ @available_groups = option_results['data'].collect {|it|
68
+ {"id" => it["value"], "name" => it["name"], "value" => it["value"]}
69
+ }
70
+ end
71
+ #puts "get_available_groups() rtn: #{@available_groups.inspect}"
72
+ return @available_groups
73
+ end
74
+
75
+ def get_available_clouds(group_id, params = {}, refresh=false)
76
+ if !group_id
77
+ option_results = options_interface.options_for_source('clouds', params.merge({'default' => 'false'}))
78
+ return option_results['data'].collect {|it|
79
+ {"id" => it["value"], "name" => it["name"], "value" => it["value"], "zoneTypeId" => it["zoneTypeId"]}
80
+ }
81
+ end
82
+ group = find_group_by_id_for_provisioning(group_id)
83
+ if !group
84
+ return []
85
+ end
86
+ if !group["clouds"] || refresh
87
+ option_results = options_interface.options_for_source('clouds', params.merge({groupId: group_id}))
88
+ group["clouds"] = option_results['data'].collect {|it|
89
+ {"id" => it["value"], "name" => it["name"], "value" => it["value"], "zoneTypeId" => it["zoneTypeId"]}
90
+ }
91
+ end
92
+ return group["clouds"]
93
+ end
94
+
95
+ def get_available_accounts(refresh=false)
96
+ if !@available_accounts || refresh
97
+ # @available_accounts = accounts_interface.list()['accounts']
98
+ @available_accounts = options_interface.options_for_source("allTenants", {})['data'].collect {|it|
99
+ {"name" => it["name"], "value" => it["value"], "id" => it["value"]}
100
+ }
101
+ end
102
+ @available_accounts
103
+ end
104
+
105
+ def get_available_plans(refresh=false)
106
+ if !@available_plans || refresh
107
+ @available_plans = instances_interface.search_plans['searchPlans'].collect {|it|
108
+ {"name" => it["name"], "value" => it["id"]}
109
+ }
110
+ end
111
+ @available_plans
112
+ end
113
+
114
+ def find_group_by_id_for_provisioning(val)
115
+ groups = get_available_groups()
116
+ group = groups.find {|it| it["id"].to_s == val.to_s }
117
+ if !group
118
+ print_red_alert "Group not found by id #{val}"
119
+ exit 1
120
+ end
121
+ return group
122
+ end
123
+
124
+ def find_group_by_name_for_provisioning(val)
125
+ groups = get_available_groups()
126
+ group = groups.find {|it| it["name"].to_s.downcase == val.to_s.downcase }
127
+ if !group
128
+ print_red_alert "Group not found by name #{val}"
129
+ exit 1
130
+ end
131
+ return group
132
+ end
133
+
134
+ def find_group_by_name_or_id_for_provisioning(val)
135
+ if val.to_s =~ /\A\d{1,}\Z/
136
+ return find_group_by_id_for_provisioning(val)
137
+ else
138
+ return find_group_by_name_for_provisioning(val)
139
+ end
140
+ end
141
+
142
+ def find_cloud_by_id_for_provisioning(group_id, val)
143
+ clouds = get_available_clouds(group_id)
144
+ cloud = clouds.find {|it| it["id"].to_s == val.to_s }
145
+ if !cloud
146
+ print_red_alert "Cloud not found by id #{val}"
147
+ exit 1
148
+ end
149
+ return cloud
150
+ end
151
+
152
+ def find_cloud_by_name_for_provisioning(group_id, val)
153
+ clouds = get_available_clouds(group_id)
154
+ cloud = clouds.find {|it| it["name"].to_s.downcase == val.to_s.downcase }
155
+ if !cloud
156
+ print_red_alert "Cloud not found by name #{val}"
157
+ exit 1
158
+ end
159
+ return cloud
160
+ end
161
+
162
+ def find_cloud_by_name_or_id_for_provisioning(group_id, val)
163
+ if val.to_s =~ /\A\d{1,}\Z/
164
+ return find_cloud_by_id_for_provisioning(group_id, val)
165
+ else
166
+ return find_cloud_by_name_for_provisioning(group_id, val)
167
+ end
168
+ end
169
+
170
+ def find_instance_type_by_id(id)
171
+ begin
172
+ json_response = instance_types_interface.get(id.to_i)
173
+ return json_response['instanceType']
174
+ rescue RestClient::Exception => e
175
+ if e.response && e.response.code == 404
176
+ print_red_alert "Instance Type not found by id #{id}"
177
+ return nil
178
+ else
179
+ raise e
180
+ end
181
+ end
182
+ end
183
+
184
+ def find_instance_type_by_code(code)
185
+ results = instance_types_interface.list({code: code})
186
+ instance_types = results['instanceTypes']
187
+ if instance_types.empty?
188
+ print_red_alert "Instance Type not found by code #{code}"
189
+ return nil
190
+ end
191
+ if instance_types.size() > 1
192
+ print as_pretty_table(instance_types, [:id,:name,:code], {color:red})
193
+ print_red_alert "Try using ID instead"
194
+ return nil
195
+ end
196
+ # return instance_types[0]
197
+ # fetch by ID to get full details
198
+ # could also use ?details-true with search
199
+ return find_instance_type_by_id(instance_types[0]['id'])
200
+ end
201
+
202
+ def find_instance_type_by_name(name)
203
+ results = instance_types_interface.list({name: name})
204
+ instance_types = results['instanceTypes']
205
+ if instance_types.empty?
206
+ print_red_alert "Instance Type not found by name #{name}"
207
+ return nil
208
+ end
209
+ if instance_types.size() > 1
210
+ print as_pretty_table(instance_types, [:id,:name,:code], {color:red})
211
+ print_red_alert "Try using ID instead"
212
+ return nil
213
+ end
214
+ # return instance_types[0]
215
+ # fetch by ID to get full details
216
+ # could also use ?details-true with search
217
+ return find_instance_type_by_id(instance_types[0]['id'])
218
+ end
219
+
220
+ def find_instance_type_by_name_or_id(val)
221
+ if val.to_s =~ /\A\d{1,}\Z/
222
+ return find_instance_type_by_id(val)
223
+ else
224
+ return find_instance_type_by_name(val)
225
+ end
226
+ end
227
+
228
+ def find_instance_by_name_or_id(val, include_tenants=false)
229
+ if val.to_s =~ /\A\d{1,}\Z/
230
+ return find_instance_by_id(val)
231
+ else
232
+ return find_instance_by_name(val, include_tenants)
233
+ end
234
+ end
235
+
236
+ def find_instance_by_id(id)
237
+ begin
238
+ json_response = instances_interface.get(id.to_i)
239
+ return json_response['instance']
240
+ rescue RestClient::Exception => e
241
+ if e.response && e.response.code == 404
242
+ print_red_alert "Instance not found by id #{id}"
243
+ exit 1
244
+ else
245
+ raise e
246
+ end
247
+ end
248
+ end
249
+
250
+ def find_instance_by_name(name, include_tenants=false)
251
+ params = {name: name.to_s}
252
+ params['includeTenants'] = true if include_tenants
253
+ instances = instances_interface.list(params)['instances']
254
+ if instances.empty?
255
+ print_red_alert "Instance not found by name #{name}"
256
+ exit 1
257
+ elsif instances.size > 1
258
+ print_red_alert "Multiple Instances exist with the name '#{name}'"
259
+ puts_error as_pretty_table(instances, [:id, :name], {color:red})
260
+ print_red_alert "Try using ID instead"
261
+ print_error reset,"\n"
262
+ exit 1
263
+ end
264
+ return instances[0]
265
+ end
266
+
267
+ def parse_instance_id_list(id_list)
268
+ parse_id_list(id_list).collect do |instance_id|
269
+ find_instance_by_name_or_id(instance_id)['id']
270
+ end
271
+ end
272
+
273
+ ## resources
274
+
275
+ # todo: crud and /api/options for resources
276
+
277
+ def parse_resource_id_list(id_list)
278
+ parse_id_list(id_list).collect do |resource_id|
279
+ #find_resource_by_name_or_id(resource_id)['id']
280
+ resource_id
281
+ end
282
+ end
283
+
284
+ ## apps
285
+
286
+ def find_app_by_id(id)
287
+ app_results = apps_interface.get(id.to_i)
288
+ if app_results['app'].empty?
289
+ print_red_alert "App not found by id #{id}"
290
+ exit 1
291
+ end
292
+ return app_results['app']
293
+ end
294
+
295
+ def find_app_by_name(name)
296
+ app_results = apps_interface.list({name: name})
297
+ apps = app_results['apps']
298
+ if apps.empty?
299
+ print_red_alert "App not found by name #{name}"
300
+ exit 1
301
+ elsif apps.size > 1
302
+ print_red_alert "#{apps.size} apps exist with the name #{name}. Try using id instead"
303
+ exit 1
304
+ end
305
+
306
+ return app_results['apps'][0]
307
+ end
308
+
309
+ def find_app_by_name_or_id(val)
310
+ if val.to_s =~ /\A\d{1,}\Z/
311
+ return find_app_by_id(val)
312
+ else
313
+ return find_app_by_name(val)
314
+ end
315
+ end
316
+
317
+ ## servers
318
+
319
+ def find_server_by_id(id)
320
+ begin
321
+ json_response = servers_interface.get(id.to_i)
322
+ return json_response['server']
323
+ rescue RestClient::Exception => e
324
+ if e.response && e.response.code == 404
325
+ print_red_alert "Server not found by id #{id}"
326
+ exit 1
327
+ else
328
+ raise e
329
+ end
330
+ end
331
+ end
332
+
333
+ def find_server_by_name(name)
334
+ results = servers_interface.list({name: name})
335
+ if results['servers'].empty?
336
+ print_red_alert "Server not found by name #{name}"
337
+ exit 1
338
+ elsif results['servers'].size > 1
339
+ print_red_alert "Multiple Servers exist with the name '#{name}'"
340
+ puts_error as_pretty_table(results['servers'], [:id, :name], {color:red})
341
+ print_red_alert "Try using ID instead"
342
+ exit 1
343
+ end
344
+ return results['servers'][0]
345
+ end
346
+
347
+ def find_server_by_name_or_id(val)
348
+ if val.to_s =~ /\A\d{1,}\Z/
349
+ return find_server_by_id(val)
350
+ else
351
+ return find_server_by_name(val)
352
+ end
353
+ end
354
+
355
+ def parse_server_id_list(id_list)
356
+ parse_id_list(id_list).collect do |server_id|
357
+ find_server_by_name_or_id(server_id)['id']
358
+ end
359
+ end
360
+
361
+ ## hosts is the same as servers, just says 'Host' instead of 'Server'
362
+
363
+ def find_host_by_id(id)
364
+ begin
365
+ json_response = servers_interface.get(id.to_i)
366
+ return json_response['server']
367
+ rescue RestClient::Exception => e
368
+ if e.response && e.response.code == 404
369
+ print_red_alert "Host not found by id #{id}"
370
+ exit 1
371
+ else
372
+ raise e
373
+ end
374
+ end
375
+ end
376
+
377
+ def find_host_by_name(name)
378
+ results = servers_interface.list({name: name})
379
+ if results['servers'].empty?
380
+ print_red_alert "Host not found by name #{name}"
381
+ exit 1
382
+ elsif results['servers'].size > 1
383
+ print_red_alert "#{results['servers'].size} hosts exist with the name #{name}. Try using id instead"
384
+ exit 1
385
+ end
386
+ return results['servers'][0]
387
+ end
388
+
389
+ def find_host_by_name_or_id(val)
390
+ if val.to_s =~ /\A\d{1,}\Z/
391
+ return find_host_by_id(val)
392
+ else
393
+ return find_host_by_name(val)
394
+ end
395
+ end
396
+
397
+ def parse_host_id_list(id_list)
398
+ parse_id_list(id_list).collect do |host_id|
399
+ find_host_by_name_or_id(host_id)['id']
400
+ end
401
+ end
402
+
403
+ def find_instance_type_layout_by_id(layout_id, id)
404
+ json_results = instance_type_layouts_interface.get(layout_id, id)
405
+ if json_results['instanceTypeLayout'].empty?
406
+ print_red_alert "Instance type layout not found by id #{id}"
407
+ exit 1
408
+ end
409
+ json_results['instanceTypeLayout']
410
+ end
411
+
412
+ def find_workflow_by_name_or_id(val)
413
+ if val.to_s =~ /\A\d{1,}\Z/
414
+ return find_workflow_by_id(val)
415
+ else
416
+ return find_workflow_by_name(val)
417
+ end
418
+ end
419
+
420
+ def find_workflow_by_id(id)
421
+ begin
422
+ json_response = @task_sets_interface.get(id.to_i)
423
+ return json_response['taskSet']
424
+ rescue RestClient::Exception => e
425
+ if e.response && e.response.code == 404
426
+ print_red_alert "Workflow not found by id #{id}"
427
+ else
428
+ raise e
429
+ end
430
+ end
431
+ end
432
+
433
+ def find_workflow_by_name(name)
434
+ workflows = @task_sets_interface.get({name: name.to_s})['taskSets']
435
+ if workflows.empty?
436
+ print_red_alert "Workflow not found by name #{name}"
437
+ return nil
438
+ elsif workflows.size > 1
439
+ print_red_alert "#{workflows.size} workflows by name #{name}"
440
+ print_workflows_table(workflows, {color: red})
441
+ print reset,"\n\n"
442
+ return nil
443
+ else
444
+ return workflows[0]
445
+ end
446
+ end
447
+
448
+ def find_cloud_resource_pool_by_name_or_id(cloud_id, val)
449
+ (val.to_s =~ /\A\d{1,}\Z/) ? find_cloud_resource_pool_by_id(cloud_id, val) : find_cloud_resource_pool_by_name(cloud_id, val)
450
+ end
451
+
452
+ def get_provision_type_for_zone_type(zone_type_id)
453
+ clouds_interface.cloud_type(zone_type_id)['zoneType']['provisionTypes'].first rescue nil
454
+ end
455
+
456
+ # prompts user for all the configuartion options for a particular instance
457
+ # returns payload of data for a new instance
458
+ def prompt_new_instance(options={})
459
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
460
+ #puts "prompt_new_instance() #{options}"
461
+ print reset # clear colors
462
+ options[:options] ||= {}
463
+ # provisioning with blueprint can lock fields
464
+ locked_fields = options[:locked_fields] || []
465
+ # Group
466
+ default_group = find_group_by_name_or_id_for_provisioning(options[:default_group] || @active_group_id) if options[:default_group] || @active_group_id
467
+
468
+ group_id = nil
469
+ group = options[:group] ? find_group_by_name_or_id_for_provisioning(options[:group]) : nil
470
+ if group
471
+ group_id = group["id"]
472
+ else
473
+ # print_red_alert "Group not found or specified!"
474
+ # exit 1
475
+ group_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'group', 'type' => 'select', 'fieldLabel' => 'Group', 'selectOptions' => get_available_groups(), 'required' => true, 'description' => 'Select Group.', 'defaultValue' => (default_group ? default_group['name'] : nil)}],options[:options],api_client,{})
476
+ group_id = group_prompt['group']
477
+ end
478
+
479
+ # Cloud
480
+ cloud_id = nil
481
+ cloud = options[:cloud] ? find_cloud_by_name_or_id_for_provisioning(group_id, options[:cloud]) : nil
482
+ if cloud
483
+ cloud_id = cloud["id"]
484
+ else
485
+ # print_red_alert "Cloud not specified!"
486
+ # exit 1
487
+ available_clouds = get_available_clouds(group_id)
488
+ cloud_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'cloud', 'type' => 'select', 'fieldLabel' => 'Cloud', 'selectOptions' => get_available_clouds(group_id), 'required' => true, 'description' => 'Select Cloud.', 'defaultValue' => options[:default_cloud] ? options[:default_cloud] : nil}],options[:options],api_client,{groupId: group_id})
489
+ cloud_id = cloud_prompt['cloud']
490
+ cloud = available_clouds.find {|it| it['value'] == cloud_id}
491
+ end
492
+
493
+ cloud_type = clouds_interface.cloud_type(cloud['zoneTypeId'])
494
+
495
+ # Instance Type
496
+ instance_type_code = nil
497
+ if options[:instance_type_code]
498
+ instance_type_code = options[:instance_type_code]
499
+ else
500
+ instance_type_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'type' => 'select', 'fieldLabel' => 'Type', 'optionSource' => 'instanceTypes', 'required' => true, 'description' => 'Select Instance Type.'}],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, restrictProvisionType:true}, no_prompt, true)
501
+ instance_type_code = instance_type_prompt['type']
502
+ end
503
+ if instance_type_code.to_s =~ /\A\d{1,}\Z/
504
+ instance_type = find_instance_type_by_id(instance_type_code)
505
+ else
506
+ instance_type = find_instance_type_by_code(instance_type_code)
507
+ end
508
+ exit 1 if !instance_type
509
+
510
+ # Instance Name
511
+ instance_name = nil
512
+ if options[:instance_name]
513
+ options[:options]['name'] = options[:instance_name]
514
+ elsif options[:options]['instance'] && options[:options]['instance']['name']
515
+ options[:options]['name'] = options[:options]['instance']['name']
516
+ end
517
+
518
+ while instance_name.nil? do
519
+ name_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Instance Name', 'type' => 'text', 'required' => options[:name_required], 'defaultValue' => options[:default_name]}], options[:options])
520
+ name = name_prompt['name']
521
+ if name.nil? && !options[:name_required]
522
+ break
523
+ else
524
+ if instances_interface.list({name: name})['instances'].empty?
525
+ instance_name = name
526
+ else
527
+ print_red_alert "Name must be unique, #{name} already exists"
528
+ options[:options].delete('name')
529
+ exit 1 if no_prompt
530
+ if options[:default_name] == name
531
+ options[:default_name] += '-2'
532
+ end
533
+ end
534
+ end
535
+ end
536
+
537
+ # config
538
+ config = {}
539
+ =begin
540
+ if cloud_type['code'] == 'amazon' && (cloud['config'] || {})['isVpc'] == 'false' && (cloud['config'] || {})['vpc'] == ''
541
+ config['isEC2'] = true
542
+ else
543
+ config['isEC2'] = false
544
+ if cloud_type['code'] == 'amazon' && (cloud['config'] || {})['isVpc'] == 'true' && (cloud['config'] || {})['vpc'] != ''
545
+ config['isVpcSelectable'] = false
546
+ config['resourcePoolId'] = cloud['config']['vpc']
547
+ else
548
+ config['isVpcSelectable'] = true
549
+ end
550
+ end
551
+ =end
552
+
553
+ payload = {
554
+ 'zoneId' => cloud_id,
555
+ # 'siteId' => siteId,
556
+ 'instance' => {
557
+ 'name' => instance_name,
558
+ 'cloud' => cloud['name'],
559
+ 'site' => {
560
+ 'id' => group_id
561
+ },
562
+ 'type' => instance_type_code,
563
+ 'instanceType' => {
564
+ 'code' => instance_type_code
565
+ }
566
+ },
567
+ 'config' => config
568
+ }
569
+
570
+ # allow arbitrary -O values passed by the user
571
+ if options[:options]
572
+ arbitrary_options = (options[:options] || {}).reject {|k,v| k.is_a?(Symbol) }
573
+ # remove some things that are being prompted for
574
+ arbitrary_options.delete('group')
575
+ arbitrary_options.delete('cloud')
576
+ arbitrary_options.delete('type')
577
+ arbitrary_options.delete('name')
578
+ #arbitrary_options.delete('version')
579
+ arbitrary_options.delete('layout')
580
+ arbitrary_options.delete('servicePlan')
581
+ arbitrary_options.delete('description')
582
+ arbitrary_options.delete('environment')
583
+ arbitrary_options.delete('instanceContext')
584
+ arbitrary_options.delete('tags')
585
+ # these are used by prompt_network_interfaces
586
+ arbitrary_options.delete('networkInterface')
587
+ (2..10).each {|i| arbitrary_options.delete('networkInterface' + i.to_s) }
588
+ # these are used by prompt_volumes
589
+ arbitrary_options.delete('rootVolume')
590
+ arbitrary_options.delete('dataVolume')
591
+ (2..10).each {|i| arbitrary_options.delete('dataVolume' + i.to_s) }
592
+ arbitrary_options.delete('lockedFields')
593
+ # arbitrary_options.delete('ports')
594
+ arbitrary_options.delete('marketplacePublisher')
595
+ arbitrary_options.delete('marketplaceOffer')
596
+ arbitrary_options.delete('marketplaceSku')
597
+ arbitrary_options.delete('marketplaceVersion')
598
+ payload.deep_merge!(arbitrary_options)
599
+ end
600
+
601
+ # Description
602
+ if options[:description]
603
+ options[:options]['description'] = options[:description]
604
+ end
605
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false, 'defaultValue' => options[:default_description]}], options[:options])
606
+ payload['instance']['description'] = v_prompt['description'] if !v_prompt['description'].empty?
607
+
608
+ # Environment
609
+ if options[:environment]
610
+ options[:options]['environment'] = options[:environment]
611
+ elsif options[:options]['instanceContext'] && !options[:options]['environment']
612
+ options[:options]['environment'] = options[:options]['instanceContext']
613
+ end
614
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'environment', 'fieldLabel' => 'Environment', 'type' => 'select', 'required' => false, 'selectOptions' => get_available_environments(), 'defaultValue' => options[:default_environment]}], options[:options])
615
+ payload['instance']['instanceContext'] = v_prompt['environment'] if !v_prompt['environment'].empty?
616
+
617
+ # Labels (used to be called tags)
618
+ unless options[:skip_labels_prompt]
619
+ if options[:labels]
620
+ payload['instance']['labels'] = options[:labels].is_a?(Array) ? options[:labels] : options[:labels].to_s.split(',').collect {|it| it.to_s.strip }.compact.uniq
621
+ else
622
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'labels', 'fieldLabel' => 'Labels', 'type' => 'text', 'required' => false, 'defaultValue' => options[:default_labels]}], options[:options])
623
+ payload['instance']['labels'] = v_prompt['labels'].split(',').collect {|it| it.to_s.strip }.compact.uniq if !v_prompt['labels'].empty?
624
+ end
625
+ end
626
+
627
+ # Version and Layout
628
+ layout_id = nil
629
+ if locked_fields.include?('instance.layout.id')
630
+ layout_id = options[:options]['instance']['layout'] rescue options[:options]['layout']
631
+ if layout_id.is_a?(Hash)
632
+ layout_id = layout_id['id'] || layout_id['code'] || layout_id['name']
633
+ end
634
+ else
635
+ layout_id = nil
636
+ if options[:layout]
637
+ layout_id = options[:layout]
638
+ # elsif options[:options]['layout']
639
+ # layout_id = options[:options]['layout']
640
+ end
641
+ if layout_id.is_a?(Hash)
642
+ layout_id = layout_id['id'] || layout_id['code'] || layout_id['name']
643
+ end
644
+ if layout_id.nil?
645
+ version_value = nil
646
+ default_layout_value = nil
647
+ if options[:version]
648
+ version_value = options[:version]
649
+ elsif options[:options]['version']
650
+ version_value = options[:options]['version']
651
+ else
652
+ available_versions = options_interface.options_for_source('instanceVersions',{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id']})['data']
653
+ # filter versions with no layouts.. api should probably do that too eh?
654
+ available_versions.reject! { |available_version| available_version["layouts"].nil? || available_version["layouts"].empty? }
655
+ default_version_value = payload['instance']['version'] ? payload['instance']['version'] : payload['version']
656
+ #default_layout_value = options[:layout]
657
+ if default_layout_value.nil?
658
+ default_layout_value = payload['instance']['layout'] ? payload['instance']['layout'] : payload['layout']
659
+ end
660
+ if default_layout_value && default_layout_value.is_a?(Hash)
661
+ default_layout_value = default_layout_value['id']
662
+ end
663
+ # JD: version is always nil because it is not stored in the blueprint or config !!
664
+ # so for now, infer the version from the layout
665
+ # requires api 3.6.2 to get "layouts" from /options/versions
666
+ if default_layout_value && default_version_value.to_s.empty?
667
+ available_versions.each do |available_version|
668
+ if available_version["layouts"]
669
+ selected_layout = available_version["layouts"].find {|it| it["value"].to_s == default_layout_value.to_s || it["id"].to_s == default_layout_value.to_s || it["code"].to_s == default_layout_value.to_s }
670
+ if selected_layout
671
+ default_version_value = available_version["value"]
672
+ break
673
+ end
674
+ end
675
+ end
676
+ end
677
+
678
+ # do not require version if a layout is passed
679
+ version_value = default_version_value
680
+ version_is_required = default_layout_value.nil?
681
+ if default_layout_value.nil? && options[:options]["layout"].nil? && options[:always_prompt] != true
682
+ version_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'version', 'type' => 'select', 'fieldLabel' => 'Version', 'selectOptions' => available_versions, 'required' => version_is_required, 'skipSingleOption' => true, 'autoPickOption' => true, 'description' => 'Select which version of the instance type to be provisioned.', 'defaultValue' => default_version_value}],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id']})
683
+ version_value = version_prompt['version']
684
+ end
685
+ end
686
+ layout_id = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'layout', 'type' => 'select', 'fieldLabel' => 'Layout', 'optionSource' => 'layoutsForCloud', 'required' => true, 'description' => 'Select which configuration of the instance type to be provisioned.', 'defaultValue' => default_layout_value}],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], version: version_value, creatable: true}, no_prompt, true)['layout']
687
+ end
688
+ end
689
+
690
+ # determine layout and provision_type
691
+
692
+ # layout = find_instance_type_layout_by_id(instance_type['id'], layout_id.to_i)
693
+ layout = (instance_type['instanceTypeLayouts'] || []).find {|it|
694
+ it['id'].to_s == layout_id.to_s || it['code'].to_s == layout_id.to_s || it['name'].to_s == layout_id.to_s
695
+ }
696
+ if !layout
697
+ print_red_alert "Layout not found by id #{layout_id}"
698
+ exit 1
699
+ end
700
+ layout_id = layout['id']
701
+ payload['instance']['layout'] = {'id' => layout['id'], 'code' => layout['code']}
702
+
703
+ # need to GET provision type for optionTypes, and other settings...
704
+ provision_type_code = layout['provisionTypeCode'] || layout['provisionType']['code']
705
+ provision_type = nil
706
+ if provision_type_code
707
+ provision_type = provision_types_interface.list({code:provision_type_code})['provisionTypes'][0]
708
+ if provision_type.nil?
709
+ print_red_alert "Provision Type not found by code #{provision_type_code}"
710
+ exit 1
711
+ end
712
+ else
713
+ provision_type = get_provision_type_for_zone_type(cloud['zoneType']['id'])
714
+ end
715
+
716
+ # build config option types
717
+ option_type_list = []
718
+ if !layout['optionTypes'].nil? && !layout['optionTypes'].empty?
719
+ option_type_list += layout['optionTypes']
720
+ end
721
+ if !instance_type['optionTypes'].nil? && !instance_type['optionTypes'].empty?
722
+ option_type_list += instance_type['optionTypes']
723
+ end
724
+
725
+ api_params = {groupId: group_id, cloudId: cloud_id, zoneId: cloud_id, instanceTypeId: instance_type['id'], version: version_value}
726
+
727
+ pool_id = nil
728
+ resource_pool = nil
729
+ service_plan = nil
730
+
731
+ prompt_service_plan = -> {
732
+ service_plans_json = instances_interface.service_plans({zoneId: cloud_id, layoutId: layout['id'], siteId: group_id}.merge(resource_pool.nil? ? {} : {'poolId' => resource_pool['id'], 'resourcePoolId' => resource_pool['id']}))
733
+ service_plans = service_plans_json["plans"]
734
+ if locked_fields.include?('plan.id')
735
+ plan_id = options[:options]['plan']['id'] rescue nil
736
+ if plan_id.nil?
737
+ plan_id = options[:options]['instance']['plan']['id'] rescue nil
738
+ end
739
+ service_plan = service_plans.find {|sp| sp['id'] == plan_id }
740
+ else
741
+ service_plan = service_plans.find {|sp| sp['id'] == options[:service_plan].to_i} if options[:service_plan]
742
+
743
+ if !service_plan
744
+ service_plans_dropdown = service_plans.collect {|sp| {'name' => sp["name"], 'value' => sp["id"], 'code' => sp['code']} } # already sorted
745
+ default_plan = nil
746
+ if payload['plan']
747
+ default_plan = payload['plan']
748
+ elsif payload['instance'] && payload['instance']['plan']
749
+ default_plan = payload['instance']['plan']
750
+ end
751
+
752
+ if options[:default_plan] && service_plans_dropdown.find {|sp| [sp["name"], sp["value"].to_s, sp["code"]].include?(options[:default_plan].to_s)}
753
+ default_plan_value = options[:default_plan]
754
+ else
755
+ default_plan_value = options[:default_plan] || (default_plan.is_a?(Hash) ? default_plan['id'] : default_plan)
756
+ end
757
+ plan_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'servicePlan', 'type' => 'select', 'fieldLabel' => 'Plan', 'selectOptions' => service_plans_dropdown, 'required' => true, 'description' => 'Choose the appropriately sized plan for this instance', 'defaultValue' => default_plan_value}],options[:options], api_client, {}, no_prompt, true)
758
+ plan_id = plan_prompt['servicePlan']
759
+ service_plan = service_plans.find {|sp| sp["id"] == plan_id.to_i }
760
+ if !service_plan
761
+ print_red_alert "Plan not found by id #{plan_id}"
762
+ exit 1
763
+ end
764
+ end
765
+ #todo: consolidate these, instances api looks for instance.plan.id and apps looks for plan.id
766
+ if options[:for_app]
767
+ payload['plan'] = {'id' => service_plan["id"], 'code' => service_plan["code"], 'name' => service_plan["name"]}
768
+ else
769
+ payload['instance']['plan'] = {'id' => service_plan["id"], 'code' => service_plan["code"], 'name' => service_plan["name"]}
770
+ end
771
+ end
772
+ }
773
+
774
+ prompt_resource_pool = -> {
775
+ # prompt for resource pool
776
+ if locked_fields.include?('config.resourcePoolId')
777
+ pool_id = payload['config']['resourcePoolId'] rescue nil
778
+ elsif locked_fields.include?('config.resourcePool')
779
+ pool_id = payload['config']['resourcePool'] rescue nil
780
+ elsif locked_fields.include?('config.azureResourceGroupId')
781
+ pool_id = payload['config']['azureResourceGroupId'] rescue nil
782
+ else
783
+ has_zone_pools = provision_type && provision_type["id"] && provision_type["hasZonePools"]
784
+ if has_zone_pools
785
+ # pluck out the resourcePoolId option type to prompt for
786
+ resource_pool_option_type = option_type_list.find {|opt| ['poolId','resourcePool','resourcePoolId','azureResourceGroupId'].include?(opt['fieldName']) }
787
+ option_type_list = option_type_list.reject {|opt| ['poolId','resourcePool','resourcePoolId','azureResourceGroupId'].include?(opt['fieldName']) }
788
+
789
+ resource_pool_options = options_interface.options_for_source('zonePools', {groupId: group_id, siteId: group_id, zoneId: cloud_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], layoutId: layout["id"]}.merge(service_plan.nil? ? {} : {planId: service_plan["id"]}))['data']
790
+ if options[:resource_pool]
791
+ resource_pool = resource_pool_options.find {|opt| opt['value'] == options[:resource_pool].to_s || opt['value'] == "pool-#{options[:resource_pool]}"}
792
+ end
793
+ pool_required = provision_type["zonePoolRequired"]
794
+ # Should pool_id have the pool-,poolGroup- prefix or not?
795
+ use_pool_prefix = resource_pool_options.find {|opt| opt['value'].to_s.include?("pool") }
796
+ if resource_pool
797
+ pool_id = resource_pool['id'] # id or value?
798
+ else
799
+ if options[:default_resource_pool]
800
+ default_resource_pool = resource_pool_options.find {|rp| rp['id'] == options[:default_resource_pool]}
801
+ end
802
+ if use_pool_prefix && options[:options]['config'] && options[:options]['config']['resourcePoolId'] && !options[:options]['config']['resourcePoolId'].to_s.include?("pool")
803
+ options[:options]['config']['resourcePoolId'] = "pool-" + options[:options]['config']['resourcePoolId'].to_s
804
+ end
805
+ resource_pool_option_type ||= {'fieldContext' => 'config', 'fieldName' => 'resourcePoolId', 'type' => 'select', 'fieldLabel' => 'Resource Pool', 'selectOptions' => resource_pool_options, 'required' => pool_required, 'skipSingleOption' => true, 'description' => 'Select resource pool.', 'defaultValue' => default_resource_pool ? default_resource_pool['name'] : nil}
806
+ resource_pool_prompt = MtnCloud::Cli::OptionTypes.prompt([resource_pool_option_type],options[:options],api_client,{}, no_prompt, true)
807
+ resource_pool_prompt.deep_compact!
808
+ payload.deep_merge!(resource_pool_prompt)
809
+ # this results in the layout, not the resourcePool for some reason...
810
+ #resource_pool = MtnCloud::Cli::OptionTypes.get_last_select()
811
+ if resource_pool_option_type['fieldContext'] && resource_pool_prompt[resource_pool_option_type['fieldContext']]
812
+ pool_id = resource_pool_prompt[resource_pool_option_type['fieldContext']][resource_pool_option_type['fieldName']]
813
+ elsif resource_pool_prompt[resource_pool_option_type['fieldName']]
814
+ pool_id = resource_pool_prompt[resource_pool_option_type['fieldName']]
815
+ end
816
+ resource_pool ||= resource_pool_options.find {|it| it['value'].to_s == pool_id.to_s}
817
+ end
818
+ end
819
+ end
820
+ }
821
+
822
+ prompt_provision_options = -> {
823
+ if !provision_type.nil? && !provision_type['optionTypes'].nil? && !provision_type['optionTypes'].empty?
824
+ option_type_list += provision_type['optionTypes'].reject {|it| (it['fieldGroup'] || '').downcase == 'provisiontype'}
825
+ provision_config_payload = MtnCloud::Cli::OptionTypes.prompt(provision_type['optionTypes'].reject {|it| (it['fieldGroup'] || '').downcase != 'provisiontype'}, options[:options], @api_client, api_params, no_prompt, true)
826
+ payload.deep_merge!(provision_config_payload)
827
+ end
828
+ }
829
+
830
+ if ['openstack', 'huawei', 'opentelekom'].include?(cloud_type['zoneType']['code'])
831
+ prompt_resource_pool.call
832
+ prompt_provision_options.call
833
+ prompt_service_plan.call
834
+ else
835
+ prompt_service_plan.call
836
+ prompt_provision_options.call
837
+ prompt_resource_pool.call
838
+ end
839
+
840
+ # remove host selection for kubernetes
841
+ if resource_pool
842
+ payload['config']['poolProviderType'] = resource_pool['providerType'] if resource_pool['providerType']
843
+ if resource_pool['providerType'] == 'kubernetes'
844
+ option_type_list = option_type_list.reject {|opt| ['provisionServerId'].include?(opt['fieldName'])}
845
+ end
846
+
847
+ # add selectable datastores
848
+ # no longer use the plan datastores info, always load from data-stores api instead
849
+ # note: this modifies service_plan in place with 'datastores' and 'autoOptions'
850
+ if options[:select_datastore]
851
+ if provision_type['hasDatastore']
852
+ datastore_params = {'zoneId' => cloud_id, 'siteId' => group_id, 'resourcePoolId' => resource_pool['id']}
853
+ load_service_plan_datastores(service_plan, provision_type, datastore_params)
854
+ end
855
+ end
856
+ end
857
+
858
+ # plan_info has this property already..
859
+ # has_datastore = provision_type && provision_type["id"] && provision_type["hasDatastore"]
860
+ # service_plan['hasDatastore'] = has_datastore
861
+
862
+ # set root volume name if has mounts
863
+ mounts = (layout['mounts'] || []).reject {|it| !it['canPersist']}
864
+ if mounts.count > 0
865
+ options[:root_volume_name] = mounts[0]['shortName']
866
+ end
867
+
868
+ # prompt for volumes
869
+ if locked_fields.include?('volumes')
870
+ payload['volumes'] = options[:options]['volumes'] if options[:options]['volumes']
871
+ else
872
+ volumes = prompt_volumes(service_plan, provision_type, options, api_client, {zoneId: cloud_id, layoutId: layout['id'], siteId: group_id})
873
+ if !volumes.empty?
874
+ payload['volumes'] = volumes
875
+ end
876
+ end
877
+
878
+ # plan customizations
879
+ plan_opts = prompt_service_plan_options(service_plan, options, api_client, {zoneId: cloud_id, layoutId: layout['id'], siteId: group_id})
880
+ if plan_opts && !plan_opts.empty?
881
+ payload['servicePlanOptions'] = plan_opts
882
+ end
883
+
884
+ # prompt networks
885
+ if locked_fields.include?('networks')
886
+ # payload['networkInterfaces'] = options[:options]['networkInterfaces'] if options[:options]['networkInterfaces']
887
+ else
888
+ if provision_type && provision_type["hasNetworks"]
889
+ # prompt for network interfaces (if supported)
890
+ begin
891
+ network_interfaces = prompt_network_interfaces(cloud_id, provision_type["id"], pool_id, options.merge({:api_params => payload['config']}))
892
+ if !network_interfaces.empty?
893
+ payload['networkInterfaces'] = network_interfaces
894
+ end
895
+ rescue RestClient::Exception => e
896
+ print yellow,"Unable to load network options. Proceeding...",reset,"\n"
897
+ print_rest_exception(e, options) if MtnCloud::Logging.debug?
898
+ end
899
+ end
900
+ end
901
+
902
+ # Security Groups
903
+ # look for securityGroups option type... this is old and goofy
904
+ sg_option_type = option_type_list.find {|opt| ((opt['code'] == 'provisionType.amazon.securityId') || (opt['fieldName'] == 'securityId' || opt['name'] == 'securityId')) }
905
+ option_type_list = option_type_list.reject {|opt| ((opt['code'] == 'provisionType.amazon.securityId') || (opt['fieldName'] == 'securityId' || opt['name'] == 'securityId')) }
906
+ if locked_fields.include?('securityGroups')
907
+ # payload['securityGroups'] = options[:options]['securityGroups'] if options[:options]['securityGroups']
908
+ else
909
+ # prompt for multiple security groups
910
+ # ok.. seed data has changed and serverTypes do not have this optionType anymore...
911
+ if sg_option_type.nil?
912
+ if provision_type && (provision_type["code"] == 'amazon')
913
+ sg_option_type = {'fieldContext' => 'config', 'fieldName' => 'securityId', 'type' => 'select', 'fieldLabel' => 'Security Group', 'optionSource' => 'amazonSecurityGroup', 'optionSourceType' => 'amazon', 'required' => true, 'description' => 'Select security group.', 'defaultValue' => options[:default_security_group]}
914
+ end
915
+ end
916
+ sg_api_params = {zoneId: cloud_id, poolId: pool_id}
917
+ # The amazon plugin uses a new optionSource options/amazon/awsPluginEc2SecurityGroup
918
+ # which expects parameter config.resourcePoolId=pool-ID
919
+ if sg_option_type
920
+ if sg_option_type['optionSource'] == 'awsPluginEc2SecurityGroup'
921
+ if pool_id
922
+ sg_api_params[:config] ||= {}
923
+ sg_api_params[:config][:resourcePoolId] = pool_id
924
+ end
925
+ # convert multiSelect to select to make prompt_security_groups() work
926
+ # though we should should be able to skip the prompt_security_groups and use multiSelect instead
927
+ sg_option_type['type'] = 'select' if sg_option_type['type'] == 'multiSelect'
928
+ sg_option_type['type'] = 'typeahead' if sg_option_type['type'] == 'multiTypeahead'
929
+ sg_option_type['fieldLabel'] = 'Security Group' if sg_option_type['fieldLabel'] == 'Security Groups'
930
+ sg_option_type['required'] = true
931
+ end
932
+ end
933
+ has_security_groups = !!sg_option_type
934
+ if options[:security_groups]
935
+ available_security_groups = []
936
+ if sg_option_type && sg_option_type['optionSource']
937
+ sg_option_results = options_interface.options_for_source(sg_option_type['optionSource'], sg_api_params, sg_option_type['optionSourceType'])
938
+ available_security_groups = sg_option_results['data'].collect do |it|
939
+ {"id" => it["value"] || it["id"], "name" => it["name"], "value" => it["value"] || it["id"]}
940
+ end
941
+ end
942
+ # work with id or names, API expects ids though.
943
+ payload['securityGroups'] = options[:security_groups].collect {|sg_id|
944
+ found_sg = available_security_groups.find {|it| sg_id && (sg_id.to_s == it['id'].to_s || sg_id.to_s == it['name'].to_s) }
945
+ if found_sg.nil?
946
+ print_red_alert "Security group not found by name or id '#{sg_id}'"
947
+ exit 1
948
+ end
949
+ {'id' => found_sg['id']}
950
+ }
951
+
952
+ elsif has_security_groups
953
+ do_prompt_sg = true
954
+ if options[:default_security_groups]
955
+ payload['securityGroups'] = options[:default_security_groups]
956
+ security_groups_value = options[:default_security_groups].collect {|sg| sg['id'] }.join(',') rescue options[:default_security_groups]
957
+ # do_prompt_sg = (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Modify security groups? (#{security_groups_value})", {:default => false}))
958
+ do_prompt_sg = (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Modify security groups?", {:default => false}))
959
+ end
960
+ if do_prompt_sg
961
+ security_groups_array = prompt_security_groups(sg_option_type, sg_api_params, options)
962
+ if !security_groups_array.empty?
963
+ payload['securityGroups'] = security_groups_array.collect {|sg_id| {'id' => sg_id} }
964
+ end
965
+ end
966
+ end
967
+ end
968
+
969
+ # prompt for option types
970
+ api_params['config'] = payload['config'] if payload['config']
971
+ api_params['poolId'] = payload['config']['resourcePoolId'] if payload['config'] && payload['config']['resourcePoolId']
972
+ api_params['resourcePoolId'] = api_params['poolId']
973
+ # some option sources expect networkIntefaces passed as networkInterfaceIds[] eg. oraclecloudAvailabilityDomains
974
+ if payload['networkInterfaces']
975
+ begin
976
+ api_params['networkInterfaceIds[]'] = payload['networkInterfaces'].collect {|it| it['network']['id'] }
977
+ rescue => netex
978
+ MtnCloud::Logging::DarkPrinter.puts "Unable to parse networkInterfaces parameter" if MtnCloud::Logging.debug?
979
+ end
980
+ end
981
+
982
+ # set option type defaults from config
983
+ if options[:default_config]
984
+ options[:default_config].each do |k,v|
985
+ if v && !(v.kind_of?(Array) && v.empty?)
986
+ option_type = option_type_list.find {|ot| ot['fieldContext'] == 'config' && ot['fieldName'] == k}
987
+ option_type['defaultValue'] = v if option_type
988
+ end
989
+ end
990
+ end
991
+
992
+ option_type_list += [
993
+ {'fieldName' => 'userGroup.id', 'fieldLabel' => 'User Group', 'fieldGroup' => 'User Config', 'type' => 'select', 'optionSource' => 'userGroups', 'displayOrder' => 0, 'fieldContext' => 'instance'},
994
+ {'fieldName' => 'hostName', 'fieldLabel' => 'Hostname', 'fieldGroup' => 'Advanced', 'type' => 'string', 'displayOrder' => 1},
995
+ {'fieldName' => 'networkDomain.id', 'fieldLabel' => 'Domain', 'fieldGroup' => 'Advanced', 'type' => 'select', 'optionSource' => 'networkDomains', 'displayOrder' => 2, 'fieldContext' => 'instance'},
996
+ {'fieldName' => 'timezone', 'fieldLabel' => 'Time Zone', 'fieldGroup' => 'Advanced', 'type' => 'select', 'optionSource' => 'timezones', 'displayOrder' => 3, 'fieldContext' => 'config'}
997
+ ]
998
+
999
+ if instance_type['hasAutoScale']
1000
+ option_type_list += [
1001
+ {'fieldName' => 'layoutSize', 'fieldLabel' => 'Scale Factor', 'fieldGroup' => 'Advanced', 'type' => 'number', 'defaultValue' => 1, 'displayOrder' => 0},
1002
+ ]
1003
+ end
1004
+
1005
+ instance_config_payload = MtnCloud::Cli::OptionTypes.prompt(option_type_list.reject {|ot| ot['type'] == 'exposedPorts'}, options[:options], @api_client, api_params, no_prompt, true)
1006
+ payload.deep_merge!(instance_config_payload)
1007
+
1008
+ # prompt for exposed ports
1009
+ if payload['ports'].nil?
1010
+ # need a way to know if the instanceType even supports this.
1011
+ # the default ports come from the node type, under layout['containerTypes']
1012
+ ports = prompt_exposed_ports(options)
1013
+ if !ports.empty?
1014
+ payload['ports'] = ports
1015
+ end
1016
+ end
1017
+
1018
+ # prompt for environment variables
1019
+ evars = prompt_evars(options)
1020
+ if !evars.empty?
1021
+ payload['evars'] = evars
1022
+ end
1023
+
1024
+ # metadata tags
1025
+ # metadata_tag_key = 'metadata'
1026
+ metadata_tag_key = 'tags'
1027
+ if !options[:metadata]
1028
+ if options[:options]['metadata'].is_a?(Array)
1029
+ options[:metadata] = options[:options]['metadata']
1030
+ end
1031
+ if options[:options]['tags'].is_a?(Array)
1032
+ options[:metadata] = options[:options]['tags']
1033
+ end
1034
+ end
1035
+ if options[:metadata]
1036
+ metadata = []
1037
+ if options[:metadata] == "[]" || options[:metadata] == "null"
1038
+ metadata = []
1039
+ elsif options[:metadata].is_a?(Array)
1040
+ metadata = options[:metadata]
1041
+ else
1042
+ # parse string into format name:value, name:value
1043
+ # merge IDs from current metadata
1044
+ # todo: should allow quoted semicolons..
1045
+ metadata = options[:metadata].split(",").select {|it| !it.to_s.empty? }
1046
+ metadata = metadata.collect do |it|
1047
+ metadata_pair = it.split(":")
1048
+ if metadata_pair.size < 2 && it.include?("=")
1049
+ metadata_pair = it.split("=")
1050
+ end
1051
+ row = {}
1052
+ row['name'] = metadata_pair[0].to_s.strip
1053
+ row['value'] = metadata_pair[1].to_s.strip
1054
+ row
1055
+ end
1056
+ end
1057
+ payload[metadata_tag_key] = metadata
1058
+ else
1059
+ # prompt for metadata tags
1060
+ metadata = prompt_metadata(options)
1061
+ if !metadata.empty?
1062
+ payload[metadata_tag_key] = metadata
1063
+ end
1064
+ end
1065
+
1066
+ # Fix any hugepages option type is using on/off which
1067
+ if payload['config']
1068
+ if payload['config']['hugepages'] == "off"
1069
+ payload['config'].delete('hugepages')
1070
+ end
1071
+ end
1072
+
1073
+ return payload
1074
+ end
1075
+
1076
+ # This recreates the behavior of multi_disk.js
1077
+ # returns array of volumes based on service plan options (plan_info)
1078
+ def prompt_volumes(plan_info, provision_type, options={}, api_client=nil, api_params={})
1079
+ #puts "Configure Volumes:"
1080
+ # return [] if plan_info['noDisks']
1081
+
1082
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
1083
+ volumes = []
1084
+ plan_size = 0
1085
+ if plan_info['maxStorage'].to_i > 0
1086
+ plan_size = plan_info['maxStorage'].to_i / (1024 * 1024 * 1024)
1087
+ end
1088
+ root_storage_types = []
1089
+ if plan_info['rootStorageTypes']
1090
+ plan_info['rootStorageTypes'].sort {|x,y| x['displayOrder'] <=> y['displayOrder'] }.each do |opt|
1091
+ if !opt.nil?
1092
+ root_storage_types << {'name' => opt['name'], 'value' => opt['id']}
1093
+ end
1094
+ end
1095
+ end
1096
+
1097
+ storage_types = []
1098
+ if plan_info['storageTypes']
1099
+ plan_info['storageTypes'].sort {|x,y| x['displayOrder'] <=> y['displayOrder'] }.each do |opt|
1100
+ if !opt.nil?
1101
+ storage_types << {'name' => opt['name'], 'value' => opt['id']}
1102
+ end
1103
+ end
1104
+ end
1105
+
1106
+ datastore_options = []
1107
+ if plan_info['supportsAutoDatastore']
1108
+ if plan_info['autoOptions']
1109
+ plan_info['autoOptions'].each do |opt|
1110
+ if !opt.nil?
1111
+ datastore_options << {'name' => opt['name'], 'value' => opt['id']}
1112
+ end
1113
+ end
1114
+ end
1115
+ end
1116
+ if plan_info['datastores']
1117
+ plan_info['datastores'].each do |k, v|
1118
+ v.each do |opt|
1119
+ if !opt.nil?
1120
+ k = 'datastores' if k == 'store'
1121
+ k = 'clusters' if k == 'cluster'
1122
+ datastore_options << {'name' => "#{k}: #{opt['name']}", 'value' => opt['id']}
1123
+ end
1124
+ end
1125
+ end
1126
+ end
1127
+ # api does not always return datastores, so go get them if needed..
1128
+ if plan_info['hasDatastore'] && datastore_options.empty?
1129
+ option_results = options_interface.options_for_source('datastores', api_params)
1130
+ option_results['data'].each do |it|
1131
+ datastore_options << {"id" => it["value"] || it["id"], "name" => it["name"], "value" => it["value"] || it["id"]}
1132
+ end
1133
+ end
1134
+ storage_profiles = []
1135
+ if provision_type['storageProfiles']
1136
+ storage_profiles = provision_type['storageProfiles'].sort {|x,y| x['displayOrder'] <=> y['displayOrder'] }.collect {|it| {'name' => it['name'], 'value' => it['code'], 'isDefault' => it['isDefault']} }
1137
+ end
1138
+
1139
+ #puts "Configure Root Volume"
1140
+
1141
+ field_context = "rootVolume"
1142
+
1143
+ volume_label = options[:root_volume_name] || 'root'
1144
+ volume = {
1145
+ 'id' => -1,
1146
+ 'rootVolume' => true,
1147
+ 'name' => volume_label,
1148
+ 'size' => plan_size,
1149
+ 'sizeId' => nil,
1150
+ 'storageType' => nil,
1151
+ 'datastoreId' => nil
1152
+ }
1153
+ if options[:options] && options[:options]['volumes'] && options[:options]['volumes'][0]
1154
+ volume = options[:options]['volumes'][0]
1155
+ end
1156
+
1157
+ if root_storage_types.empty?
1158
+ # this means there's no configuration, just send a single root volume to the server
1159
+ storage_type_id = nil
1160
+ storage_type = nil
1161
+ else
1162
+ default_storage_type = root_storage_types.find {|t| t['value'].to_s == (volume['typeId'] || volume['storageType']).to_s}
1163
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => 'Root Storage Type', 'selectOptions' => root_storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.', 'defaultValue' => default_storage_type ? default_storage_type['name'] : volume['storageType']}], options[:options])
1164
+ storage_type_id = v_prompt[field_context]['storageType']
1165
+ storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1166
+ volume['storageType'] = storage_type_id
1167
+ end
1168
+
1169
+ # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1170
+ root_custom_size_options = []
1171
+ if plan_info['rootCustomSizeOptions'] && plan_info['rootCustomSizeOptions'][storage_type_id.to_s]
1172
+ plan_info['rootCustomSizeOptions'][storage_type_id.to_s].each do |opt|
1173
+ if !opt.nil?
1174
+ root_custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1175
+ end
1176
+ end
1177
+ end
1178
+
1179
+ if plan_info['rootDiskCustomizable'] && storage_type && storage_type['customLabel']
1180
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Root Volume Label', 'required' => true, 'description' => 'Enter a volume label.', 'defaultValue' => volume['name']}], options[:options])
1181
+ volume['name'] = v_prompt[field_context]['name']
1182
+ end
1183
+ if plan_info['rootDiskCustomizable'] && storage_type && storage_type['customSize']
1184
+ # provision_type['rootDiskSizeKnown'] == false means size cannot be changed
1185
+ if provision_type['rootDiskSizeKnown'] == false
1186
+ # volume['size'] = plan_size if plan_size.to_i != 0
1187
+ else
1188
+ if root_custom_size_options.empty?
1189
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => 'Root Volume Size (GB)', 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => volume['size']}], options[:options])
1190
+ volume['size'] = v_prompt[field_context]['size']
1191
+ volume['sizeId'] = nil #volume.delete('sizeId')
1192
+ else
1193
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => 'Root Volume Size', 'selectOptions' => root_custom_size_options, 'required' => true, 'description' => 'Choose a volume size.', 'defaultValue' => volume['sizeId']}], options[:options])
1194
+ volume['sizeId'] = v_prompt[field_context]['sizeId']
1195
+ volume['size'] = nil #volume.delete('size')
1196
+ end
1197
+ end
1198
+ # root volume storage controller mount input is disabled in the UI.
1199
+ # if plan_info['customizeVolume'] # && storage_type['customStorageController']
1200
+ # cmp = prompt_volume_controller_mount_point(volumes, field_context, volume_index, volume, plan_info, provision_type, options, server)
1201
+ # volume['controllerMountPoint'] = cmp if !cmp.to_s.empty?
1202
+ # end
1203
+ else
1204
+ # might need different logic here ? =o
1205
+ #volume['size'] = plan_size
1206
+ #volume['sizeId'] = nil #volume.delete('sizeId')
1207
+ end
1208
+
1209
+ if !datastore_options.empty? && !provision_type['disableRootDatastore']
1210
+ default_datastore = datastore_options.find {|ds| ds['value'].to_s == volume['datastoreId'].to_s}
1211
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => 'Root Datastore', 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.', 'defaultValue' => default_datastore ? default_datastore['name'] : volume['datastoreId']}], options[:options])
1212
+ volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1213
+ end
1214
+ if !storage_profiles.empty?
1215
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageProfile', 'type' => 'select', 'fieldLabel' => 'Root Storage Profile', 'selectOptions' => storage_profiles, 'required' => false, 'description' => 'Choose a storage profile.'}], options[:options])
1216
+ volume['storageProfile'] = v_prompt[field_context]['storageProfile'] if v_prompt[field_context] && !v_prompt[field_context]['storageProfile'].to_s.empty?
1217
+ end
1218
+
1219
+ volumes << volume
1220
+
1221
+ if plan_info['addVolumes']
1222
+ volume_index = 1
1223
+ has_another_volume = (options[:options] && options[:options]["dataVolume#{volume_index}"]) || (options[:options] && options[:options]['volumes'] && options[:options]['volumes'][volume_index])
1224
+ add_another_volume = has_another_volume || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add data volume?", {:default => (options[:defaultAddFirstDataVolume] == true && volume_index == 1)}))
1225
+ while add_another_volume do
1226
+ #puts "Configure Data #{volume_index} Volume"
1227
+
1228
+ field_context = "dataVolume#{volume_index}"
1229
+
1230
+ #volume_label = (volume_index == 1 ? 'data' : "data #{volume_index}")
1231
+ volume_label = "data-#{volume_index}"
1232
+ volume = {
1233
+ #'id' => -1,
1234
+ 'rootVolume' => false,
1235
+ 'name' => volume_label,
1236
+ 'size' => plan_size,
1237
+ 'sizeId' => nil,
1238
+ 'storageType' => nil,
1239
+ 'datastoreId' => nil
1240
+ }
1241
+ if options[:options] && options[:options]['volumes'] && options[:options]['volumes'][volume_index]
1242
+ volume = options[:options]['volumes'][volume_index]
1243
+ end
1244
+
1245
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.', 'defaultValue' => volume['storageType']}], options[:options])
1246
+ storage_type_id = v_prompt[field_context]['storageType']
1247
+ volume['storageType'] = storage_type_id
1248
+ storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1249
+
1250
+ # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1251
+ custom_size_options = []
1252
+ if plan_info['customSizeOptions'] && plan_info['customSizeOptions'][storage_type_id.to_s]
1253
+ plan_info['customSizeOptions'][storage_type_id.to_s].each do |opt|
1254
+ if !opt.nil?
1255
+ custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1256
+ end
1257
+ end
1258
+ end
1259
+
1260
+ if plan_info['customizeVolume'] && storage_type['customLabel']
1261
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "Disk #{volume_index} Volume Label", 'required' => true, 'description' => 'Enter a volume label.', 'defaultValue' => volume['name']}], options[:options])
1262
+ volume['name'] = v_prompt[field_context]['name']
1263
+ end
1264
+ if plan_info['customizeVolume'] && storage_type['customSize']
1265
+ if custom_size_options.empty?
1266
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => "Disk #{volume_index} Volume Size (GB)", 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => volume['size']}], options[:options])
1267
+ volume['size'] = v_prompt[field_context]['size']
1268
+ volume['sizeId'] = nil #volume.delete('sizeId')
1269
+ else
1270
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Volume Size", 'selectOptions' => custom_size_options, 'required' => true, 'description' => 'Choose a volume size.', 'defaultValue' => volume['sizeId']}], options[:options])
1271
+ volume['sizeId'] = v_prompt[field_context]['sizeId']
1272
+ volume['size'] = nil #volume.delete('size')
1273
+ end
1274
+ else
1275
+ # might need different logic here ? =o
1276
+ volume['size'] = plan_size
1277
+ volume['sizeId'] = nil #volume.delete('sizeId')
1278
+ end
1279
+ #todo: Pass in virtual_image to prompt based on its storage_controllers (and volume configuration)
1280
+ if plan_info['customizeVolume'] # && storage_type['customStorageController']
1281
+ cmp = prompt_volume_controller_mount_point(volumes, field_context, volume_index, volume, plan_info, provision_type, options, nil)
1282
+ volume['controllerMountPoint'] = cmp if !cmp.to_s.empty?
1283
+ end
1284
+ if !datastore_options.empty?
1285
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Datastore", 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.', 'defaultValue' => volume['datastoreId']}], options[:options])
1286
+ volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1287
+ end
1288
+ if !storage_profiles.empty?
1289
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageProfile', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Profile", 'selectOptions' => storage_profiles, 'required' => false, 'description' => 'Choose a storage profile.'}], options[:options])
1290
+ volume['storageProfile'] = v_prompt[field_context]['storageProfile'] if v_prompt[field_context] && !v_prompt[field_context]['storageProfile'].to_s.empty?
1291
+ end
1292
+
1293
+ volumes << volume
1294
+
1295
+ volume_index += 1
1296
+ if options[:options] && options[:options]['volumes'] && options[:options]['volumes'][volume_index]
1297
+ add_another_volume = true
1298
+ elsif plan_info['maxDisk'] && volume_index >= plan_info['maxDisk']
1299
+ # todo: should maxDisk check consider the root volume too?
1300
+ add_another_volume = false
1301
+ else
1302
+ has_another_volume = options[:options] && options[:options]["dataVolume#{volume_index}"]
1303
+ add_another_volume = has_another_volume || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add another data volume?", {:default => false}))
1304
+ end
1305
+
1306
+ end
1307
+
1308
+ end
1309
+
1310
+ return volumes
1311
+ end
1312
+
1313
+
1314
+ # This recreates the behavior of multi_disk.js
1315
+ # returns array of volumes based on service plan options (plan_info)
1316
+ def prompt_resize_volumes(current_volumes, plan_info, provision_type, options={}, server=nil)
1317
+ #puts "Configure Volumes:"
1318
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
1319
+
1320
+ current_root_volume = current_volumes[0]
1321
+
1322
+ volumes = []
1323
+
1324
+ plan_size = nil
1325
+ if plan_info['maxStorage'].to_i > 0
1326
+ plan_size = plan_info['maxStorage'].to_i / (1024 * 1024 * 1024)
1327
+ end
1328
+
1329
+ root_storage_types = []
1330
+ if plan_info['rootStorageTypes']
1331
+ plan_info['rootStorageTypes'].each do |opt|
1332
+ if !opt.nil?
1333
+ root_storage_types << {'name' => opt['name'], 'value' => opt['id']}
1334
+ end
1335
+ end
1336
+ end
1337
+
1338
+ storage_types = []
1339
+ if plan_info['storageTypes']
1340
+ plan_info['storageTypes'].each do |opt|
1341
+ if !opt.nil?
1342
+ storage_types << {'name' => opt['name'], 'value' => opt['id']}
1343
+ end
1344
+ end
1345
+ end
1346
+
1347
+ # no longer use the plan datastores info, always load from data-stores api instead
1348
+ # note: this modifies service_plan in place with 'datastores' and 'autoOptions'
1349
+ group_id = cloud_id = options['siteId']
1350
+ cloud_id = options['zoneId']
1351
+ server_resource_pool_id = server ? (server['resourcePoolId'] || (server['resourcePool'] ? server['resourcePool']['id'] : nil)) : nil
1352
+ resource_pool_id = options['resourcePoolId'] || server_resource_pool_id
1353
+ if provision_type['hasDatastore']
1354
+ datastore_params = {'zoneId' => cloud_id, 'siteId' => group_id, 'resourcePoolId' => resource_pool_id}
1355
+ load_service_plan_datastores(plan_info, provision_type, datastore_params)
1356
+ end
1357
+ datastore_options = []
1358
+ if plan_info['supportsAutoDatastore']
1359
+ if plan_info['autoOptions']
1360
+ plan_info['autoOptions'].each do |opt|
1361
+ if !opt.nil?
1362
+ datastore_options << {'name' => opt['name'], 'value' => opt['id']}
1363
+ end
1364
+ end
1365
+ end
1366
+ end
1367
+ if plan_info['datastores']
1368
+ plan_info['datastores'].each do |k, v|
1369
+ v.each do |opt|
1370
+ if !opt.nil?
1371
+ datastore_options << {'name' => "#{k}: #{opt['name']}", 'value' => opt['id']}
1372
+ end
1373
+ end
1374
+ end
1375
+ end
1376
+ storage_profiles = []
1377
+ if provision_type['storageProfiles']
1378
+ storage_profiles = provision_type['storageProfiles'].sort {|x,y| x['displayOrder'] <=> y['displayOrder'] }.collect {|it| {'name' => it['name'], 'value' => it['code'], 'isDefault' => it['isDefault']} }
1379
+ end
1380
+ #puts "Configure Root Volume"
1381
+
1382
+ field_context = "rootVolume"
1383
+ storage_type = nil
1384
+ storage_type_id = nil
1385
+ if root_storage_types.empty?
1386
+ # this means there's no configuration, just send a single root volume to the server
1387
+ storage_type_id = nil
1388
+ storage_type = nil
1389
+ else
1390
+ #v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => 'Root Storage Type', 'selectOptions' => root_storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
1391
+ #storage_type_id = v_prompt[field_context]['storageType']
1392
+ storage_type_id = (current_root_volume['typeId'] || current_root_volume['storageType'] || (current_root_volume['type']['id'] rescue nil))
1393
+ storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1394
+ end
1395
+
1396
+ # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1397
+ root_custom_size_options = []
1398
+ if plan_info['rootCustomSizeOptions'] && plan_info['rootCustomSizeOptions'][storage_type_id.to_s]
1399
+ plan_info['rootCustomSizeOptions'][storage_type_id.to_s].each do |opt|
1400
+ if !opt.nil?
1401
+ root_custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1402
+ end
1403
+ end
1404
+ end
1405
+ volume = {
1406
+ 'id' => current_root_volume['id'],
1407
+ 'rootVolume' => true,
1408
+ 'name' => current_root_volume['name'],
1409
+ 'size' => get_volume_size(current_root_volume) || plan_size,
1410
+ 'sizeId' => nil,
1411
+ 'storageType' => storage_type_id,
1412
+ 'datastoreId' => current_root_volume['datastoreId']
1413
+ }
1414
+ if !current_root_volume['controllerMountPoint'].to_s.empty?
1415
+ volume['controllerMountPoint'] = current_root_volume['controllerMountPoint']
1416
+ end
1417
+ if !current_root_volume['storageProfile'].to_s.empty?
1418
+ volume['storageProfile'] = current_root_volume['storageProfile']
1419
+ end
1420
+ if plan_info['rootDiskCustomizable'] && storage_type && storage_type['customLabel']
1421
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Root Volume Label', 'required' => true, 'description' => 'Enter a volume label.', 'defaultValue' => volume['name']}], options[:options])
1422
+ volume['name'] = v_prompt[field_context]['name']
1423
+ end
1424
+ if plan_info['rootDiskCustomizable'] && storage_type && storage_type['customSize']
1425
+ # provision_type['rootDiskSizeKnown'] == false means size cannot be changed
1426
+ if provision_type['rootDiskSizeKnown'] == false
1427
+ # volume['size'] = plan_size if plan_size.to_i != 0
1428
+ else
1429
+ if root_custom_size_options.empty?
1430
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => 'Root Volume Size (GB)', 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => volume['size']}], options[:options])
1431
+ volume['size'] = v_prompt[field_context]['size']
1432
+ volume['sizeId'] = nil #volume.delete('sizeId')
1433
+ else
1434
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => 'Root Volume Size', 'selectOptions' => root_custom_size_options, 'required' => true, 'description' => 'Choose a volume size.'}], options[:options])
1435
+ volume['sizeId'] = v_prompt[field_context]['sizeId']
1436
+ volume['size'] = nil #volume.delete('size')
1437
+ end
1438
+ end
1439
+ else
1440
+ # might need different logic here ? =o
1441
+ # volume['size'] = plan_size
1442
+ # volume['sizeId'] = nil #volume.delete('sizeId')
1443
+ end
1444
+ # if !datastore_options.empty?
1445
+ # v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => 'Root Datastore', 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.'}], options[:options])
1446
+ # volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1447
+ # end
1448
+ if !storage_profiles.empty?
1449
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageProfile', 'type' => 'select', 'fieldLabel' => 'Root Storage Profile', 'selectOptions' => storage_profiles, 'required' => false, 'description' => 'Choose a storage profile.', 'defaultValue' => volume['storageProfile']}], options[:options])
1450
+ volume['storageProfile'] = v_prompt[field_context]['storageProfile'] if v_prompt[field_context] && !v_prompt[field_context]['storageProfile'].to_s.empty?
1451
+ end
1452
+ volumes << volume
1453
+
1454
+ # modify or delete existing data volumes
1455
+ (1..(current_volumes.size-1)).each do |volume_index|
1456
+ current_volume = current_volumes[volume_index]
1457
+ if current_volume
1458
+
1459
+ field_context = "dataVolume#{volume_index}"
1460
+
1461
+ action_options = [{'name' => 'Modify', 'value' => 'modify'}, {'name' => 'Keep', 'value' => 'keep'}, {'name' => 'Delete', 'value' => 'delete'}]
1462
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'action', 'type' => 'select', 'fieldLabel' => "Modify/Keep/Delete volume '#{current_volume['name']}'", 'selectOptions' => action_options, 'required' => true, 'defaultValue' => 'keep', 'description' => 'Modify, Keep or Delete existing data volume?'}], options[:options])
1463
+ volume_action = v_prompt[field_context]['action']
1464
+
1465
+ if volume_action == 'delete'
1466
+ # deleted volume is just excluded from post params
1467
+ next
1468
+ elsif volume_action == 'keep'
1469
+ volume = {
1470
+ 'id' => current_volume['id'].to_i,
1471
+ 'rootVolume' => false,
1472
+ 'name' => current_volume['name'],
1473
+ 'size' => get_volume_size(current_volume) || plan_size,
1474
+ 'sizeId' => nil,
1475
+ 'storageType' => (current_volume['typeId'] || current_volume['storageType'] || (current_volume['type']['id'] rescue nil)),
1476
+ 'datastoreId' => current_volume['datastoreId']
1477
+ }
1478
+ if !current_volume['controllerMountPoint'].to_s.empty?
1479
+ volume['controllerMountPoint'] = current_volume['controllerMountPoint']
1480
+ end
1481
+ if !current_volume['storageProfile'].to_s.empty?
1482
+ volume['storageProfile'] = current_volume['storageProfile']
1483
+ end
1484
+ volumes << volume
1485
+ else
1486
+ # v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
1487
+ # storage_type_id = v_prompt[field_context]['storageType']
1488
+ storage_type_id = (current_volume['typeId'] || current_volume['storageType'] || (current_volume['type']['id'] rescue nil))
1489
+ storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1490
+ # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1491
+ custom_size_options = []
1492
+ if plan_info['customSizeOptions'] && plan_info['customSizeOptions'][storage_type_id.to_s]
1493
+ plan_info['customSizeOptions'][storage_type_id.to_s].each do |opt|
1494
+ if !opt.nil?
1495
+ custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1496
+ end
1497
+ end
1498
+ end
1499
+
1500
+ volume = {
1501
+ 'id' => current_volume['id'].to_i,
1502
+ 'rootVolume' => false,
1503
+ 'name' => current_volume['name'],
1504
+ 'size' => get_volume_size(current_volume) || plan_size,
1505
+ 'sizeId' => nil,
1506
+ 'storageType' => (current_volume['typeId'] || current_volume['storageType'] || (current_volume['type']['id'] rescue current_volume['type'])),
1507
+ 'datastoreId' => current_volume['datastoreId']
1508
+ }
1509
+ if !current_volume['controllerMountPoint'].to_s.empty?
1510
+ volume['controllerMountPoint'] = current_volume['controllerMountPoint']
1511
+ end
1512
+ if !current_volume['storageProfile'].to_s.empty?
1513
+ volume['storageProfile'] = current_volume['storageProfile']
1514
+ end
1515
+ if plan_info['customizeVolume'] && storage_type['customLabel']
1516
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "Disk #{volume_index} Volume Label", 'required' => true, 'description' => 'Enter a volume label.', 'defaultValue' => volume['name']}], options[:options])
1517
+ volume['name'] = v_prompt[field_context]['name']
1518
+ end
1519
+ if plan_info['customizeVolume'] && storage_type['customSize']
1520
+ if custom_size_options.empty?
1521
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => "Disk #{volume_index} Volume Size (GB)", 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => volume['size']}], options[:options])
1522
+ volume['size'] = v_prompt[field_context]['size']
1523
+ volume['sizeId'] = nil #volume.delete('sizeId')
1524
+ else
1525
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Volume Size", 'selectOptions' => custom_size_options, 'required' => true, 'description' => 'Choose a volume size.'}], options[:options])
1526
+ volume['sizeId'] = v_prompt[field_context]['sizeId']
1527
+ volume['size'] = nil #volume.delete('size')
1528
+ end
1529
+ else
1530
+ # might need different logic here ? =o
1531
+ # volume['size'] = plan_size
1532
+ # volume['sizeId'] = nil #volume.delete('sizeId')
1533
+ end
1534
+ # if plan_info['customizeVolume'] # && storage_type['customStorageController']
1535
+ # cmp = prompt_volume_controller_mount_point(volumes, field_context, volume_index, volume, plan_info, provision_type, options, server)
1536
+ # volume['controllerMountPoint'] = cmp if !cmp.to_s.empty?
1537
+ # end
1538
+ # if !datastore_options.empty?
1539
+ # v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Datastore", 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.'}], options[:options])
1540
+ # volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1541
+ # end
1542
+ if !storage_profiles.empty?
1543
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageProfile', 'type' => 'select', 'fieldLabel' => 'Root Storage Profile', 'selectOptions' => storage_profiles, 'required' => false, 'description' => 'Choose a storage profile.', 'defaultValue' => current_volume['storageProfile']}], options[:options])
1544
+ volume['storageProfile'] = v_prompt[field_context]['storageProfile'] if v_prompt[field_context] && !v_prompt[field_context]['storageProfile'].to_s.empty?
1545
+ end
1546
+
1547
+ volumes << volume
1548
+
1549
+ end
1550
+
1551
+ end
1552
+ end
1553
+
1554
+
1555
+ if plan_info['addVolumes']
1556
+ volume_index = current_volumes.size
1557
+ has_another_volume = options[:options] && options[:options]["dataVolume#{volume_index}"]
1558
+ add_another_volume = has_another_volume || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add data volume?", {:default => false}))
1559
+ while add_another_volume do
1560
+ #puts "Configure Data #{volume_index} Volume"
1561
+ previous_volume = volumes.last
1562
+ default_storage_type_id = (previous_volume['typeId'] || previous_volume['storageType'] || (previous_volume['type']['id'] rescue previous_volume['type']))
1563
+ storage_type_match = storage_types.find {|type| type['value'] == default_storage_type_id}
1564
+ default_storage_type = storage_type_match ? (storage_type_match['name'] || storage_type_match['value']) : nil
1565
+ field_context = "dataVolume#{volume_index}"
1566
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'defaultValue' => default_storage_type, 'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
1567
+ storage_type_id = v_prompt[field_context]['storageType']
1568
+ storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1569
+
1570
+ # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1571
+ custom_size_options = []
1572
+ if plan_info['customSizeOptions'] && plan_info['customSizeOptions'][storage_type_id.to_s]
1573
+ plan_info['customSizeOptions'][storage_type_id.to_s].each do |opt|
1574
+ if !opt.nil?
1575
+ custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1576
+ end
1577
+ end
1578
+ end
1579
+
1580
+ #volume_label = (volume_index == 1 ? 'data' : "data #{volume_index}")
1581
+ volume_label = "data-#{volume_index}"
1582
+ volume = {
1583
+ 'id' => -1,
1584
+ 'rootVolume' => false,
1585
+ 'name' => volume_label,
1586
+ 'size' => plan_size,
1587
+ 'sizeId' => nil,
1588
+ 'storageType' => storage_type_id,
1589
+ 'datastoreId' => nil
1590
+ }
1591
+
1592
+ if plan_info['customizeVolume'] && storage_type['customLabel']
1593
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "Disk #{volume_index} Volume Label", 'required' => true, 'description' => 'Enter a volume label.', 'defaultValue' => volume_label}], options[:options])
1594
+ volume['name'] = v_prompt[field_context]['name']
1595
+ end
1596
+ if plan_info['customizeVolume'] && storage_type['customSize']
1597
+ if custom_size_options.empty?
1598
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => "Disk #{volume_index} Volume Size (GB)", 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => volume['size']}], options[:options])
1599
+ volume['size'] = v_prompt[field_context]['size']
1600
+ volume['sizeId'] = nil #volume.delete('sizeId')
1601
+ else
1602
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Volume Size", 'selectOptions' => custom_size_options, 'required' => true, 'description' => 'Choose a volume size.'}], options[:options])
1603
+ volume['sizeId'] = v_prompt[field_context]['sizeId']
1604
+ volume['size'] = nil #volume.delete('size')
1605
+ end
1606
+ else
1607
+ # might need different logic here ? =o
1608
+ # volume['size'] = plan_size
1609
+ # volume['sizeId'] = nil #volume.delete('sizeId')
1610
+ end
1611
+ if plan_info['customizeVolume'] # && storage_type['customStorageController']
1612
+ cmp = prompt_volume_controller_mount_point(volumes, field_context, volume_index, volume, plan_info, provision_type, options, server)
1613
+ volume['controllerMountPoint'] = cmp if !cmp.to_s.empty?
1614
+ end
1615
+ if !datastore_options.empty?
1616
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Datastore", 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.', 'defaultValue' => current_root_volume['datastoreId']}], options[:options])
1617
+ volume['datastoreId'] = v_prompt[field_context]['datastoreId'] unless v_prompt[field_context]['datastoreId'].to_s.empty?
1618
+ end
1619
+ if !storage_profiles.empty?
1620
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageProfile', 'type' => 'select', 'fieldLabel' => 'Root Storage Profile', 'selectOptions' => storage_profiles, 'required' => false, 'description' => 'Choose a storage profile.'}], options[:options])
1621
+ volume['storageProfile'] = v_prompt[field_context]['storageProfile'] if v_prompt[field_context] && !v_prompt[field_context]['storageProfile'].to_s.empty?
1622
+ end
1623
+
1624
+ volumes << volume
1625
+
1626
+ # todo: should maxDisk check consider the root volume too?
1627
+ if plan_info['maxDisk'] && volume_index >= plan_info['maxDisk']
1628
+ add_another_volume = false
1629
+ else
1630
+ volume_index += 1
1631
+ has_another_volume = options[:options] && options[:options]["dataVolume#{volume_index}"]
1632
+ add_another_volume = has_another_volume || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add another data volume?", {default:false}))
1633
+ end
1634
+
1635
+ end
1636
+
1637
+ end
1638
+
1639
+ return volumes
1640
+ end
1641
+
1642
+ # loads datastores and modifies service_plan in place
1643
+ # populating datastores: {datastores: [],clusters: []}, supportsAutoDatastore: true, autoOptions: []
1644
+ def load_service_plan_datastores(service_plan, provision_type, params)
1645
+ begin
1646
+ selectable_datastores = datastores_interface.list(params)
1647
+ service_plan['datastores'] = {'clusters' => [], 'datastores' => []}
1648
+ ['clusters', 'datastores'].each do |type|
1649
+ service_plan['datastores'][type] ||= []
1650
+ selectable_datastores[type].reject { |ds| service_plan['datastores'][type].find {|it| it['id'] == ds['id']} }.each { |ds|
1651
+ service_plan['datastores'][type] << ds
1652
+ }
1653
+ end
1654
+ rescue => error
1655
+ MtnCloud::Logging::DarkPrinter.puts "Unable to load available data-stores, using datastores option source instead." if MtnCloud::Logging.debug?
1656
+ end
1657
+
1658
+ if provision_type && provision_type['supportsAutoDatastore']
1659
+ service_plan['supportsAutoDatastore'] = true
1660
+ service_plan['autoOptions'] ||= []
1661
+ if service_plan['datastores'] && service_plan['datastores']['clusters']
1662
+ if service_plan['datastores']['clusters'].count > 0 && !service_plan['autoOptions'].find {|it| it['id'] == 'autoCluster'}
1663
+ service_plan['autoOptions'] << {'id' => 'autoCluster', 'name' => 'Auto - Cluster'}
1664
+ end
1665
+ else
1666
+ service_plan['autoOptions'] << {'id' => 'autoCluster', 'name' => 'Auto - Cluster'}
1667
+ end
1668
+ if service_plan['datastores'] && service_plan['datastores']['datastores']
1669
+ if service_plan['datastores']['datastores'].count > 0 && !service_plan['autoOptions'].find {|it| it['id'] == 'auto'}
1670
+ service_plan['autoOptions'] << {'id' => 'auto', 'name' => 'Auto - Datastore'}
1671
+ end
1672
+ else
1673
+ service_plan['autoOptions'] << {'id' => 'auto', 'name' => 'Auto - Datastore'}
1674
+ end
1675
+ end
1676
+ return service_plan
1677
+ end
1678
+
1679
+ def get_volume_size(volume)
1680
+ if volume['size'].to_i > 0
1681
+ volume['size']
1682
+ elsif volume['maxStorage'].to_i > 0
1683
+ volume['maxStorage'].to_i / (1024**3)
1684
+ else
1685
+ nil
1686
+ end
1687
+ end
1688
+
1689
+ def prompt_volume_controller_mount_point(volumes, field_context, volume_index, volume, plan_info, provision_type, options={}, server=nil)
1690
+ begin
1691
+ # The /api/provision-types has always returned all the controllerTypes, yay
1692
+ # It did not return hasStorageControllers: true|false though, so fallback to checking if controllerTypes.size > 0.
1693
+ # has_storage_controllers = provision_type['hasStorageControllers'] || ['vmware'].include?(provision_type['code'])
1694
+ # storage_controller_types = has_storage_controllers ? options_interface.options_for_source('storageControllerTypes',{provisionTypeId:provision_type['id']})['data'] : []
1695
+ storage_controller_types = provision_type['controllerTypes']
1696
+ has_storage_controllers = provision_type['hasStorageControllers'] || (storage_controller_types && !storage_controller_types.empty?)
1697
+ storage_controllers = server ? server['controllers'] : []
1698
+ if has_storage_controllers
1699
+ # existing storage controllers from server
1700
+ storage_controller_options = []
1701
+ storage_controller_options.push({'name' => 'Auto', 'value' => 'auto'})
1702
+ storage_controller_options.push({'name' => 'New', 'value' => -1})
1703
+ storage_controllers.each_with_index do |sc, index|
1704
+ # storage_controller_options.push({'name' => sc['name'], 'value' => sc['id']})
1705
+ # API did not always return controller busNumber so extract from the volume mountPoint if needed
1706
+ if sc['busNumber'].nil?
1707
+ matching_volume = volumes.find {|v| v['controllerMountPoint'] && v['controllerMountPoint'].split(':')[0] == sc['id'].to_s }
1708
+ if matching_volume
1709
+ sc['busNumber'] = matching_volume['controllerMountPoint'].split(":")[1]
1710
+ end
1711
+ end
1712
+ sct = storage_controller_types.find { |t| t['id'] == sc['type']['id'] }
1713
+ storage_controller_options.push({'name' => "#{sct['category'].to_s.upcase rescue ''} #{sc['busNumber'] || index}", 'value' => sc['id']})
1714
+ end if storage_controllers
1715
+ # The controller mount point specification for this volume in the format: `id:busNumber:typeId:unitNumber"`
1716
+ # For new storage controllers the id is passed as -1, so an example value would be: `"-1:1:6:0"` which translates to id: -1 (new), busNumber: 1, storage controller type id: 6 (SCSI VMware Paravirtual), unit number: 0.
1717
+ # Split -O dataVolume1.controllerMountPoint="-1:1:6:0" into its components.
1718
+ controller_mount_point = ""
1719
+ controller_id, bus_number, controller_type_id, unit_number = nil, nil, nil, nil
1720
+ if options[:options][field_context] && options[:options][field_context]['controllerMountPoint']
1721
+ parts = options[:options][field_context]['controllerMountPoint'].split(":").collect {|it| it.to_s.strip }
1722
+ controller_id, bus_number, controller_type_id, unit_number = parts[0],parts[1],parts[2],parts[3]
1723
+ # if we dont have every part then clear the options value so it prompt down below
1724
+ if controller_id.to_s.empty? || bus_number.to_s.empty? || controller_type_id.to_s.empty? || unit_number.to_s.empty?
1725
+ options[:options][field_context].delete('controllerMountPoint')
1726
+ end
1727
+ end
1728
+ # Select Storage Controller, auto, new or existing
1729
+ # Choose Existing or Add a new controller which requires selecting type selecting type.
1730
+ if !controller_id.to_s.empty?
1731
+ controller_id = controller_id
1732
+ else
1733
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'controllerId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Controller #{volume_index}", 'selectOptions' => storage_controller_options, 'required' => true, 'description' => 'Choose a storage controller.', 'defaultValue' => (volume['controllerId'] || 'auto') }], options[:options])
1734
+ controller_id = v_prompt[field_context]['controllerId']
1735
+ end
1736
+ skip_controller_mount_point = false
1737
+ if controller_id.to_s == 'auto'
1738
+ # auto just means omit controllerMountPoint
1739
+ skip_controller_mount_point = true
1740
+ elsif controller_id.to_i < 1
1741
+ # -1 (or anything but a positive integer) means new controller
1742
+ controller_id = -1
1743
+ elsif controller_id
1744
+ controller_id = controller_id.to_i
1745
+ end
1746
+ if skip_controller_mount_point
1747
+ # skip mount point parameter
1748
+ # volume.delete('controllerMountPoint')
1749
+ # volume.delete('controllerId')
1750
+ return nil
1751
+ else
1752
+ storage_controller_type = nil
1753
+ storage_controller = nil
1754
+ # New Controller?
1755
+ if controller_id == -1
1756
+ #[field_context]['controllerTypeId'] controller_type_id = unless controller_type_id.to_s.empty?
1757
+ storage_controller_type_options = storage_controller_types.sort {|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i}.collect {|it| {"name" => it["name"], "value" => it["id"]}}
1758
+ if !controller_type_id.to_s.empty?
1759
+ options[:options][field_context]['controllerTypeId'] = controller_type_id
1760
+ end
1761
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'controllerTypeId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Controller Type", 'selectOptions' => storage_controller_type_options, 'required' => true, 'description' => 'Choose a storage controller type.'}], options[:options])
1762
+ controller_type_id = v_prompt[field_context]['controllerTypeId']
1763
+ # find the selected controller type
1764
+ storage_controller_type = storage_controller_types.find {|it| it['id'] == controller_type_id }
1765
+ if storage_controller_type.nil?
1766
+ raise_command_error("Storage controller type not found for id #{controller_type_id}")
1767
+ end
1768
+ storage_controller = {'id' => -1, 'type' => {'id' => storage_controller_type['id']}}
1769
+ max_devices = storage_controller_type['maxDevices'] || 1
1770
+ reserved_bus_numbers = storage_controllers.select {|it| it['category'] == storage_controller_type['category'] }.collect {|it| it['busNumber'].to_s}.uniq
1771
+ bus_number = bus_number.to_i || 0
1772
+ while true
1773
+ if !reserved_bus_numbers.include?(bus_number.to_s)
1774
+ break
1775
+ else
1776
+ next_bus_number = bus_number + 1
1777
+ if next_bus_number >= max_devices
1778
+ MtnCloud::Logging::DarkPrinter.puts "Failed to find an available bus number!"
1779
+ break
1780
+ end
1781
+ bus_number = next_bus_number
1782
+ end
1783
+ end
1784
+ storage_controller['busNumber'] = bus_number.to_s
1785
+ storage_controller['name'] = "#{storage_controller_type['category'].to_s.upcase} #{bus_number}"
1786
+ # storage_controller['unitNumber'] = "null"
1787
+ # add new controller to the list
1788
+ storage_controllers.push(storage_controller)
1789
+ else
1790
+ # existing controller
1791
+ # controller_type_id = controller_type_id
1792
+ # find the selected controller type
1793
+ storage_controller = storage_controllers.find {|it| it['id'] == controller_id.to_i }
1794
+ if storage_controller.nil?
1795
+ raise_command_error("Storage controller type not found for id #{controller_type_id}")
1796
+ end
1797
+ end
1798
+ # Choose Controller Mount Point
1799
+ mount_point_options = []
1800
+ controller_list = storage_controllers
1801
+ # only show the selected controller
1802
+ controller_list = [storage_controller]
1803
+ controller_list.each do |sc|
1804
+ sct = storage_controller_types.find { |t| t['id'] == sc['type']['id'] }
1805
+ category = sct['category']
1806
+ max_devices = sct['maxDevices'] || 1
1807
+ matching_type_ids = storage_controller_types.select { |t| t['category'] == category }.collect {|t| t['id'] }
1808
+ # find available unit numbers by reversing the controllerMountPoint to find matching types, blarghhh
1809
+ reserved_unit_numbers = (volumes || []).collect {|v|
1810
+ if !v['controllerMountPoint'].to_s.empty?
1811
+ parts = v['controllerMountPoint'].split(":")
1812
+ vol_controller_id = parts[0]
1813
+ vol_bus_number = parts[1]
1814
+ vol_sct_id = parts[2]
1815
+ vol_unit_number = parts[3]
1816
+ if matching_type_ids.include?(vol_sct_id.to_i)
1817
+ vol_unit_number.to_i
1818
+ else
1819
+ nil
1820
+ end
1821
+ else
1822
+ nil
1823
+ end
1824
+ }.compact.uniq
1825
+ (0..(max_devices - 1)).each do |index|
1826
+ cmp_value = [sc['id'],sc['busNumber'],sct['id'],index].join(":")
1827
+ if reserved_unit_numbers.include?(index)
1828
+ #mount_point_options << {'name' => "#{category.to_s.upcase} #{sc['busNumber']}:#{index}", 'value' => cmp_value}
1829
+ else
1830
+ mount_point_options << {'name' => "#{category.to_s.upcase} #{sc['busNumber']}:#{index}", 'value' => cmp_value}
1831
+ end
1832
+ end
1833
+ end
1834
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'controllerMountPoint', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Controller Mount Point", 'selectOptions' => mount_point_options, 'required' => true, 'description' => 'Choose a storage controller mount point.'}], options[:options])
1835
+ controller_mount_point = v_prompt[field_context]['controllerMountPoint']
1836
+ # build controllerMountPoint parameter as "id:busNumber:typeId:unitNumber"
1837
+ #controller_mount_point = [controller_id, bus_number, controller_type_id, unit_number].collect {|it| it.to_s.strip}.join(":")
1838
+ # volume['controllerMountPoint'] = controller_mount_point
1839
+ return controller_mount_point
1840
+ end
1841
+ else
1842
+ # storage controllers not supported, carry on then
1843
+ end
1844
+ rescue => e
1845
+ raise e
1846
+ MtnCloud::Cli::ErrorHandler.new(my_terminal.stderr).handle_error(e) if MtnCloud::Logging.debug?
1847
+ MtnCloud::Logging::DarkPrinter.puts "Failed to prompt for storage controller. Proceeding..."
1848
+ end
1849
+ end
1850
+
1851
+ # This recreates the behavior of multi_networks.js
1852
+ # This is used by both `instances add` and `hosts add`
1853
+ # returns array of networkInterfaces based on provision type and cloud settings
1854
+ def prompt_network_interfaces(zone_id, provision_type_id, pool_id, options={})
1855
+ #puts "Configure Networks:"
1856
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
1857
+ network_interfaces = []
1858
+ api_params = {zoneId: zone_id, provisionTypeId: provision_type_id}.merge(options[:api_params] || {})
1859
+ if pool_id
1860
+ api_params[:poolId] = pool_id
1861
+ end
1862
+
1863
+ zone_network_options_json = api_client.options.options_for_source('zoneNetworkOptions', api_params)
1864
+ # puts "zoneNetworkOptions JSON"
1865
+ # puts JSON.pretty_generate(zone_network_options_json)
1866
+ zone_network_data = zone_network_options_json['data'] || {}
1867
+ networks = zone_network_data['networks']
1868
+ network_groups = zone_network_data['networkGroups']
1869
+ if network_groups
1870
+ networks = network_groups + networks
1871
+ end
1872
+ network_subnets = zone_network_data['networkSubnets']
1873
+ if network_subnets
1874
+ networks += network_subnets
1875
+ end
1876
+ network_interface_types = (zone_network_data['networkTypes'] || []).sort { |x,y| x['displayOrder'] <=> y['displayOrder'] }
1877
+ enable_network_type_selection = (zone_network_data['enableNetworkTypeSelection'] == 'on' || zone_network_data['enableNetworkTypeSelection'] == true)
1878
+ has_networks = zone_network_data["hasNetworks"] == true
1879
+ max_networks = (zone_network_data["maxNetworks"].to_i > 0) ? zone_network_data["maxNetworks"].to_i : nil
1880
+
1881
+ # skip unless provision type supports networks
1882
+ if !has_networks
1883
+ return nil
1884
+ end
1885
+
1886
+ # no networks available, shouldn't happen
1887
+ if networks.empty?
1888
+ return network_interfaces
1889
+ end
1890
+
1891
+ network_options = []
1892
+ networks.each do |opt|
1893
+ if !opt.nil?
1894
+ network_options << {'name' => opt['name'], 'value' => opt['id']}
1895
+ end
1896
+ end
1897
+
1898
+ network_interface_type_options = []
1899
+ network_interface_types.each do |opt|
1900
+ if !opt.nil?
1901
+ network_interface_type_options << {'name' => opt['name'], 'value' => opt['id']}
1902
+ end
1903
+ end
1904
+
1905
+ interface_index = 0
1906
+ add_another_interface = true
1907
+ while add_another_interface do
1908
+ # if !no_prompt
1909
+ # if interface_index == 0
1910
+ # puts "Configure Network Interface"
1911
+ # else
1912
+ # puts "Configure Network Interface #{interface_index+1}"
1913
+ # end
1914
+ # end
1915
+
1916
+ # networkInterfaces may be passed as objects from blueprints or via -O networkInterfaces='[]'
1917
+ field_context = interface_index == 0 ? "networkInterface" : "networkInterface#{interface_index+1}"
1918
+ network_interface = {}
1919
+ if options[:options] && options[:options]['networkInterfaces'] && options[:options]['networkInterfaces'][interface_index]
1920
+ network_interface = options[:options]['networkInterfaces'][interface_index]
1921
+ end
1922
+
1923
+ default_network_id = network_interface['networkId'] || network_interface['id']
1924
+ if default_network_id.nil? && network_interface['network'].is_a?(Hash) && network_interface['network']['id']
1925
+ default_network_id = network_interface['network']['id']
1926
+ end
1927
+ # JD: this for cluster or server prompting perhaps?
1928
+ # because zoneNetworkOptions already returns foramt like "id": "network-1"
1929
+ if !default_network_id || !default_network_id.to_s.include?("-")
1930
+ if network_interface['network'] && network_interface['network']['id']
1931
+ if network_interface['network']['subnet']
1932
+ default_network_id = "subnet-#{network_interface['network']['subnet']}"
1933
+ elsif network_interface['network']['group']
1934
+ default_network_id = "networkGroup-#{network_interface['network']['group']}"
1935
+ else
1936
+ default_network_id = "network-#{network_interface['network']['id']}"
1937
+ end
1938
+ end
1939
+ end
1940
+
1941
+ default_network_value = (network_options.find {|n| n['value'] == default_network_id} || {})['name']
1942
+
1943
+ # choose network
1944
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'networkId', 'type' => 'select', 'fieldLabel' => "Network", 'selectOptions' => network_options, 'required' => true, 'skipSingleOption' => false, 'description' => 'Choose a network for this interface.', 'defaultValue' => default_network_value}], options[:options], api_client, {}, no_prompt, true)
1945
+ network_interface['network'] = {}
1946
+ network_interface['network']['id'] = v_prompt[field_context]['networkId'].to_s
1947
+ selected_network = networks.find {|it| it["id"].to_s == network_interface['network']['id'] }
1948
+ #network_options.reject! {|it| it['value'] == v_prompt[field_context]['networkId']}
1949
+
1950
+ if !selected_network
1951
+ print_red_alert "Network not found by id #{network_interface['network']['id']}!"
1952
+ exit 1
1953
+ end
1954
+
1955
+ # choose network interface type
1956
+ if enable_network_type_selection && !network_interface_type_options.empty?
1957
+ default_interface_type_value = (network_interface_type_options.find {|t| t['value'].to_s == network_interface['networkInterfaceTypeId'].to_s} || {})['name']
1958
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'networkInterfaceTypeId', 'type' => 'select', 'fieldLabel' => "Network Interface Type", 'selectOptions' => network_interface_type_options, 'required' => true, 'skipSingleOption' => true, 'description' => 'Choose a network interface type.', 'defaultValue' => default_interface_type_value}], options[:options])
1959
+ network_interface['networkInterfaceTypeId'] = v_prompt[field_context]['networkInterfaceTypeId'].to_i
1960
+ selected_network_interface_type = network_interface_types.find {|it| it["id"] == network_interface['networkInterfaceTypeId']}
1961
+ end
1962
+
1963
+ # determine if IP can/should be specified for selected network
1964
+ ip_flags = determine_ip_requirements(selected_network)
1965
+ ip_available = ip_flags[:ip_available]
1966
+ ip_required = ip_flags[:ip_required]
1967
+
1968
+ # Prompt for IP input when static assignment is allowed or an IP is explicitly required
1969
+ if ip_available || ip_required
1970
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'ipAddress', 'type' => 'text', 'fieldLabel' => "IP Address", 'required' => ip_required, 'description' => 'Enter an IP for this network interface. x.x.x.x', 'defaultValue' => network_interface['ipAddress']}], options[:options])
1971
+ if v_prompt[field_context] && !v_prompt[field_context]['ipAddress'].to_s.empty?
1972
+ network_interface['ipAddress'] = v_prompt[field_context]['ipAddress']
1973
+ end
1974
+ end
1975
+
1976
+ if ip_required == false && network_interface['ipAddress'] == nil && selected_network['dhcpServer'] == true
1977
+ network_interface['ipMode'] = 'dhcp'
1978
+ elsif network_interface['ipAddress']
1979
+ network_interface['ipMode'] = 'static'
1980
+ end
1981
+
1982
+ # prompt for virtual interfaces if supported by the selected network interface type
1983
+ virtual_interfaces = prompt_virtual_interfaces(
1984
+ selected_network_interface_type,
1985
+ networks,
1986
+ network_options,
1987
+ field_context,
1988
+ interface_index,
1989
+ options,
1990
+ no_prompt
1991
+ )
1992
+ virtual_interfaces ||= []
1993
+ network_interface['networkInterfaces'] = virtual_interfaces unless virtual_interfaces.empty?
1994
+
1995
+ # append the interface
1996
+ network_interfaces << network_interface
1997
+ interface_index += 1
1998
+ if options[:options] && options[:options]['networkInterfaces'] && options[:options]['networkInterfaces'][interface_index]
1999
+ add_another_interface = true
2000
+ elsif (max_networks && network_interfaces.size >= max_networks) || network_options.count == 0
2001
+ add_another_interface = false
2002
+ else
2003
+ has_another_interface = options[:options] && options[:options]["networkInterface#{interface_index+1}"]
2004
+ add_another_interface = has_another_interface || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add another network interface?", {:default => false}))
2005
+ end
2006
+
2007
+ end
2008
+
2009
+ return network_interfaces
2010
+
2011
+ end
2012
+
2013
+ # determine if IP can/should be specified for selected network
2014
+ def determine_ip_requirements(selected_network)
2015
+ ip_available = selected_network['allowStaticOverride'] != false
2016
+ ip_required = true
2017
+ if selected_network['id'].to_s.include?('networkGroup')
2018
+ ip_available = false
2019
+ ip_required = false
2020
+ elsif selected_network['pool']
2021
+ ip_required = false
2022
+ elsif selected_network['dhcpServer']
2023
+ ip_required = false
2024
+ end
2025
+ {ip_available: ip_available, ip_required: ip_required}
2026
+ end
2027
+
2028
+ # Prompt for virtual interfaces if supported by the selected network interface type
2029
+ def prompt_virtual_interfaces(selected_network_interface_type, networks, network_options, field_context, interface_index, options, no_prompt)
2030
+ return [] unless selected_network_interface_type && selected_network_interface_type['hasVirtualInterfaces']
2031
+
2032
+ virtual_interfaces = []
2033
+ # gather virtual interface types for the selected network interface type
2034
+ network_interface_types = selected_network_interface_type['virtualInterfaces'] || []
2035
+ return [] if network_interface_types.empty?
2036
+
2037
+ network_interface_type_options = []
2038
+ network_interface_types.each do |opt|
2039
+ next if opt.nil?
2040
+ network_interface_type_options << {'name' => opt['name'], 'value' => opt['id']}
2041
+ end
2042
+
2043
+ # Preconfigured virtual interfaces passed in options
2044
+ prespecified = false
2045
+ preconfigured_vi = []
2046
+ if options[:options]
2047
+ if options[:options]['networkInterfaces'] &&
2048
+ options[:options]['networkInterfaces'][interface_index]
2049
+ preconfigured_vi = options[:options]['networkInterfaces'][interface_index]['networkInterfaces'] || []
2050
+ prespecified = true
2051
+ end
2052
+ if options[:options][field_context].is_a?(Hash) && !options[:options][field_context].empty?
2053
+ prespecified = true
2054
+ end
2055
+ end
2056
+
2057
+ vi_index = 0
2058
+ has_another = false
2059
+ # Process any pre-configured virtual interfaces first, then prompt for additional ones.
2060
+ if preconfigured_vi[vi_index]
2061
+ has_another = true
2062
+ elsif options[:options] && options[:options][field_context] && !options[:options][field_context].empty?
2063
+ has_another = options[:options][field_context]['virtualInterface'] || false
2064
+ end
2065
+ confirm_message = prespecified ? "Add virtual network interface? (under prespecified interface #{interface_index})": "Add virtual network interface?"
2066
+
2067
+ add_another = has_another || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm(confirm_message, {default:false}))
2068
+ while add_another
2069
+ vi_field_context = vi_index == 0 ? "virtualInterface" : "virtualInterface#{vi_index+1}"
2070
+ vi = preconfigured_vi[vi_index] ? preconfigured_vi[vi_index].dup : {}
2071
+
2072
+ # Determine default network for this virtual interface
2073
+ default_network_id = vi['networkId'] || (vi['network'] && vi['network']['id'])
2074
+ default_network_name = (network_options.find {|n| n['value'] == default_network_id} || {})['name']
2075
+
2076
+ # Choose network
2077
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt(
2078
+ [{
2079
+ 'fieldContext' => "#{field_context}.#{vi_field_context}",
2080
+ 'fieldName' => 'networkId',
2081
+ 'type' => 'select',
2082
+ 'fieldLabel' => "Virtual Network",
2083
+ 'selectOptions' => network_options,
2084
+ 'required' => true,
2085
+ 'skipSingleOption' => false,
2086
+ 'description' => 'Choose a network for this virtual interface.',
2087
+ 'defaultValue' => default_network_name
2088
+ }],
2089
+ options[:options],
2090
+ nil,
2091
+ {},
2092
+ no_prompt,
2093
+ true
2094
+ )
2095
+ vi['network'] ||= {}
2096
+ vi['network']['id'] = v_prompt[field_context][vi_field_context]['networkId'].to_s
2097
+
2098
+ # determine / prompt interface type for virtual interface
2099
+ default_interface_type_id = vi['networkInterfaceTypeId'] ||
2100
+ (vi['networkInterfaceType'] && vi['networkInterfaceType']['id'])
2101
+ default_interface_type_name =
2102
+ (network_interface_type_options.find { |t| t['value'].to_s == default_interface_type_id.to_s } || {})['name']
2103
+ type_prompt = MtnCloud::Cli::OptionTypes.prompt(
2104
+ [{
2105
+ 'fieldContext' => "#{field_context}.#{vi_field_context}",
2106
+ 'fieldName' => 'networkInterfaceTypeId',
2107
+ 'type' => 'select',
2108
+ 'fieldLabel' => "Virtual Network Interface Type",
2109
+ 'selectOptions' => network_interface_type_options,
2110
+ 'required' => true,
2111
+ 'skipSingleOption' => true,
2112
+ 'description' => 'Choose a network interface type.',
2113
+ 'defaultValue' => default_interface_type_name
2114
+ }],
2115
+ options[:options]
2116
+ )
2117
+ vi['networkInterfaceTypeId'] = type_prompt[field_context][vi_field_context]['networkInterfaceTypeId'].to_i
2118
+
2119
+ # IP logic
2120
+ selected_network = networks.find {|n| n['id'].to_s == vi['network']['id'] }
2121
+ if selected_network
2122
+ ip_flags = determine_ip_requirements(selected_network)
2123
+ if ip_flags[:ip_available] || ip_flags[:ip_required]
2124
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt(
2125
+ [{
2126
+ 'fieldContext' => "#{field_context}.#{vi_field_context}",
2127
+ 'fieldName' => 'ipAddress',
2128
+ 'type' => 'text',
2129
+ 'fieldLabel' => "IP Address for Virtual Interface",
2130
+ 'required' => ip_flags[:ip_required],
2131
+ 'description' => 'Enter an IP for this virtual interface.',
2132
+ 'defaultValue' => vi['ipAddress']
2133
+ }],
2134
+ options[:options]
2135
+ )
2136
+ if v_prompt[field_context][vi_field_context] && !v_prompt[field_context][vi_field_context]['ipAddress'].to_s.empty?
2137
+ vi['ipAddress'] = v_prompt[field_context][vi_field_context]['ipAddress']
2138
+ vi['ipMode'] = 'static'
2139
+ elsif ip_flags[:ip_required] == false && selected_network['dhcpServer']
2140
+ vi['ipMode'] = 'dhcp'
2141
+ end
2142
+ end
2143
+ end
2144
+
2145
+ virtual_interfaces << vi
2146
+ vi_index += 1
2147
+
2148
+ has_another = false
2149
+ confirm_message = prespecified ? "Add another virtual network interface? (under prespecified interface #{interface_index})": "Add another virtual network interface?"
2150
+ if preconfigured_vi[vi_index]
2151
+ has_another = true
2152
+ elsif options[:options] && options[:options][field_context]
2153
+ has_another = options[:options][field_context]["virtualInterface#{vi_index+1}"] && !options[:options][field_context]["virtualInterface#{vi_index+1}"].empty?
2154
+ end
2155
+ add_another = has_another || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm(confirm_message,{default:false}))
2156
+ end
2157
+ virtual_interfaces
2158
+ end
2159
+
2160
+ # Prompts user for environment variables for new instance
2161
+ # returns array of evar objects {id: null, name: "VAR", value: "somevalue"}
2162
+ def prompt_evars(options={})
2163
+ #puts "Configure Environment Variables:"
2164
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
2165
+ evars = []
2166
+ evar_index = 0
2167
+ has_another_evar = options[:options] && options[:options]["evar#{evar_index}"]
2168
+ add_another_evar = has_another_evar || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add an environment variable?", {default: false}))
2169
+ while add_another_evar do
2170
+ field_context = "evar#{evar_index}"
2171
+ evar = {}
2172
+ evar['id'] = nil
2173
+ evar_label = evar_index == 0 ? "ENV" : "ENV [#{evar_index+1}]"
2174
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "#{evar_label} Name", 'required' => true, 'description' => 'Environment Variable Name.', 'defaultValue' => evar['name']}], options[:options])
2175
+ evar['name'] = v_prompt[field_context]['name']
2176
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'value', 'type' => 'text', 'fieldLabel' => "#{evar_label} Value", 'required' => true, 'description' => 'Environment Variable Value', 'defaultValue' => evar['value']}], options[:options])
2177
+ evar['value'] = v_prompt[field_context]['value']
2178
+ evars << evar
2179
+ evar_index += 1
2180
+ has_another_evar = options[:options] && options[:options]["evar#{evar_index}"]
2181
+ add_another_evar = has_another_evar || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add another environment variable?", {default: false}))
2182
+ end
2183
+
2184
+ return evars
2185
+ end
2186
+
2187
+ # Converts metadata tags array into a string like "name:value, foo:bar"
2188
+ def format_metadata(tags)
2189
+ if tags.nil? || tags.empty?
2190
+ ""
2191
+ elsif tags.instance_of?(Array)
2192
+ tags.collect {|tag| "#{tag['name']}: #{tag['value']}" }.sort.join(", ")
2193
+ elsif tags.instance_of?(Hash)
2194
+ tags.collect {|k,v| "#{k}: #{v}" }.sort.join(", ")
2195
+ else
2196
+ tags.to_s
2197
+ end
2198
+ end
2199
+
2200
+ # Parses metadata tags object (string) into an array
2201
+ def parse_metadata(val)
2202
+ metadata = nil
2203
+ if val
2204
+ if val == "[]" || val == "null"
2205
+ metadata = []
2206
+ elsif val.is_a?(Array)
2207
+ metadata = val
2208
+ else
2209
+ # parse string into format name:value, name:value
2210
+ # merge IDs from current metadata
2211
+ # todo: should allow quoted semicolons..
2212
+ metadata_list = val.to_s.split(",").select {|it| !it.to_s.empty? }
2213
+ metadata_list = metadata_list.collect do |it|
2214
+ metadata_pair = it.include?(":") ? it.split(":") : it.split("=")
2215
+ row = {}
2216
+ row['name'] = metadata_pair[0].to_s.strip
2217
+ row['value'] = metadata_pair[1].to_s.strip
2218
+ # hacky way to set masked flag to true of false to (masked) in the value itself
2219
+ if(row['value'].include?("(masked)"))
2220
+ row['value'] = row['value'].gsub("(masked)", "").strip
2221
+ row['masked'] = true
2222
+ end
2223
+ if(row['value'].include?("(unmasked)"))
2224
+ row['value'] = row['value'].gsub("(unmasked)", "").strip
2225
+ row['masked'] = false
2226
+ end
2227
+ row
2228
+ end
2229
+ metadata = metadata_list
2230
+ end
2231
+ end
2232
+ return metadata
2233
+ end
2234
+
2235
+ # Prompts user for environment variables for new instance
2236
+ # returns array of metadata objects {id: null, name: "MYTAG", value: "myvalue"}
2237
+ def prompt_metadata(options={})
2238
+ #puts "Configure Environment Variables:"
2239
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
2240
+ metadata_array = []
2241
+ metadata_index = 0
2242
+ # Keep Current Tags (foo:bar,hello:world)
2243
+ # this is used by clone()
2244
+ if options[:current_tags] && !options[:current_tags].empty?
2245
+ current_tags_string = options[:current_tags].collect { |tag| tag['name'].to_s + '=' + tag['value'].to_s }.join(', ')
2246
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'keepExistingTags', 'type' => 'checkbox', 'fieldLabel' => "Keep existing metadata tags (#{current_tags_string}) ?", 'required' => true, 'description' => 'Whether or not to keep existing metadata tags', 'defaultValue' => true}], options[:options])
2247
+ if ['on','true','1',''].include?(v_prompt['keepExistingTags'].to_s.downcase)
2248
+ options[:current_tags].each do |tag|
2249
+ current_tag = tag.clone
2250
+ current_tag.delete('id')
2251
+ metadata_array << current_tag
2252
+ end
2253
+ end
2254
+ end
2255
+ has_another_metadata = options[:options] && options[:options]["metadata#{metadata_index}"]
2256
+ add_another_metadata = has_another_metadata || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add a metadata tag?", {default: false}))
2257
+ while add_another_metadata do
2258
+ field_context = "metadata#{metadata_index}"
2259
+ metadata = {}
2260
+ #metadata['id'] = nil
2261
+ metadata_label = metadata_index == 0 ? "Metadata Tag" : "Metadata Tag [#{metadata_index+1}]"
2262
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "#{metadata_label} Name", 'required' => true, 'description' => 'Metadata Tag Name.', 'defaultValue' => metadata['name']}], options[:options])
2263
+ # todo: metadata.type ?
2264
+ metadata['name'] = v_prompt[field_context]['name']
2265
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'value', 'type' => 'text', 'fieldLabel' => "#{metadata_label} Value", 'required' => true, 'description' => 'Metadata Tag Value', 'defaultValue' => metadata['value']}], options[:options])
2266
+ metadata['value'] = v_prompt[field_context]['value']
2267
+ metadata_array << metadata
2268
+ metadata_index += 1
2269
+ has_another_metadata = options[:options] && options[:options]["metadata#{metadata_index}"]
2270
+ add_another_metadata = has_another_metadata || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add another metadata tag?", {default: false}))
2271
+ end
2272
+
2273
+ return metadata_array
2274
+ end
2275
+
2276
+ def prompt_security_groups(sg_option_type, api_params, options)
2277
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
2278
+ security_groups_array = []
2279
+ sg_required = sg_option_type['required']
2280
+ sg_index = 0
2281
+ add_another_sg = sg_required || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add a security group?", {default: false}))
2282
+ while add_another_sg do
2283
+ cur_sg_option_type = sg_option_type.merge({'required' => (sg_index == 0 ? sg_required : false)})
2284
+ if sg_index == 0 && !options[:default_security_group].nil?
2285
+ cur_sg_option_type['defaultValue'] = options[:default_security_group]
2286
+ end
2287
+ field_context = cur_sg_option_type['fieldContext']
2288
+ field_name = cur_sg_option_type['fieldName']
2289
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([cur_sg_option_type], options[:options], api_client, api_params)
2290
+ has_another_sg = false
2291
+ if field_context
2292
+ if v_prompt[field_context] && !v_prompt[field_context][field_name].to_s.empty?
2293
+ security_groups_array << v_prompt[field_context][field_name]
2294
+ end
2295
+ has_another_sg = options[:options] && options[:options][field_context] && options[:options][field_context]["#{field_name}#{sg_index+2}"]
2296
+ else
2297
+ if !v_prompt[field_name].to_s.empty?
2298
+ security_groups_array << v_prompt[field_name]
2299
+ end
2300
+ has_another_sg = options[:options] && options[:options]["#{field_name}#{sg_index+2}"]
2301
+ end
2302
+ add_another_sg = has_another_sg || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add another security group?", {default: false}))
2303
+ sg_index += 1
2304
+ end
2305
+
2306
+ return security_groups_array
2307
+ end
2308
+
2309
+ # Prompts user for load balancer settings
2310
+ # returns Hash of parameters like {loadBalancerId: "-1", etc}
2311
+ def prompt_instance_load_balancer(instance, default_lb_id, options)
2312
+ #puts "Configure Environment Variables:"
2313
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
2314
+ payload = {}
2315
+ api_params = {}
2316
+ if instance['id']
2317
+ api_params['instanceId'] = instance['id']
2318
+ end
2319
+ if instance['zone']
2320
+ api_params['zoneId'] = instance['zone']['id']
2321
+ elsif instance['cloud']
2322
+ api_params['zoneId'] = instance['cloud']['id']
2323
+ end
2324
+ if instance['group']
2325
+ api_params['siteId'] = instance['group']['id']
2326
+ elsif instance['site']
2327
+ api_params['siteId'] = instance['site']['id']
2328
+ end
2329
+ if instance['plan']
2330
+ api_params['planId'] = instance['plan']['id']
2331
+ end
2332
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'loadBalancerId', 'type' => 'select', 'fieldLabel' => "Load Balancer", 'optionSource' => 'loadBalancers', 'required' => true, 'description' => 'Select Load Balancer for instance', 'defaultValue' => default_lb_id || ''}], options[:options], api_client, api_params)
2333
+ lb_id = v_prompt['loadBalancerId']
2334
+ payload['loadBalancerId'] = lb_id
2335
+
2336
+ # todo: finish implmenting this
2337
+
2338
+ # loadBalancerId
2339
+ # loadBalancerProxyProtocol
2340
+ # loadBalancerName
2341
+ # loadBalancerDescription
2342
+ # loadBalancerSslCert
2343
+ # loadBalancerScheme
2344
+
2345
+ return payload
2346
+ end
2347
+
2348
+ def prompt_cluster_load_balancer(cluster, options, can_use_kubevip)
2349
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'loadBalancerTypeId', 'type' => 'select', 'fieldLabel' => "Load Balancer", 'optionSource' => 'loadBalancerTypes', 'required' => false, 'description' => 'Select Load Balancer for Cluster', 'defaultValue' => '', 'excludeKubevip' => !can_use_kubevip}], options[:options], api_client, cluster)
2350
+ lb_type_id = v_prompt['loadBalancerTypeId']
2351
+
2352
+ if lb_type_id.empty?
2353
+ return false
2354
+ end
2355
+
2356
+ return lb_type_id
2357
+ end
2358
+
2359
+
2360
+ # reject old option types that now come from the selected service plan
2361
+ # these will eventually get removed from the associated optionTypes
2362
+ def reject_service_plan_option_types(option_types)
2363
+ option_types.reject {|opt|
2364
+ ['cpuCount', 'memorySize', 'memory'].include?(opt['fieldName'])
2365
+ }
2366
+ end
2367
+
2368
+ def get_available_environments(refresh=false)
2369
+ if !@available_environments || refresh
2370
+ begin
2371
+ option_results = options_interface.options_for_source('environments',{})
2372
+ @available_environments = option_results['data'].collect {|it|
2373
+ {"code" => (it["code"] || it["value"]), "name" => it["name"], "value" => it["value"]}
2374
+ }
2375
+ rescue RestClient::Exception => e
2376
+ # if e.response && e.response.code == 404
2377
+ MtnCloud::Logging::DarkPrinter.puts "Unable to determine available environments, using default options" if MtnCloud::Logging.debug?
2378
+ @available_environments = get_static_environments()
2379
+ # else
2380
+ # raise e
2381
+ # end
2382
+ end
2383
+ end
2384
+ return @available_environments
2385
+ end
2386
+
2387
+ def get_static_environments
2388
+ [{'name' => 'Dev', 'value' => 'dev'}, {'name' => 'Test', 'value' => 'qa'}, {'name' => 'Staging', 'value' => 'staging'}, {'name' => 'Production', 'value' => 'production'}]
2389
+ end
2390
+
2391
+ def add_perms_options(opts, options, excludes = [])
2392
+ if !excludes.include?('groups')
2393
+ opts.on('--group-access-all [on|off]', String, "Toggle Access for all groups.") do |val|
2394
+ options[:groupAccessAll] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
2395
+ end
2396
+ opts.on('--group-access LIST', Array, "Group Access, comma separated list of group IDs.") do |list|
2397
+ if list.size == 1 && list[0] == 'null' # hacky way to clear it
2398
+ options[:groupAccessList] = []
2399
+ else
2400
+ options[:groupAccessList] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
2401
+ end
2402
+ end
2403
+ if !excludes.include?('groupDefaults')
2404
+ opts.on('--group-defaults LIST', Array, "Group Default Selection, comma separated list of group IDs") do |list|
2405
+ if list.size == 1 && list[0] == 'null' # hacky way to clear it
2406
+ options[:groupDefaultsList] = []
2407
+ else
2408
+ options[:groupDefaultsList] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
2409
+ end
2410
+ end
2411
+ end
2412
+ end
2413
+
2414
+ if !excludes.include?('plans')
2415
+ opts.on('--plan-access-all [on|off]', String, "Toggle Access for all service plans.") do |val|
2416
+ options[:planAccessAll] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
2417
+ end
2418
+ opts.on('--plan-access LIST', Array, "Service Plan Access, comma separated list of plan IDs.") do |list|
2419
+ if list.size == 1 && list[0] == 'null' # hacky way to clear it
2420
+ options[:planAccessList] = []
2421
+ else
2422
+ options[:planAccessList] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
2423
+ end
2424
+ end
2425
+ opts.on('--plan-defaults LIST', Array, "Plan Default Selection, comma separated list of plan IDs") do |list|
2426
+ if list.size == 1 && list[0] == 'null' # hacky way to clear it
2427
+ options[:planDefaultsList] = []
2428
+ else
2429
+ options[:planDefaultsList] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
2430
+ end
2431
+ end
2432
+ end
2433
+
2434
+ if !excludes.include?('visibility')
2435
+ opts.on('--visibility VISIBILITY', String, "Visibility [private|public]") do |val|
2436
+ options[:visibility] = val
2437
+ end
2438
+ end
2439
+
2440
+ if !excludes.include?('tenants')
2441
+ opts.on('--tenants LIST', Array, "Tenant Access, comma separated list of account IDs") do |list|
2442
+ if list.size == 1 && list[0] == 'null' # hacky way to clear it
2443
+ options[:tenants] = []
2444
+ else
2445
+ options[:tenants] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
2446
+ end
2447
+ end
2448
+ end
2449
+ end
2450
+
2451
+ def prompt_permissions(options, excludes = [])
2452
+ all_groups = nil
2453
+ group_access = nil
2454
+ all_plans = nil
2455
+ plan_access = nil
2456
+ permissions = {}
2457
+
2458
+ # Group Access
2459
+ unless excludes.include?('groups')
2460
+ if !options[:groupAccessAll].nil?
2461
+ all_groups = options[:groupAccessAll]
2462
+ end
2463
+
2464
+ if !options[:groupAccessList].empty?
2465
+ group_access = options[:groupAccessList].collect {|site_id|
2466
+ found_group = find_group_by_name_or_id_for_provisioning(site_id)
2467
+ return 1, "group not found by #{site_id}" if found_group.nil?
2468
+ {'id' => found_group['id']}
2469
+ } || []
2470
+ elsif !options[:no_prompt] && !all_groups
2471
+ available_groups = options[:available_groups] || get_available_groups
2472
+
2473
+ if available_groups.empty?
2474
+ #print_red_alert "No available groups"
2475
+ #exit 1
2476
+ elsif available_groups.count > 1
2477
+ available_groups.unshift({"id" => '0', "name" => "All", "value" => "all"})
2478
+
2479
+ # default to all
2480
+ group_id = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'group', 'type' => 'select', 'fieldLabel' => 'Group Access', 'selectOptions' => available_groups, 'required' => false, 'description' => 'Add Group Access.'}], options[:options], @api_client, {})['group']
2481
+
2482
+ if !group_id.nil?
2483
+ if group_id == 'all'
2484
+ all_groups = true
2485
+ else
2486
+ group_access = (excludes.include?('groupDefaults') ? [{'id' => group_id}] : [{'id' => group_id, 'default' => MtnCloud::Cli::OptionTypes.confirm("Set '#{available_groups.find{|it| it['value'] == group_id}['name']}' as default?", {:default => false})}])
2487
+ end
2488
+ available_groups = available_groups.reject {|it| it['value'] == group_id}
2489
+
2490
+ while !group_id.nil? && !available_groups.empty? && MtnCloud::Cli::OptionTypes.confirm("Add another group access?", {:default => false})
2491
+ group_id = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'group', 'type' => 'select', 'fieldLabel' => 'Group Access', 'selectOptions' => available_groups, 'required' => false, 'description' => 'Add Group Access.'}], options[:options], @api_client, {})['group']
2492
+
2493
+ if !group_id.nil?
2494
+ if group_id == 'all'
2495
+ all_groups = true
2496
+ else
2497
+ group_access ||= []
2498
+ group_access << (excludes.include?('groupDefaults') ? {'id' => group_id} : {'id' => group_id, 'default' => MtnCloud::Cli::OptionTypes.confirm("Set '#{available_groups.find{|it| it['value'] == group_id}['name']}' as default?", {:default => false})})
2499
+ end
2500
+ available_groups = available_groups.reject {|it| it['value'] == group_id}
2501
+ end
2502
+ end
2503
+ end
2504
+ end
2505
+ end
2506
+ end
2507
+
2508
+ # Plan Access
2509
+ unless excludes.include?('plans')
2510
+ if !options[:planAccessAll].nil?
2511
+ all_plans = options[:planAccessAll]
2512
+ end
2513
+
2514
+ if !options[:planAccessList].empty?
2515
+ plan_access = options[:planAccessList].collect {|plan_id| {'id' => plan_id.to_i}}
2516
+ elsif !options[:no_prompt]
2517
+ available_plans = options[:available_plans] || get_available_plans
2518
+
2519
+ if available_plans.empty?
2520
+ #print_red_alert "No available plans"
2521
+ #exit 1
2522
+ elsif !available_plans.empty? && !options[:no_prompt]
2523
+ available_plans.unshift({"id" => '0', "name" => "All", "value" => "all"})
2524
+
2525
+ # default to all
2526
+ plan_id = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'plan', 'type' => 'select', 'fieldLabel' => 'Service Plan Access', 'selectOptions' => available_plans, 'required' => false, 'description' => 'Add Service Plan Access.'}], options[:options], @api_client, {})['plan']
2527
+
2528
+ if !plan_id.nil?
2529
+ if plan_id == 'all'
2530
+ all_plans = true
2531
+ else
2532
+ plan_access = [{'id' => plan_id, 'default' => MtnCloud::Cli::OptionTypes.confirm("Set '#{available_plans.find{|it| it['value'] == plan_id}['name']}' as default?", {:default => false})}]
2533
+ end
2534
+
2535
+ available_plans = available_plans.reject {|it| it['value'] == plan_id}
2536
+
2537
+ while !plan_id.nil? && !available_plans.empty? && MtnCloud::Cli::OptionTypes.confirm("Add another service plan access?", {:default => false})
2538
+ plan_id = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'plan', 'type' => 'select', 'fieldLabel' => 'Service Plan Access', 'selectOptions' => available_plans, 'required' => false, 'description' => 'Add Service Plan Access.'}], options[:options], @api_client, {})['plan']
2539
+
2540
+ if !plan_id.nil?
2541
+ if plan_id == 'all'
2542
+ all_plans = true
2543
+ else
2544
+ plan_access ||= []
2545
+ plan_access << {'id' => plan_id, 'default' => MtnCloud::Cli::OptionTypes.confirm("Set '#{available_plans.find{|it| it['value'] == plan_id}['name']}' as default?", {:default => false})}
2546
+ end
2547
+ available_plans = available_plans.reject {|it| it['value'] == plan_id}
2548
+ end
2549
+ end
2550
+ end
2551
+ end
2552
+ end
2553
+ end
2554
+
2555
+ unless excludes.include?('resource')
2556
+ resource_perms = {}
2557
+ resource_perms['all'] = all_groups if !all_groups.nil?
2558
+ resource_perms['sites'] = group_access if !group_access.nil?
2559
+ resource_perms['allPlans'] = all_plans if !all_plans.nil?
2560
+ resource_perms['plans'] = plan_access if !plan_access.nil?
2561
+ permissions['resourcePermissions'] = resource_perms
2562
+ end
2563
+
2564
+ available_accounts = get_available_accounts() #.collect {|it| {'name' => it['name'], 'value' => it['id']}}
2565
+ accounts = []
2566
+
2567
+ # Prompts for multi tenant
2568
+ if available_accounts.count > 1
2569
+ visibility = options[:visibility]
2570
+ unless excludes.include?('visibility')
2571
+ if !visibility && !options[:no_prompt]
2572
+ visibility = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'visibility', 'fieldLabel' => 'Tenant Permissions Visibility', 'type' => 'select', 'defaultValue' => 'private', 'required' => true, 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}]}], options[:options], @api_client, {})['visibility']
2573
+ end
2574
+ permissions['resourcePool'] = {'visibility' => visibility} if visibility
2575
+ end
2576
+
2577
+ # Tenants
2578
+ default_stores = []
2579
+ default_targets = []
2580
+ unless excludes.include?('tenants')
2581
+ if !options[:tenants].nil?
2582
+ accounts = options[:tenants].collect {|id| id.to_i}
2583
+ elsif !options[:no_prompt]
2584
+ account_id = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'account', 'type' => 'select', 'fieldLabel' => 'Add Tenant', 'selectOptions' => available_accounts, 'required' => false, 'description' => 'Add Tenant Permissions.'}], options[:options], @api_client, {})['account']
2585
+
2586
+ unless account_id.nil?
2587
+ accounts << account_id
2588
+ available_accounts = available_accounts.reject {|it| it['value'] == account_id}
2589
+
2590
+ # Prompt default store / target
2591
+ if options[:for_datastore]
2592
+ if MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'defaultTarget', 'type' => 'checkbox', 'fieldLabel' => 'Default', 'required' => false, 'description' => 'Designate as Default Store.', 'defaultValue' => false}], options[:options].merge({:checkbox_as_boolean => true}), @api_client, {})['defaultTarget']
2593
+ default_targets << account_id
2594
+ end
2595
+ if MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'defaultStore', 'type' => 'checkbox', 'fieldLabel' => 'Image Target', 'required' => false, 'description' => 'Designate as Image Target.', 'defaultValue' => false}], options[:options].merge({:checkbox_as_boolean => true}), @api_client, {})['defaultStore']
2596
+ default_stores << account_id
2597
+ end
2598
+ end
2599
+
2600
+ while !available_accounts.empty? && (account_id = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'account', 'type' => 'select', 'fieldLabel' => 'Add Another Tenant', 'selectOptions' => available_accounts, 'required' => false, 'description' => 'Add Tenant Permissions.'}], options[:options], @api_client, {})['account'])
2601
+ if !account_id.nil?
2602
+ accounts << account_id
2603
+ available_accounts = available_accounts.reject {|it| it['value'] == account_id}
2604
+
2605
+ # Prompt default store / target
2606
+ if options[:for_datastore]
2607
+ if MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'defaultTarget', 'type' => 'checkbox', 'fieldLabel' => 'Default', 'required' => false, 'description' => 'Designate as Default Store.', 'defaultValue' => false}], options[:options].merge({:checkbox_as_boolean => true}), @api_client, {})['defaultTarget']
2608
+ default_targets << account_id
2609
+ end
2610
+ if MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'defaultStore', 'type' => 'checkbox', 'fieldLabel' => 'Image Target', 'required' => false, 'description' => 'Designate as Image Target.', 'defaultValue' => false}], options[:options].merge({:checkbox_as_boolean => true}), @api_client, {})['defaultStore']
2611
+ default_stores << account_id
2612
+ end
2613
+ end
2614
+ end
2615
+ end
2616
+ end
2617
+ end
2618
+ if options[:for_datastore]
2619
+ permissions['tenantPermissions'] = accounts.collect {|it| {'id' => it, 'defaultStore' => default_stores.include?(it), 'defaultTarget' => default_targets.include?(it)}}
2620
+ else
2621
+ permissions['tenantPermissions'] = {'accounts' => accounts}
2622
+ end
2623
+ end
2624
+ end
2625
+ permissions
2626
+ end
2627
+
2628
+ def prompt_permissions_v2(options, excludes = [])
2629
+ perms = prompt_permissions(options, excludes)
2630
+ rtn = {}
2631
+ rtn['visibility'] = perms['resourcePool']['visibility'] if !perms['resourcePool'].nil? && !excludes.include?('visibility')
2632
+ rtn['tenants'] = ((perms['tenantPermissions'] || {})['accounts'] || []).collect {|it| {'id' => it}}
2633
+ rtn
2634
+ end
2635
+
2636
+ def print_permissions(permissions, excludes = [])
2637
+ if permissions.nil?
2638
+ print_h2 "Permissions"
2639
+ print yellow,"No permissions found.",reset,"\n"
2640
+ else
2641
+ if !permissions['resourcePermissions'].nil?
2642
+ if !excludes.include?('groups')
2643
+ print_h2 "Group Access"
2644
+
2645
+ if excludes.include?('groupDefaults')
2646
+ groups = []
2647
+ groups << 'All' if permissions['resourcePermissions']['all']
2648
+ groups += permissions['resourcePermissions']['sites'].collect {|it| it['name']} if permissions['resourcePermissions']['sites']
2649
+
2650
+ if groups.count > 0
2651
+ print cyan,"#{groups.join(', ')}".center(20)
2652
+ else
2653
+ print yellow,"No group access",reset,"\n"
2654
+ end
2655
+ print "\n"
2656
+ else
2657
+ rows = []
2658
+ if permissions['resourcePermissions']['all']
2659
+ rows.push({group: 'All'})
2660
+ end
2661
+ if permissions['resourcePermissions']['sites']
2662
+ permissions['resourcePermissions']['sites'].each do |site|
2663
+ rows.push({group: site['name'], default: site['default'] ? 'Yes' : ''})
2664
+ end
2665
+ end
2666
+ if rows.empty?
2667
+ print yellow,"No group access",reset,"\n"
2668
+ else
2669
+ columns = [:group, :default]
2670
+ print cyan
2671
+ print as_pretty_table(rows, columns)
2672
+ end
2673
+ end
2674
+ end
2675
+
2676
+ if !excludes.include?('plans')
2677
+ print_h2 "Plan Access"
2678
+ rows = []
2679
+ if permissions['resourcePermissions']['allPlans']
2680
+ rows.push({plan: 'All'})
2681
+ end
2682
+ if permissions['resourcePermissions']['plans']
2683
+ permissions['resourcePermissions']['plans'].each do |plan|
2684
+ rows.push({plan: plan['name'], default: plan['default'] ? 'Yes' : ''})
2685
+ end
2686
+ end
2687
+ if rows.empty?
2688
+ print yellow,"No plan access",reset,"\n"
2689
+ else
2690
+ columns = [:plan, :default]
2691
+ print cyan
2692
+ print as_pretty_table(rows, columns)
2693
+ end
2694
+ end
2695
+
2696
+ if !excludes.include?('tenants')
2697
+ if !permissions['tenantPermissions'].nil?
2698
+ print_h2 "Tenant Permissions"
2699
+ if !permissions['resourcePool'].nil?
2700
+ print cyan
2701
+ print "Visibility: #{permissions['resourcePool']['visibility'].to_s.capitalize}".center(20)
2702
+ print "\n"
2703
+ end
2704
+ if !permissions['tenantPermissions'].nil?
2705
+ print cyan
2706
+ print "Accounts: #{permissions['tenantPermissions']['accounts'].join(', ')}".center(20)
2707
+ print "\n"
2708
+ end
2709
+ end
2710
+ end
2711
+ print "\n"
2712
+ end
2713
+ end
2714
+ end
2715
+
2716
+
2717
+ ## Exposed Ports component
2718
+
2719
+ def load_balance_protocols_dropdown
2720
+ [
2721
+ {'name' => 'None', 'value' => ''},
2722
+ {'name' => 'HTTP', 'value' => 'HTTP'},
2723
+ {'name' => 'HTTPS', 'value' => 'HTTPS'},
2724
+ {'name' => 'TCP', 'value' => 'TCP'}
2725
+ ]
2726
+ end
2727
+
2728
+ # Prompts user for ports array
2729
+ # returns array of port objects
2730
+ def prompt_exposed_ports(options={}, api_client=nil, api_params={})
2731
+ #puts "Configure ports:"
2732
+ passed_ports = ((options[:options] && options[:options]["ports"]) ? options[:options]["ports"] : nil)
2733
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
2734
+ # skip prompting?
2735
+ if no_prompt
2736
+ return passed_ports
2737
+ end
2738
+ # value already given
2739
+ if passed_ports.is_a?(Array)
2740
+ return passed_ports
2741
+ end
2742
+
2743
+ # prompt for ports
2744
+ ports = []
2745
+ port_index = 0
2746
+ has_another_port = options[:options] && options[:options]["ports"]
2747
+ add_another_port = has_another_port || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add an exposed port?", {default:false}))
2748
+ while add_another_port do
2749
+ field_context = port_index == 0 ? "ports" : "ports#{port_index}"
2750
+
2751
+ port = {}
2752
+ port_label = port_index == 0 ? "Port" : "Port [#{port_index+1}]"
2753
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "#{port_label} Name", 'required' => false, 'description' => 'Choose a name for this port.', 'defaultValue' => port['name']}], options[:options])
2754
+ port['name'] = v_prompt[field_context]['name']
2755
+
2756
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'port', 'type' => 'number', 'fieldLabel' => "#{port_label} Number", 'required' => true, 'description' => 'A port number. eg. 8001', 'defaultValue' => (port['port'] ? port['port'].to_i : nil)}], options[:options])
2757
+ port['port'] = v_prompt[field_context]['port']
2758
+
2759
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'lb', 'type' => 'select', 'fieldLabel' => "#{port_label} LB", 'required' => false, 'selectOptions' => load_balance_protocols_dropdown, 'description' => 'Choose a load balance protocol.', 'defaultValue' => port['lb']}], options[:options])
2760
+ # port['loadBalanceProtocol'] = v_prompt[field_context]['lb']
2761
+ port['lb'] = v_prompt[field_context]['lb']
2762
+
2763
+ ports << port
2764
+ port_index += 1
2765
+ has_another_port = options[:options] && options[:options]["ports#{port_index}"]
2766
+ add_another_port = has_another_port || (!no_prompt && MtnCloud::Cli::OptionTypes.confirm("Add another exposed port?", {default:false}))
2767
+ end
2768
+
2769
+
2770
+ return ports
2771
+ end
2772
+
2773
+ def prompt_service_plan_options(plan_info, options={}, api_client=nil, api_params={}, instance=nil)
2774
+ plan_opts = {}
2775
+ # provisioning with blueprint can lock fields
2776
+ locked_fields = options[:locked_fields] || []
2777
+ if options[:options]['servicePlanOptions']
2778
+ plan_opts = options[:options]['servicePlanOptions']
2779
+ end
2780
+ default_max_cores = plan_info['maxCores'].to_i != 0 ? plan_info['maxCores'] : 1
2781
+ default_cores_per_socket = plan_info['coresPerSocket'].to_i != 0 ? plan_info['coresPerSocket'] : 1
2782
+ default_max_memory = plan_info['maxMemory'].to_i != 0 ? plan_info['maxMemory'] : nil
2783
+ # use defaults from the instance/server
2784
+ if instance
2785
+ default_max_cores = instance["maxCores"] if instance["maxCores"]
2786
+ default_cores_per_socket = instance["coresPerSocket"] if instance["coresPerSocket"]
2787
+ default_max_memory = instance["maxMemory"] if instance["maxMemory"]
2788
+ end
2789
+ # Core Count
2790
+ if plan_info["customCores"]
2791
+ if locked_fields.include?('servicePlanOptions.maxCores')
2792
+ if options[:options]['servicePlanOptions'] && options[:options]['servicePlanOptions']['maxCores']
2793
+ plan_opts['maxCores'] = options[:options]['servicePlanOptions']['maxCores'].to_i
2794
+ end
2795
+ else
2796
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => 'servicePlanOptions', 'fieldName' => 'maxCores', 'type' => 'number', 'fieldLabel' => "Core Count", 'required' => true, 'defaultValue' => default_max_cores, 'description' => "Customize service plan options Core Count"}], options[:options])
2797
+ if v_prompt['servicePlanOptions'] && v_prompt['servicePlanOptions']['maxCores']
2798
+ plan_opts['maxCores'] = v_prompt['servicePlanOptions']['maxCores'].to_i
2799
+ end
2800
+ end
2801
+ end
2802
+ # Cores Per Socket
2803
+ if plan_info["customCoresPerSocket"]
2804
+ if locked_fields.include?('servicePlanOptions.coresPerSocket')
2805
+ if options[:options]['servicePlanOptions'] && options[:options]['servicePlanOptions']['coresPerSocket']
2806
+ plan_opts['coresPerSocket'] = options[:options]['servicePlanOptions']['coresPerSocket'].to_i
2807
+ end
2808
+ else
2809
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldContext' => 'servicePlanOptions', 'fieldName' => 'coresPerSocket', 'type' => 'number', 'fieldLabel' => "Cores Per Socket", 'required' => true, 'defaultValue' => default_cores_per_socket, 'description' => "Customize service plan options Cores Per Socket"}], options[:options])
2810
+ if v_prompt['servicePlanOptions'] && v_prompt['servicePlanOptions']['coresPerSocket']
2811
+ plan_opts['coresPerSocket'] = v_prompt['servicePlanOptions']['coresPerSocket'].to_i
2812
+ end
2813
+ end
2814
+ end
2815
+ # Memory
2816
+ if plan_info["customMaxMemory"]
2817
+ if locked_fields.include?('servicePlanOptions.maxMemory')
2818
+ if options[:options]['servicePlanOptions'] && options[:options]['servicePlanOptions']['maxMemory']
2819
+ plan_opts['maxMemory'] = options[:options]['servicePlanOptions']['maxMemory'].to_i
2820
+ end
2821
+ else
2822
+ if options[:options]['servicePlanOptions'] && options[:options]['servicePlanOptions']['maxMemory']
2823
+ plan_opts['maxMemory'] = options[:options]['servicePlanOptions']['maxMemory'].to_i
2824
+ else
2825
+ # prompt for "memoryMB" field as MB or "memoryGB" in GB
2826
+ # always convert maxMemory to bytes
2827
+ if plan_info["memorySizeType"] == "MB" || options[:options]["memoryMB"]
2828
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'memoryMB', 'type' => 'text', 'fieldLabel' => "Memory (MB)", 'required' => true, 'defaultValue' => default_max_memory ? (default_max_memory / (1024 * 1024)) : nil, 'description' => "Customize service plan options Memory (MB). Value is in megabytes."}], options[:options])
2829
+ if v_prompt['memoryMB'].to_s != ""
2830
+ plan_opts['maxMemory'] = v_prompt['memoryMB'].to_i * 1024 * 1024
2831
+ end
2832
+ else
2833
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'memoryGB', 'type' => 'text', 'fieldLabel' => "Memory (GB)", 'required' => true, 'defaultValue' => default_max_memory ? (default_max_memory / (1024 * 1024 * 1024)) : nil, 'description' => "Customize service plan options Memory (GB). Value is in gigabytes."}], options[:options])
2834
+ if v_prompt['memoryGB'].to_s != ""
2835
+ plan_opts['maxMemory'] = v_prompt['memoryGB'].to_i * 1024 * 1024 * 1024
2836
+ end
2837
+ end
2838
+ # remove transient memory field just used for prompting for MB or GB
2839
+ plan_opts.delete("memoryMB")
2840
+ plan_opts.delete("memoryGB")
2841
+ end
2842
+ end
2843
+ end
2844
+ return plan_opts
2845
+ end
2846
+
2847
+ def format_instance_status(instance, return_color=cyan)
2848
+ out = ""
2849
+ status_string = instance['status'].to_s
2850
+ if status_string == 'running'
2851
+ out << "#{green}#{status_string.upcase}#{return_color}"
2852
+ elsif status_string == 'provisioning'
2853
+ out << "#{cyan}#{status_string.upcase}#{return_color}"
2854
+ elsif status_string == 'stopped' or status_string == 'failed'
2855
+ out << "#{red}#{status_string.upcase}#{return_color}"
2856
+ else
2857
+ out << "#{yellow}#{status_string.upcase}#{return_color}"
2858
+ end
2859
+ out
2860
+ end
2861
+
2862
+ def format_instance_connection_string(instance)
2863
+ if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
2864
+ connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
2865
+ end
2866
+ end
2867
+
2868
+ def format_app_status(app, return_color=cyan)
2869
+ out = ""
2870
+ status_string = app['status'] || app['appStatus'] || ''
2871
+ if status_string == 'running'
2872
+ out = "#{green}#{status_string.upcase}#{return_color}"
2873
+ elsif status_string == 'provisioning'
2874
+ out = "#{cyan}#{status_string.upcase}#{cyan}"
2875
+ elsif status_string == 'stopped' or status_string == 'failed'
2876
+ out = "#{red}#{status_string.upcase}#{return_color}"
2877
+ elsif status_string == 'unknown'
2878
+ out = "#{yellow}#{status_string.upcase}#{return_color}"
2879
+ elsif status_string == 'warning' && app['instanceCount'].to_i == 0
2880
+ # show this instead of WARNING
2881
+ out = "#{cyan}EMPTY#{return_color}"
2882
+ else
2883
+ out = "#{yellow}#{status_string.upcase}#{return_color}"
2884
+ end
2885
+ out
2886
+ end
2887
+
2888
+ def format_container_status(container, return_color=cyan)
2889
+ out = ""
2890
+ status_string = container['status'].to_s
2891
+ if status_string == 'running'
2892
+ out << "#{green}#{status_string.upcase}#{return_color}"
2893
+ elsif status_string == 'provisioning'
2894
+ out << "#{cyan}#{status_string.upcase}#{return_color}"
2895
+ elsif status_string == 'stopped' or status_string == 'failed'
2896
+ out << "#{red}#{status_string.upcase}#{return_color}"
2897
+ else
2898
+ out << "#{yellow}#{status_string.upcase}#{return_color}"
2899
+ end
2900
+ out
2901
+ end
2902
+
2903
+ def format_container_connection_string(container)
2904
+ if !container['ports'].nil? && container['ports'].empty? == false
2905
+ connection_string = "#{container['ip']}:#{container['ports'][0]['external']}"
2906
+ else
2907
+ # eh? more logic needed here i think, see taglib morph:containerLocationMenu
2908
+ connection_string = "#{container['ip']}"
2909
+ end
2910
+ end
2911
+
2912
+ def format_instance_container_display_name(instance, plural=false)
2913
+ #<span class="info-label">${[null,'docker'].contains(instance.layout?.provisionType?.code) ? 'Containers' : 'Virtual Machines'}:</span> <span class="info-value">${instance.containers?.size()}</span>
2914
+ v = plural ? "Containers" : "Container"
2915
+ if instance && instance['layout'] && instance['layout'].key?("provisionTypeCode")
2916
+ if [nil, 'docker'].include?(instance['layout']["provisionTypeCode"])
2917
+ v = plural ? "Virtual Machines" : "Virtual Machine"
2918
+ end
2919
+ end
2920
+ return v
2921
+ end
2922
+
2923
+ def format_blueprint_type(type_code)
2924
+ return type_code.to_s # just show it as is
2925
+ if type_code.to_s.empty?
2926
+ type_code = "morpheus"
2927
+ end
2928
+ if type_code.to_s.downcase == "arm"
2929
+ "ARM"
2930
+ else
2931
+ return type_code.to_s.capitalize
2932
+ end
2933
+ end
2934
+
2935
+ def parse_blueprint_type(type_code)
2936
+ return type_code.to_s # just use it as is
2937
+ # if type_code.to_s.empty?
2938
+ # type_code = "morpheus"
2939
+ # end
2940
+ if type_code.to_s.downcase == "arm"
2941
+ "arm"
2942
+ elsif type_code.to_s.downcase == "cloudformation"
2943
+ type_code = "cloudFormation"
2944
+ else
2945
+ return type_code.to_s.downcase
2946
+ end
2947
+ end
2948
+
2949
+ def format_snapshot_status(snapshot, return_color=cyan)
2950
+ out = ""
2951
+ status_string = snapshot['status'].to_s
2952
+ if status_string == 'complete'
2953
+ out << "#{green}#{status_string.upcase}#{return_color}"
2954
+ elsif status_string == 'creating'
2955
+ out << "#{cyan}#{status_string.upcase}#{return_color}"
2956
+ elsif status_string == 'failed'
2957
+ out << "#{red}#{status_string.upcase}#{return_color}"
2958
+ else
2959
+ out << "#{yellow}#{status_string.upcase}#{return_color}"
2960
+ end
2961
+ out
2962
+ end
2963
+ end