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,1631 @@
1
+ require 'uri'
2
+ require 'term/ansicolor'
3
+ require 'json'
4
+ require 'yaml'
5
+ require 'ostruct'
6
+ require 'io/console'
7
+ require 'mtncloud/logging'
8
+ require 'fileutils'
9
+ require 'filesize'
10
+
11
+ module MtnCloud::Cli::PrintHelper
12
+
13
+ def self.included(klass)
14
+ klass.send :include, Term::ANSIColor
15
+ end
16
+
17
+ def self.terminal_width
18
+ @@terminal_width ||= 80
19
+ end
20
+
21
+ def self.terminal_width=(v)
22
+ if v.nil? || v.to_i == 0
23
+ @@terminal_width = nil
24
+ else
25
+ @@terminal_width = v.to_i
26
+ end
27
+ @@terminal_width
28
+ end
29
+
30
+ # for consistancy, maybe..
31
+ unless defined?(ALL_LABELS_UPCASE)
32
+ ALL_LABELS_UPCASE = false
33
+ end
34
+
35
+ def current_terminal_width
36
+ return IO.console.winsize[1] rescue 0
37
+ end
38
+
39
+ # puts red message to stderr
40
+ # why this not stderr yet? use print_error or if respond_to?(:my_terminal)
41
+ def print_red_alert(msg)
42
+ $stderr.print "#{red}#{msg}#{reset}\n"
43
+ #print_error "#{red}#{msg}#{reset}\n"
44
+ #puts_error "#{red}#{msg}#{reset}"
45
+ end
46
+
47
+ # puts green message to stdout
48
+ def print_green_success(msg=nil)
49
+ if msg.nil?
50
+ msg = "success"
51
+ end
52
+ print "#{green}#{msg}#{reset}\n"
53
+ end
54
+
55
+ # print_h1 prints a header title and optional subtitles
56
+ # Output:
57
+ #
58
+ # title - subtitle1, subtitle2
59
+ # ==================
60
+ #
61
+ def print_h1(title, subtitles=nil, options=nil, suffix_content="\n")
62
+ # ok, support all these formats for now:
63
+ # print_h1(title, options={})
64
+ # print_h1(title, subtitles, options={})
65
+ # this can go away when we have a dirty @current_options
66
+
67
+
68
+ # auto include remote name in h1 titles
69
+ # eg. MTN Cloud Instances [dev]
70
+ # if title && @appliance_name
71
+ # title = "#{title} [#{@appliance_name}]"
72
+ # end
73
+
74
+ if subtitles.is_a?(Hash)
75
+ options = subtitles
76
+ subtitles = (options[:subtitles] || []).flatten
77
+ end
78
+ if subtitles.is_a?(String)
79
+ subtitles = [subtitles]
80
+ end
81
+ subtitles = (subtitles || []).flatten
82
+ options ||= {}
83
+ color = options[:color] || cyan
84
+ out = ""
85
+ out << "\n"
86
+ out << "#{color}#{bold}#{title}#{reset}"
87
+ if !subtitles.empty?
88
+ out << "#{color} | #{subtitles.join(', ')}#{reset}"
89
+ end
90
+ out << "\n"
91
+ if options[:border_style] == :thin
92
+
93
+ else
94
+ out << "#{color}#{bold}==================#{reset}\n"
95
+ end
96
+ # Default suffix_content is typically one extra newline
97
+ if !suffix_content.to_s.empty?
98
+ out << suffix_content.to_s
99
+ end
100
+ print out
101
+ end
102
+
103
+ def print_h2(title, subtitles=nil, options=nil, suffix_content="\n")
104
+ # ok, support all these formats for now:
105
+ # print_h2(title={})
106
+ # print_h2(title, options={})
107
+ # print_h2(title, subtitles, options={})
108
+ # this can go away when we have a dirty @current_options
109
+ if subtitles.is_a?(Hash)
110
+ options = subtitles
111
+ subtitles = (options[:subtitles] || []).flatten
112
+ end
113
+ subtitles = (subtitles || []).flatten
114
+ options ||= {}
115
+ color = options[:color] || cyan
116
+ out = ""
117
+ out << "\n"
118
+ out << "#{color}#{bold}#{title}#{reset}"
119
+ if !subtitles.empty?
120
+ out << "#{color} - #{subtitles.join(', ')}#{reset}"
121
+ end
122
+ out << "\n"
123
+ if options[:border_style] == :thin
124
+
125
+ else
126
+ out << "#{color}---------------------#{reset}\n"
127
+ end
128
+ # Default suffix_content is typically one extra newline
129
+ if !suffix_content.to_s.empty?
130
+ out << suffix_content.to_s
131
+ end
132
+ print out
133
+ end
134
+
135
+ def print_rest_exception(e, options={})
136
+ if respond_to?(:my_terminal)
137
+ MtnCloud::Cli::ErrorHandler.new(my_terminal.stderr).print_rest_exception(e, options)
138
+ else
139
+ MtnCloud::Cli::ErrorHandler.new.print_rest_exception(e, options)
140
+ end
141
+ end
142
+
143
+ def print_rest_errors(errors, options={})
144
+ if respond_to?(:my_terminal)
145
+ MtnCloud::Cli::ErrorHandler.new(my_terminal.stderr).print_rest_errors(errors, options)
146
+ else
147
+ MtnCloud::Cli::ErrorHandler.new.print_rest_errors(errors, options)
148
+ end
149
+ end
150
+
151
+ def parse_rest_exception(e, options={})
152
+ data = {}
153
+ begin
154
+ data = JSON.parse(e.response.to_s)
155
+ rescue
156
+ # MtnCloud::Logging::DarkPrinter.puts "Failed to parse error response as JSON: #{ex}" if MtnCloud::Logging.debug?
157
+ end
158
+ return data
159
+ end
160
+
161
+ def print_dry_run(api_request, options={})
162
+ # 2nd argument used to be command_string (String)
163
+ command_string = nil
164
+ if options.is_a?(String)
165
+ command_string = options
166
+ options = {}
167
+ end
168
+ options ||= {}
169
+ # api client injects common command options here
170
+ if api_request[:command_options]
171
+ options = options.merge(api_request[:command_options])
172
+ end
173
+ options ||= {}
174
+ # parse params request arguments
175
+ http_method = api_request[:method]
176
+ url = api_request[:url]
177
+ headers = api_request[:headers]
178
+ params = nil
179
+ if api_request[:params] && !api_request[:params].empty?
180
+ params = api_request[:params]
181
+ elsif headers && headers[:params]
182
+ # params inside headers for restclient reasons..
183
+ params = headers[:params]
184
+ elsif api_request[:query] && !api_request[:query].empty?
185
+ params = api_request[:query]
186
+ end
187
+ query_string = params
188
+ if query_string.respond_to?(:map)
189
+ query_string = URI.encode_www_form(query_string)
190
+ end
191
+ if query_string && !query_string.empty?
192
+ url = "#{url}?#{query_string}"
193
+ end
194
+ request_string = "#{http_method.to_s.upcase} #{url}".strip
195
+ payload = api_request[:payload] || api_request[:body]
196
+ #MtnCloud::Logging::DarkPrinter.puts "API payload is: (#{payload.class}) #{payload.inspect}"
197
+ content_type = (headers && headers['Content-Type']) ? headers['Content-Type'] : 'application/x-www-form-urlencoded'
198
+ # build output, either CURL or REQUEST
199
+ output = ""
200
+ if api_request[:curl] || options[:curl]
201
+ output = format_curl_command(http_method, url, headers, payload, options)
202
+ elsif options[:json]
203
+ # --dry --json should print the payload only
204
+ payload_object = payload.is_a?(String) ? JSON.parse(payload) : payload
205
+ output = as_json(payload_object, options)
206
+ elsif options[:yaml]
207
+ # --dry --yaml should print the payload only as yaml
208
+ payload_object = payload.is_a?(String) ? JSON.parse(payload) : payload
209
+ output = as_yaml(payload_object, options)
210
+ else
211
+ # default format is
212
+ # DRY RUN
213
+ # REQUEST
214
+ # GET https://server/api/things
215
+ #
216
+ # JSON
217
+ # {
218
+ # "thing": { ... }
219
+ # }
220
+ output = format_api_request(http_method, url, headers, payload, options)
221
+ end
222
+ # this is an extra scrub, should remove
223
+ if options[:scrub]
224
+ output = MtnCloud::Logging.scrub_message(output)
225
+ end
226
+ # write to a file?
227
+ if options[:outfile]
228
+ print_result = print_to_file(output, options[:outfile], options[:overwrite])
229
+ # with_stdout_to_file(options[:outfile], options[:overwrite]) { print output }
230
+ print "#{cyan}Wrote output to file #{options[:outfile]} (#{format_bytes(File.size(options[:outfile]))})\n" unless options[:quiet]
231
+ #return print_result
232
+ return
233
+ end
234
+ # print output
235
+ if api_request[:curl] || options[:curl]
236
+ print "\n"
237
+ print "#{cyan}#{bold}#{dark}CURL COMMAND#{reset}\n"
238
+ print output
239
+ print reset, "\n"
240
+ print reset
241
+ elsif options[:json] || options[:yaml]
242
+ # print just the just payload
243
+ print output, "\n"
244
+ else
245
+ print "\n"
246
+ print "#{cyan}#{bold}#{dark}REQUEST#{reset}\n"
247
+ print output
248
+ print reset, "\n"
249
+ print reset
250
+ end
251
+ return output
252
+ end
253
+
254
+ def print_system_command_dry_run(cmd, options={})
255
+ print "\n"
256
+ print "#{cyan}#{bold}#{dark}SYSTEM COMMAND#{reset}\n"
257
+ print cmd
258
+ print reset, "\n"
259
+ return
260
+ end
261
+
262
+ def format_api_request(http_method, url, headers, payload=nil, options={})
263
+ out = ""
264
+ # out << "\n"
265
+ # out << "#{cyan}#{bold}#{dark}REQUEST#{reset}\n"
266
+ request_string = "#{http_method.to_s.upcase} #{url}".strip
267
+ out << request_string + "\n"
268
+ out << cyan
269
+ if payload
270
+ out << "\n"
271
+ is_multipart = (payload.is_a?(Hash) && payload[:multipart] == true)
272
+ content_type = (headers && headers['Content-Type']) ? headers['Content-Type'] : (is_multipart ? 'multipart/form-data' : 'application/x-www-form-urlencoded')
273
+ if content_type == 'application/json'
274
+ if payload.is_a?(String)
275
+ begin
276
+ payload = JSON.parse(payload)
277
+ rescue => e
278
+ #payload = "(unparsable) #{payload}"
279
+ end
280
+ end
281
+ out << "#{cyan}#{bold}#{dark}JSON#{reset}\n"
282
+ if options[:scrub]
283
+ out << MtnCloud::Logging.scrub_message(JSON.pretty_generate(payload))
284
+ else
285
+ out << JSON.pretty_generate(payload)
286
+ end
287
+ else
288
+ out << "Content-Type: #{content_type}" + "\n"
289
+ out << reset
290
+ if payload.is_a?(File)
291
+ #pretty_size = "#{payload.size} B"
292
+ pretty_size = format_bytes(payload.size)
293
+ out << "File: #{payload.path} (#{pretty_size})"
294
+ elsif payload.is_a?(String)
295
+ if options[:scrub]
296
+ out << MtnCloud::Logging.scrub_message(payload)
297
+ else
298
+ out << payload
299
+ end
300
+ else
301
+ if content_type == 'application/x-www-form-urlencoded' || content_type.to_s.include?('multipart')
302
+ body_str = payload.to_s
303
+ begin
304
+ payload.delete(:multipart) if payload.is_a?(Hash)
305
+ # puts "grailsifying it!"
306
+ payload = MtnCloud::RestClient.grails_params(payload)
307
+ payload.each do |k,v|
308
+ if v.is_a?(File)
309
+ payload[k] = "@#{v.path}"
310
+ payload[k] = v.path
311
+ end
312
+ end
313
+ body_str = URI.encode_www_form(payload)
314
+ rescue => ex
315
+ raise ex
316
+ end
317
+ if options[:scrub]
318
+ out << MtnCloud::Logging.scrub_message(body_str)
319
+ else
320
+ out << body_str
321
+ end
322
+ else
323
+ if options[:scrub]
324
+ out << MtnCloud::Logging.scrub_message(payload)
325
+ else
326
+ out << payload.to_s
327
+ end
328
+ end
329
+ end
330
+ end
331
+ out << "\n"
332
+ end
333
+ # out << "\n"
334
+ out << reset
335
+ return out
336
+ end
337
+
338
+ # format_curl_command generates a valid curl command for the given api request
339
+ # @param api_request [Hash] api request, typically returned from api_client.dry.execute()
340
+ # @param options [Hash] common cli options
341
+ # formats command like:
342
+ #
343
+ # curl -XPOST "https://api.gomorpheus.com/api/cypher" \
344
+ # -H "Authorization: BEARER ******************" \
345
+ # -H "Content-Type: application/json" \
346
+ # -d '{
347
+ # "value": "mysecret"
348
+ # }'
349
+ def format_curl_command(http_method, url, headers, payload=nil, options={})
350
+ options ||= {}
351
+ # build curl [options]
352
+ out = ""
353
+ out << "curl -X#{http_method.to_s.upcase} \"#{url}\""
354
+ if headers
355
+ headers.each do |k,v|
356
+ # avoid weird [:headers][:params]
357
+ unless k == :params
358
+ header_value = v
359
+ out << ' \\' + "\n"
360
+ header_line = " -H \"#{k.is_a?(Symbol) ? k.to_s.capitalize : k.to_s}: #{v}\""
361
+ out << header_line
362
+ end
363
+ end
364
+ end
365
+ if payload #&& !payload.empty?
366
+ out << + ' \\' + "\n"
367
+ if headers && headers['Content-Type'] == 'application/json'
368
+ if payload.is_a?(String)
369
+ begin
370
+ payload = JSON.parse(payload)
371
+ rescue => e
372
+ #payload = "(unparsable) #{payload}"
373
+ end
374
+ end
375
+ if payload.is_a?(Hash)
376
+ out << " -d '#{as_json(payload, options)}'"
377
+ else
378
+ out << " -d '#{payload}'"
379
+ end
380
+ out << "\n"
381
+ else
382
+ is_multipart = (payload.is_a?(Hash) && payload[:multipart] == true)
383
+ content_type = headers['Content-Type'] || 'application/x-www-form-urlencoded'
384
+
385
+ if payload.is_a?(File)
386
+ # pretty_size = Filesize.from("#{payload.size} B").pretty.strip
387
+ pretty_size = "#{payload.size} B"
388
+ # print "File: #{payload.path} (#{payload.size} bytes)"
389
+ out << " --data-binary @#{payload.path}"
390
+ elsif payload.is_a?(String)
391
+ out << " -d '#{payload}'"
392
+ elsif payload.respond_to?(:map)
393
+ payload.delete(:multipart) if payload.is_a?(Hash)
394
+ # puts "grailsifying it!"
395
+ payload = MtnCloud::RestClient.grails_params(payload)
396
+ payload.each do |k,v|
397
+ if v.is_a?(File)
398
+ out << " -F '#{k}=@#{v.path}"
399
+ else
400
+ out << " -d '#{URI.encode_www_form({(k) => v})}'"
401
+ end
402
+ out << "\n"
403
+ end
404
+ #body_str = URI.encode_www_form(payload)
405
+ # out << " -d '#{body_str}'"
406
+ end
407
+ end
408
+ else
409
+ out << "\n"
410
+ end
411
+ if options[:scrub]
412
+ out = MtnCloud::Logging.scrub_message(out)
413
+ end
414
+ return out
415
+
416
+ end
417
+ def print_results_pagination(json_response, options={})
418
+ # print cyan,"\nViewing #{json_response['meta']['offset'].to_i + 1}-#{json_response['meta']['offset'].to_i + json_response['meta']['size'].to_i} of #{json_response['meta']['total']}\n", reset
419
+ print format_results_pagination(json_response, options)
420
+ end
421
+
422
+ def format_results_pagination(json_response, options={})
423
+ # no output for strange, empty data
424
+ if json_response.nil? || json_response.empty?
425
+ return ""
426
+ end
427
+ color = options.key?(:color) ? options[:color] : cyan
428
+ label = options[:label]
429
+ n_label = options[:n_label] || (label ? label.to_s.pluralize : nil)
430
+ message = options[:message] || "Viewing %{start_index}-%{end_index} of %{total} %{label}"
431
+ blank_message = options[:blank_message] || nil # "No %{label} found"
432
+
433
+ # support lazy passing of common list json_response {"meta": {"size": 25, "total": 56} }
434
+ # priority is:
435
+ # 1. "meta" that api response contains for list endpoints
436
+ # 2. "total" and "size" values if passed explicitely by the cli (pretty sure symbols are no longer used)
437
+ # 3. examine the first array found in the response
438
+ meta = nil
439
+ records = nil
440
+ # assume records is the first array in the response
441
+ records_key = json_response.keys.find { |k| json_response[k].is_a?(Array) }
442
+ if records_key
443
+ records = json_response[records_key]
444
+ meta = {'offset' => 0, 'size' => records.size, 'total' => records.size}
445
+ end
446
+ if json_response[:meta] || json_response["meta"]
447
+ meta = json_response[:meta] || json_response["meta"]
448
+ elsif json_response.key?('size') || json_response.key?('total')
449
+ meta = json_response
450
+ elsif json_response.key?(:size) || json_response.key?(:total)
451
+ meta = {'size' => json_response[:size], 'total' => json_response[:total], 'offset' => json_response[:offset]}
452
+ elsif records
453
+ # just use the first key in the response
454
+ meta = {'size' => records.size, 'total' => records.size}
455
+ end
456
+ # did not find pagination meta info?
457
+ if meta.nil?
458
+ return ""
459
+ end
460
+ # api should not need to return the size, just use records.size
461
+ if meta["size"].nil? && records
462
+ meta["size"] = records.size
463
+ end
464
+ offset = meta['offset'].to_i
465
+ size = meta['size'].to_i
466
+ total = meta['total'].to_i
467
+ # perhaps no total count returned, let total be equal to size of list
468
+ if total == 0 && size > 0
469
+ total = size
470
+ end
471
+ # plural label?
472
+ if total != 1
473
+ label = n_label
474
+ end
475
+ out_str = ""
476
+ string_key_values = {start_index: format_number(offset + 1), end_index: format_number(offset + size),
477
+ total: format_number(total), size: format_number(size), offset: format_number(offset), label: label}
478
+ if size > 0
479
+ if message
480
+ out_str << message % string_key_values
481
+ end
482
+ else
483
+ if blank_message
484
+ out_str << blank_message % string_key_values
485
+ else
486
+ #out << "No records"
487
+ end
488
+ end
489
+ out = ""
490
+ out << "\n"
491
+ out << color if color
492
+ out << out_str.strip
493
+ out << reset if color
494
+ out << "\n"
495
+ out
496
+ end
497
+
498
+ def required_blue_prompt
499
+ "#{cyan}|#{reset}"
500
+ end
501
+
502
+
503
+ # shows cyan, yellow, red progress bar where 50% looks like [||||| ]
504
+ # todo: render units used / available here too maybe
505
+ def generate_usage_bar(used_value, max_value, opts={})
506
+ opts[:bar_color] ||= :rainbow # :rainbow, :solid, or a color eg. cyan
507
+ max_bars = opts[:max_bars] || 50
508
+ out = ""
509
+ bars = []
510
+ percent = 0
511
+ percent_sigdig = opts[:percent_sigdig] || 2
512
+ if max_value.nil?
513
+ percent = 0
514
+ elsif max_value == 0
515
+ percent = used_value.to_f == 0 ? 0 : 100
516
+ else
517
+ percent = ((used_value.to_f / max_value.to_f) * 100)
518
+ end
519
+ unlimited_label = opts[:unlimited_label] || "n/a"
520
+ percent_label = (max_value.nil? ? unlimited_label : "#{percent.round(percent_sigdig)}%").rjust(6, ' ')
521
+ bar_display = ""
522
+ if percent > 100
523
+ max_bars.times { bars << "|" }
524
+ # percent = 100
525
+ else
526
+ n_bars = ((percent / 100.0) * max_bars).ceil
527
+ n_bars.times { bars << "|" }
528
+ end
529
+
530
+ if opts[:bar_color] == :rainbow
531
+ rainbow_bar = ""
532
+ cur_rainbow_color = reset # default terminal color
533
+ rainbow_bar << cur_rainbow_color
534
+ bars.each_with_index {|bar, i|
535
+ reached_percent = (i / max_bars.to_f) * 100
536
+ new_bar_color = cur_rainbow_color
537
+ if reached_percent > 80
538
+ new_bar_color = red
539
+ elsif reached_percent > 50
540
+ new_bar_color = yellow
541
+ elsif reached_percent > 10
542
+ new_bar_color = cyan
543
+ else
544
+ new_bar_color = reset
545
+ end
546
+ if cur_rainbow_color != new_bar_color
547
+ cur_rainbow_color = new_bar_color
548
+ rainbow_bar << cur_rainbow_color
549
+ end
550
+ rainbow_bar << bar
551
+ }
552
+ padding = max_bars - bars.size
553
+ if padding > 0
554
+ padding.times { rainbow_bar << " " }
555
+ #rainbow_bar << " " * padding
556
+ end
557
+ rainbow_bar << reset
558
+ bar_display = cyan + "[" + rainbow_bar + cyan + "]" + " #{cur_rainbow_color}#{percent_label}#{reset}"
559
+ out << bar_display
560
+ elsif opts[:bar_color] == :solid
561
+ bar_color = cyan
562
+ if percent > 80
563
+ bar_color = red
564
+ elsif percent > 50
565
+ bar_color = yellow
566
+ elsif percent > 10
567
+ bar_color = cyan
568
+ else
569
+ bar_color = reset
570
+ end
571
+ bar_display = cyan + "[" + bar_color + bars.join.ljust(max_bars, ' ') + cyan + "]" + " #{percent_label}" + reset
572
+ out << bar_display
573
+ else
574
+ bar_color = opts[:bar_color] || reset
575
+ bar_display = cyan + "[" + bar_color + bars.join.ljust(max_bars, ' ') + cyan + "]" + " #{percent_label}" + reset
576
+ out << bar_display
577
+ end
578
+ return out
579
+ end
580
+
581
+ def print_stats_usage(stats, opts={})
582
+ label_width = opts[:label_width] || 10
583
+ out = ""
584
+ if stats.nil? || stats.empty?
585
+ out << cyan + "No data." + "\n" + reset
586
+ print out
587
+ return
588
+ end
589
+ opts[:include] ||= [:cpu, :memory, :storage]
590
+ if opts[:include].include?(:max_cpu)
591
+ cpu_usage = stats['cpuUsagePeak']
592
+ out << cyan + "Max CPU".rjust(label_width, ' ') + ": " + generate_usage_bar(cpu_usage.to_f, 100) + "\n"
593
+ end
594
+ if opts[:include].include?(:avg_cpu)
595
+ cpu_usage = stats['cpuUsageAvg'] || stats['cpuUsageAverage']
596
+ out << cyan + "Avg. CPU".rjust(label_width, ' ') + ": " + generate_usage_bar(cpu_usage.to_f, 100) + "\n"
597
+ end
598
+ if opts[:include].include?(:cpu)
599
+ cpu_usage = stats['cpuUsage'] || stats['usedCpu']
600
+ out << cyan + "CPU".rjust(label_width, ' ') + ": " + generate_usage_bar(cpu_usage.to_f, 100) + "\n"
601
+ end
602
+ if opts[:include].include?(:memory)
603
+ out << cyan + "Memory".rjust(label_width, ' ') + ": " + generate_usage_bar(stats['usedMemory'], stats['maxMemory']) + cyan + Filesize.from("#{stats['usedMemory']} B").pretty.strip.rjust(15, ' ') + " / " + Filesize.from("#{stats['maxMemory']} B").pretty.strip.ljust(15, ' ') + "\n"
604
+ end
605
+ if opts[:include].include?(:storage)
606
+ out << cyan + "Storage".rjust(label_width, ' ') + ": " + generate_usage_bar(stats['usedStorage'], stats['maxStorage']) + cyan + Filesize.from("#{stats['usedStorage']} B").pretty.strip.rjust(15, ' ') + " / " + Filesize.from("#{stats['maxStorage']} B").pretty.strip.ljust(15, ' ') + "\n"
607
+ end
608
+ print out
609
+ end
610
+
611
+ def format_available_options(option_types)
612
+ option_lines = option_types.collect {|it| "\t-O #{it['fieldContext'] ? it['fieldContext'] + '.' : ''}#{it['fieldName']}=\"value\"" }.join("\n")
613
+ return "Available Options:\n#{option_lines}\n\n"
614
+ end
615
+
616
+ def format_dt_dd(label, value, label_width=10, justify="right", do_wrap=true)
617
+ # JD: uncomment next line to do away with justified labels
618
+ # label_width, justify = 0, "none"
619
+ out = ""
620
+ value = value.to_s
621
+ if do_wrap && value && value.include?(" ") && MtnCloud::Cli::PrintHelper.terminal_width
622
+ value_width = MtnCloud::Cli::PrintHelper.terminal_width - label_width
623
+ if value_width > 0 && value.gsub(/\e\[(\d+)m/, '').to_s.size > value_width
624
+ wrap_indent = label_width + 1
625
+ value = wrap(value, value_width, wrap_indent)
626
+ end
627
+ end
628
+ if justify == "right"
629
+ out << "#{label}:".rjust(label_width, ' ') + " #{value}"
630
+ elsif justify == "left"
631
+ out << "#{label}:".ljust(label_width, ' ') + " #{value}"
632
+ else
633
+ # default is none
634
+ out << "#{label}:" + " #{value}"
635
+ end
636
+ out
637
+ end
638
+
639
+ # truncate_string truncates a string and appends the suffix "..."
640
+ # @param value [String] the string to pad
641
+ # @param width [Integer] the length to truncate to
642
+ # @param suffix [String] the character to pad right side with. Default is '...'
643
+ def truncate_string(value, width, suffix="...")
644
+ value = value.to_s
645
+ if !width
646
+ return value
647
+ end
648
+ # JD: hack alerty.. this sux, but it's a best effort to preserve values containing ascii coloring codes
649
+ # it stops working when there are words separated by ascii codes, eg. two diff colors
650
+ # plus this is probably pretty slow...
651
+ uncolored_value = Term::ANSIColor.coloring? ? Term::ANSIColor.uncolored(value.to_s) : value.to_s
652
+ if uncolored_value != value
653
+ trimmed_value = nil
654
+ if uncolored_value.size > width
655
+ if suffix
656
+ trimmed_value = uncolored_value[0..width-(suffix.size+1)] + suffix
657
+ else
658
+ trimmed_value = uncolored_value[0..width-1]
659
+ end
660
+ return value.gsub(uncolored_value, trimmed_value)
661
+ else
662
+ return value
663
+ end
664
+ else
665
+ if value.size > width
666
+ if suffix
667
+ return value[0..width-(suffix.size+1)] + suffix
668
+ else
669
+ return value[0..width-1]
670
+ end
671
+ else
672
+ return value
673
+ end
674
+ end
675
+ end
676
+
677
+ # truncate_string truncates a string and appends the prefix "..."
678
+ # @param value [String] the string to pad
679
+ # @param width [Integer] the length to truncate to
680
+ # @param prefix [String] the character to pad left side with. Default is '...'
681
+ def truncate_string_right(value, width, prefix="...")
682
+ value = value.to_s
683
+ # JD: hack alerty.. this sux, but it's a best effort to preserve values containing ascii coloring codes
684
+ # it stops working when there are words separated by ascii codes, eg. two diff colors
685
+ # plus this is probably pretty slow...
686
+ uncolored_value = Term::ANSIColor.coloring? ? Term::ANSIColor.uncolored(value.to_s) : value.to_s
687
+ if uncolored_value != value
688
+ trimmed_value = nil
689
+ if uncolored_value.size > width
690
+ if prefix
691
+ trimmed_value = prefix + uncolored_value[(uncolored_value.size - width - prefix.size)..-1]
692
+ else
693
+ trimmed_value = uncolored_value[(uncolored_value.size - width)..-1]
694
+ end
695
+ return value.gsub(uncolored_value, trimmed_value)
696
+ else
697
+ return value
698
+ end
699
+ else
700
+ if value.size > width
701
+ if prefix
702
+ return prefix + value[(value.size - width - prefix.size)..-1]
703
+ else
704
+ return value[(value.size - width)..-1]
705
+ end
706
+ else
707
+ return value
708
+ end
709
+ end
710
+ end
711
+
712
+ # justified returns a left, center, or right aligned string.
713
+ # @param value [String] the string to pad
714
+ # @param width [Integer] the length to truncate to
715
+ # @param pad_char [String] the character to pad with. Default is ' '
716
+ # @return [String]
717
+ def justify_string(value, width, justify="left", pad_char=" ")
718
+ # JD: hack alert! this sux, but it's a best effort to preserve values containing ascii coloring codes
719
+ value = value.to_s
720
+ uncolored_value = Term::ANSIColor.coloring? ? Term::ANSIColor.uncolored(value.to_s) : value.to_s
721
+ if value.size != uncolored_value.size
722
+ width = width + (value.size - uncolored_value.size)
723
+ end
724
+ if justify == "right"
725
+ return "#{value}".rjust(width, pad_char)
726
+ elsif justify == "center"
727
+ return "#{value}".center(width, pad_char)
728
+ else
729
+ return "#{value}".ljust(width, pad_char)
730
+ end
731
+ end
732
+
733
+ def format_table_cell(value, width, justify="left", pad_char=" ", suffix="...")
734
+ #puts "format_table_cell(#{value}, #{width}, #{justify}, #{pad_char.inspect})"
735
+ cell = value.to_s
736
+ cell = truncate_string(cell, width, suffix)
737
+ cell = justify_string(cell, width, justify, pad_char)
738
+ cell
739
+ end
740
+
741
+ # as_pretty_table generates a table with aligned columns and truncated values.
742
+ # This can be used in place of TablePrint.tp()
743
+ # @param data [Array] A list of objects to extract the data from.
744
+ # @param columns - [Array of Objects] list of column definitions, A column definition can be a String, Symbol, or Hash
745
+ # @return [String]
746
+ # Usage: puts as_pretty_table(my_objects, [:id, :name])
747
+ # puts as_pretty_table(my_objects, ["id", "name", {"plan" => "plan.name" }], {color: white})
748
+ #
749
+ def as_pretty_table(data, columns, options={})
750
+ data = [data].flatten
751
+ if options[:reverse]
752
+ if data.is_a?(Array)
753
+ data.reverse!
754
+ elsif data.is_a?(Hash)
755
+ data.values.each {|v| v.reverse! if v.is_a?(Array) }
756
+ end
757
+ end
758
+ # support --fields x,y,z and --all-fields or --fields all
759
+ all_fields = data.first ? data.first.keys : []
760
+ #todo: support --raw-fields meh, not really needed..
761
+ # if options[:include_fields] && options[:raw_fields]
762
+ # data = transform_data_for_field_options(data, options)
763
+ # data = data[options[:include_fields_context]] if options[:include_fields_context]
764
+ # elsif options[:include_fields]
765
+ if options[:include_fields]
766
+ if (options[:include_fields].is_a?(Array) && options[:include_fields].size == 1 && options[:include_fields][0] == 'all') || options[:include_fields] == 'all'
767
+ columns = all_fields
768
+ else
769
+ # so let's use the passed in column definitions instead of the raw data properties
770
+ # columns = options[:include_fields]
771
+ new_columns = {}
772
+ options[:include_fields].each do |f|
773
+ matching_column = nil
774
+ # column definitions vary right now, array of symbols/strings/hashes or perhaps a single hash
775
+ if columns.is_a?(Array) && columns[0] && columns[0].is_a?(Hash)
776
+ matching_column = columns.find {|c|
777
+ if c.is_a?(Hash)
778
+ c.keys[0].to_s.downcase == f.to_s.downcase
779
+ else
780
+ c && c.to_s.downcase == f.to_s.downcase
781
+ end
782
+ }
783
+ elsif columns.is_a?(Hash)
784
+ matching_key = columns.keys.find {|k| k.to_s.downcase == f.to_s.downcase }
785
+ if matching_key
786
+ matching_column = columns[matching_key]
787
+ end
788
+ end
789
+ new_columns[f] = matching_column ? matching_column : f
790
+ end
791
+ columns = new_columns
792
+ end
793
+ elsif options[:all_fields]
794
+ columns = all_fields
795
+ else
796
+ columns = columns
797
+ end
798
+
799
+ columns = build_column_definitions(columns)
800
+
801
+ table_color = options[:color] || cyan
802
+ cell_delim = options[:delim] || " | "
803
+
804
+ header_row = []
805
+
806
+ columns.each do |column_def|
807
+ header_row << column_def.label
808
+ end
809
+
810
+ # generate rows matrix data for the specified columns
811
+ rows = []
812
+ data.each do |row_data|
813
+ row = []
814
+ columns.each do |column_def|
815
+ # r << column_def.display_method.respond_to?(:call) ? column_def.display_method.call(row_data) : get_object_value(row_data, column_def.display_method)
816
+ value = column_def.display_method.call(row_data)
817
+ value = JSON.fast_generate(value) if value.is_a?(Hash) || value.is_a?(Array)
818
+ row << value
819
+ end
820
+ rows << row
821
+ end
822
+
823
+ # all rows (pre-formatted)
824
+ data_matrix = [header_row] + rows
825
+
826
+ # determine column meta info i.e. width
827
+ columns.each_with_index do |column_def, column_index|
828
+ # column_def.meta = {
829
+ # max_value_size: (header_row + rows).max {|row| row[column_index] ? row[column_index].to_s.size : 0 }.size
830
+ # }
831
+ if column_def.fixed_width
832
+ column_def.width = column_def.fixed_width.to_i
833
+ else
834
+ max_value_size = 0
835
+ data_matrix.each do |row|
836
+ v = row[column_index].to_s
837
+ v_size = Term::ANSIColor.coloring? ? Term::ANSIColor.uncolored(v).size : v.size
838
+ if v_size > max_value_size
839
+ max_value_size = v_size
840
+ end
841
+ end
842
+
843
+ max_width = (column_def.max_width.to_i > 0) ? column_def.max_width.to_i : nil
844
+ min_width = (column_def.min_width.to_i > 0) ? column_def.min_width.to_i : nil
845
+ if min_width && max_value_size < min_width
846
+ column_def.width = min_width
847
+ elsif max_width && max_value_size > max_width
848
+ column_def.width = max_width
849
+ else
850
+ # expand / contract to size of the value by default
851
+ column_def.width = max_value_size
852
+ end
853
+ #puts "DEBUG: #{column_index} column_def.width: #{column_def.width}"
854
+ end
855
+ end
856
+
857
+ # responsive tables
858
+ # pops columns off end until they all fit on the terminal
859
+ # could use some options[:preferred_columns] logic here to throw away in some specified order
860
+ # --all fields disables this
861
+ trimmed_columns = []
862
+ if options[:wrap] != true # && options[:include_fields].nil? && options[:all_fields] != true
863
+
864
+ begin
865
+ term_width = current_terminal_width()
866
+ table_width = columns.inject(0) {|acc, column_def| acc + (column_def.width || 0) }
867
+ table_width += ((columns.size-0) * (3)) # col border width
868
+ if term_width && table_width
869
+ # leave 1 column always...
870
+ while table_width > term_width && columns.size > 1
871
+ column_index = columns.size - 1
872
+ removed_column = columns.pop
873
+ trimmed_columns << removed_column
874
+ if removed_column.width
875
+ table_width -= removed_column.width
876
+ table_width -= 3 # col border width
877
+ end
878
+
879
+ # clear from data_matrix
880
+ # wel, nvm it just gets regenerated
881
+
882
+ end
883
+ end
884
+ rescue => ex
885
+ MtnCloud::Logging::DarkPrinter.puts "Encountered error while applying responsive table sizing: (#{ex.class}) #{ex}"
886
+ end
887
+
888
+ if trimmed_columns.size > 0
889
+ # data_matrix = generate_table_data(data, columns, options)
890
+ header_row = []
891
+ columns.each do |column_def|
892
+ header_row << column_def.label
893
+ end
894
+ rows = []
895
+ data.each do |row_data|
896
+ row = []
897
+ columns.each do |column_def|
898
+ # r << column_def.display_method.respond_to?(:call) ? column_def.display_method.call(row_data) : get_object_value(row_data, column_def.display_method)
899
+ value = column_def.display_method.call(row_data)
900
+ value = JSON.fast_generate(value) if value.is_a?(Hash) || value.is_a?(Array)
901
+ row << value
902
+ end
903
+ rows << row
904
+ end
905
+ data_matrix = [header_row] + rows
906
+ end
907
+ end
908
+
909
+ # format header row
910
+ header_cells = []
911
+ columns.each_with_index do |column_def, column_index|
912
+ value = header_row[column_index] # column_def.label
913
+ header_cells << format_table_cell(value, column_def.width, column_def.justify)
914
+ end
915
+
916
+ # format header spacer row
917
+ if options[:border_style] == :thin
918
+ # a simpler looking table
919
+ cell_delim = " "
920
+ h_line = header_cells.collect {|cell| ("-" * cell.strip.size).ljust(cell.size, ' ') }.join(cell_delim)
921
+ else
922
+ # default border style
923
+ h_line = header_cells.collect {|cell| ("-" * cell.size) }.join(cell_delim.gsub(" ", "-"))
924
+ end
925
+
926
+ # format data rows
927
+ formatted_rows = []
928
+ rows.each_with_index do |row, row_index|
929
+ formatted_row = []
930
+ row.each_with_index do |value, column_index|
931
+ column_def = columns[column_index]
932
+ formatted_row << format_table_cell(value, column_def.width, column_def.justify)
933
+ end
934
+ formatted_rows << formatted_row
935
+ end
936
+
937
+
938
+
939
+ table_str = ""
940
+ table_str << header_cells.join(cell_delim) + "\n"
941
+ table_str << h_line + "\n"
942
+ formatted_rows.each do |row|
943
+ table_str << row.join(cell_delim) + "\n"
944
+ end
945
+
946
+ out = ""
947
+ out << table_color if table_color
948
+ out << table_str
949
+ out << reset if table_color
950
+ out
951
+ end
952
+
953
+
954
+ # as_description_list() prints a a two column table containing
955
+ # the name and value of a list of descriptions
956
+ # @param columns - [Hash or Array or Hashes] list of column definitions, A column defintion can be a String, Symbol, Hash or Proc
957
+ # @param obj [Object] an object to extract the data from, it is treated like a Hash.
958
+ # @param opts [Map] rendering options for label :justify, :wrap
959
+ # Usage:
960
+ # print_description_list([:id, :name, :status], my_instance, {})
961
+ #
962
+ def as_description_list(obj, columns, opts={})
963
+
964
+ columns = build_column_definitions(columns)
965
+
966
+ #label_width = opts[:label_width] || 10
967
+ max_label_width = 0
968
+ justify = opts.key?(:justify) ? opts[:justify] : "right"
969
+ do_wrap = opts.key?(:wrap) ? !!opts[:wrap] : true
970
+ color = opts.key?(:color) ? opts[:color] : cyan
971
+ rows = []
972
+
973
+ columns.flatten.each do |column_def|
974
+ label = column_def.label
975
+ label = label.upcase if ALL_LABELS_UPCASE
976
+ # value = get_object_value(obj, column_def.display_method)
977
+ value = column_def.display_method.call(obj)
978
+ value = JSON.fast_generate(value) if value.is_a?(Hash) || value.is_a?(Array)
979
+ if label.size > max_label_width
980
+ max_label_width = label.size
981
+ end
982
+ rows << {label: label, value: value}
983
+ end
984
+ label_width = max_label_width + 1 # for a leading space ' ' ..ew
985
+ value_width = nil
986
+ if MtnCloud::Cli::PrintHelper.terminal_width
987
+ value_width = MtnCloud::Cli::PrintHelper.terminal_width - label_width
988
+ end
989
+
990
+ out = ""
991
+ out << color if color
992
+ rows.each do |row|
993
+ value = row[:value].to_s
994
+ out << format_dt_dd(row[:label], value, label_width, justify, do_wrap) + "\n"
995
+ end
996
+ out << reset if color
997
+ return out
998
+ end
999
+
1000
+ # print_description_list() is an alias for `print generate_description_list()`
1001
+ def print_description_list(columns, obj, opts={})
1002
+ # raise "oh no.. replace with as_description_list()"
1003
+ print as_description_list(obj, columns, opts)
1004
+ end
1005
+
1006
+ def print_pretty_details(obj, opts={})
1007
+ print as_pretty_details(obj, opts)
1008
+ end
1009
+
1010
+ def as_pretty_details(obj, opts={})
1011
+ print as_pretty_details(obj, opts.merge({pretty: true}))
1012
+ end
1013
+
1014
+ def print_details(obj, opts={})
1015
+ print as_details(obj, opts)
1016
+ end
1017
+
1018
+ def as_details(obj, opts={})
1019
+ #return "" if obj.nil?
1020
+ columns = {}
1021
+ keys = obj.keys
1022
+ if opts[:sort]
1023
+ keys.sort!
1024
+ end
1025
+ keys.each do |key|
1026
+ display_proc = nil
1027
+ if opts[:column_format] && opts[:column_format].key?(key.to_sym)
1028
+ display_proc = opts[:column_format][key.to_sym]
1029
+ else
1030
+ display_proc = lambda {|it| format_detail_value(it[key]) }
1031
+ end
1032
+ label = opts[:pretty] ? key.to_s.titleize : key.to_s
1033
+ columns[label] = display_proc
1034
+ end
1035
+ as_description_list(obj, columns, opts)
1036
+ end
1037
+
1038
+ def format_detail_value(value)
1039
+ rtn = value
1040
+ if value.is_a?(Array)
1041
+ # show the first three objects
1042
+ rtn = value.first(3).collect { |row| format_abbreviated_value(row) }.join(", ")
1043
+ elsif value.is_a?(Hash)
1044
+ # show the first three properties
1045
+ keys = values.keys.select { |key| value[key].is_a?(Numeric) || value[key].is_a?(String) }.first(3)
1046
+ rtn = keys.collect { |key|
1047
+ "#{key.to_s.titleize}: #{format_abbreviated_value(value[key])}"
1048
+ }.join(", ")
1049
+ elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
1050
+ rtn = format_boolean(value)
1051
+ else
1052
+ rtn = value.to_s
1053
+ end
1054
+ return rtn
1055
+ end
1056
+
1057
+ def format_abbreviated_value(value)
1058
+ if value.is_a?(Hash)
1059
+ if value.keys.size == 0
1060
+ "{}"
1061
+ else
1062
+ # show the first three properties
1063
+ keys = value.keys.select { |key| value[key].is_a?(Numeric) || value[key].is_a?(String) }.first(3)
1064
+ keys.collect { |key|
1065
+ "#{key.to_s.titleize}: #{value[key]}"
1066
+ }.join(", ")
1067
+ end
1068
+ elsif value.is_a?(Array)
1069
+ if value.size == 0
1070
+ return "[]"
1071
+ elsif obj.size == 1
1072
+ "[(#{obj.size})]"
1073
+ else
1074
+ "[(#{obj.size})]"
1075
+ end
1076
+ elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
1077
+ format_boolean(value)
1078
+ else
1079
+ value.to_s
1080
+ end
1081
+ end
1082
+
1083
+ # build_column_definitions constructs an Array of column definitions (OpenStruct)
1084
+ # Each column is defined by a label (String), and a display_method (Proc)
1085
+ #
1086
+ # @columns [Array] list of definitions. A column definition can be a String, Symbol, Proc or Hash
1087
+ # @return [Array of OpenStruct] list of column definitions (OpenStruct) like:
1088
+ # [{label: "ID", display_method: 'id'}, {label: "Name", display_method: Proc}]
1089
+ # Usage:
1090
+ # build_column_definitions(:id, :name)
1091
+ # build_column_definitions({"Object Id" => 'id'}, :name)
1092
+ # build_column_definitions({"ID" => 'id'}, "name", "plan.name", {status: lambda {|data| data['status'].upcase } })
1093
+ #
1094
+ def build_column_definitions(*columns)
1095
+ # allow passing a single hash instead of an array of hashes
1096
+ if columns.size == 1 && columns[0].is_a?(Hash)
1097
+ columns = columns[0].collect {|k,v| {(k) => v} }
1098
+ else
1099
+ columns = columns.flatten.compact
1100
+ end
1101
+ results = []
1102
+ columns.each do |col|
1103
+ # determine label
1104
+ if col.is_a?(String)
1105
+ # supports "field as Label"
1106
+ field_key, field_label = col.split(/\s+as\s+/)
1107
+ if field_key && field_label
1108
+ k = field_label.strip
1109
+ v = field_key.strip
1110
+ else
1111
+ k = col.strip
1112
+ v = col.strip
1113
+ end
1114
+ build_column_definitions([{(k) => v}]).each do |r|
1115
+ results << r if r
1116
+ end
1117
+ elsif col.is_a?(Symbol)
1118
+ k = col.to_s.upcase #.capitalize
1119
+ v = col.to_s
1120
+ build_column_definitions([{(k) => v}]).each do |r|
1121
+ results << r if r
1122
+ end
1123
+ elsif col.is_a?(Hash)
1124
+ column_def = OpenStruct.new
1125
+ k, v = col.keys[0], col.values[0]
1126
+ if k.is_a?(String)
1127
+ column_def.label = k
1128
+ elsif k.is_a?(Symbol)
1129
+ column_def.label = k
1130
+ else
1131
+ column_def.label = k.to_s
1132
+ # raise "invalid column definition label (#{k.class}) #{k.inspect}. Should be a String or Symbol."
1133
+ end
1134
+
1135
+ # determine display_method
1136
+ if v.is_a?(String)
1137
+ column_def.display_method = lambda {|data| get_object_value(data, v) }
1138
+ elsif v.is_a?(Symbol)
1139
+ column_def.display_method = lambda {|data| get_object_value(data, v) }
1140
+ elsif v.is_a?(Proc)
1141
+ column_def.display_method = v
1142
+ elsif v.is_a?(Hash) || v.is_a?(OpenStruct)
1143
+ if v[:display_name] || v[:label]
1144
+ column_def.label = v[:display_name] || v[:label]
1145
+ end
1146
+ if v[:display_method]
1147
+ if v[:display_method].is_a?(Proc)
1148
+ column_def.display_method = v[:display_method]
1149
+ else
1150
+ # assume v[:display_method] is a String, Symbol
1151
+ column_def.display_method = lambda {|data| get_object_value(data, v[:display_method]) }
1152
+ end
1153
+ else
1154
+ # the default behavior is to use the key (undoctored) to find the data
1155
+ # column_def.display_method = k
1156
+ column_def.display_method = lambda {|data| get_object_value(data, k) }
1157
+ end
1158
+
1159
+ # other column rendering options
1160
+ column_def.justify = v[:justify]
1161
+ if v[:max_width]
1162
+ column_def.max_width = v[:max_width]
1163
+ end
1164
+ if v[:min_width]
1165
+ column_def.min_width = v[:min_width]
1166
+ end
1167
+ # tp uses width to behave like max_width, but tp() is gone, remove this?
1168
+ if v[:width]
1169
+ column_def.width = v[:width]
1170
+ column_def.max_width = v[:width]
1171
+ end
1172
+ column_def.wrap = v[:wrap].nil? ? true : v[:wrap] # only utlized in as_description_list() right now
1173
+
1174
+ else
1175
+ raise "invalid column definition value (#{v.class}) #{v.inspect}. Should be a String, Symbol, Proc or Hash"
1176
+ end
1177
+
1178
+ # only upcase label for symbols, this is silly anyway,
1179
+ # just pass the exact label (key) that you want printed..
1180
+ if column_def.label.is_a?(Symbol)
1181
+ column_def.label = column_def.label.to_s.upcase
1182
+ end
1183
+
1184
+ results << column_def
1185
+
1186
+ else
1187
+ raise "invalid column definition (#{column_def.class}) #{column_def.inspect}. Should be a String, Symbol or Hash"
1188
+ end
1189
+
1190
+ end
1191
+
1192
+ return results
1193
+ end
1194
+
1195
+ def wrap(s, width, indent=0)
1196
+ out = s
1197
+ if s.size > width
1198
+ if indent > 0
1199
+ out = s.gsub(/(.{1,#{width}})(\s+|\Z)/, "#{' ' * indent}\\1\n").strip
1200
+ else
1201
+ out = s.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n")
1202
+ end
1203
+ else
1204
+ return s
1205
+ end
1206
+ end
1207
+
1208
+ def format_boolean(v)
1209
+ if v == true || v == "true" || v == "on"
1210
+ "Yes"
1211
+ else
1212
+ "No"
1213
+ end
1214
+ end
1215
+
1216
+ def quote_csv_value(v)
1217
+ '"' + v.to_s.gsub('"', '""') + '"'
1218
+ end
1219
+
1220
+ def as_csv(data, default_columns=nil, options={}, object_key=nil)
1221
+ out = ""
1222
+ delim = options[:csv_delim] || options[:delim] || ","
1223
+ newline = options[:csv_newline] || options[:newline] || "\n"
1224
+ include_header = options[:csv_no_header] ? false : true
1225
+ do_quotes = options[:csv_quotes] || options[:quotes]
1226
+ if options[:reverse]
1227
+ if data.is_a?(Array)
1228
+ data.reverse!
1229
+ elsif data.is_a?(Hash)
1230
+ data.values.each {|v| v.reverse! if v.is_a?(Array) }
1231
+ end
1232
+ end
1233
+ if options[:include_fields]
1234
+ data = transform_data_for_field_options(data, options, object_key)
1235
+ if data.is_a?(Hash)
1236
+ if options[:raw_fields]
1237
+ if options[:include_fields_context]
1238
+ data = data[options[:include_fields_context]]
1239
+ else
1240
+ # todo: could use a dynamic object_key, first Array or Hash in the data, can probably always do this...
1241
+ # if object_key.nil?
1242
+ # object_key = data.keys.find {|k| data[k].is_a?(Array) || data[k].is_a?(Hash) }
1243
+ # end
1244
+ # if object_key && data[object_key]
1245
+ # data = data[object_key]
1246
+ # end
1247
+ end
1248
+ else
1249
+ if object_key
1250
+ data = data[object_key]
1251
+ end
1252
+ end
1253
+ end
1254
+ else
1255
+ # need array of records, so always select the object/array here
1256
+ if object_key
1257
+ if data.is_a?(Hash)
1258
+ data = data[object_key]
1259
+ else
1260
+ MtnCloud::Logging::DarkPrinter.puts "as_csv() expects data as an to fetch object key '#{object_key}' from, #{data.class}." if MtnCloud::Logging.debug?
1261
+ end
1262
+ end
1263
+ end
1264
+
1265
+
1266
+ records = data
1267
+
1268
+ # allow records as Array and Hash only..
1269
+ if records.is_a?(Array)
1270
+ # records = records
1271
+ elsif records.is_a?(Hash)
1272
+ records = [records]
1273
+ else
1274
+ #raise "records_as_csv expects records as an Array of objects to render"
1275
+ MtnCloud::Logging::DarkPrinter.puts "as_csv() expects data as an Array of objects to render, got a #{records.class} instead" if MtnCloud::Logging.debug?
1276
+ # return ""
1277
+ return out
1278
+ end
1279
+
1280
+ # build column definitions, by default use all properties for the first record (Hash) in the array
1281
+ columns = []
1282
+ all_fields = records.first.is_a?(Hash) ? records.first.keys : []
1283
+ if options[:include_fields]
1284
+ if (options[:include_fields].is_a?(Array) && options[:include_fields].size == 1 && options[:include_fields][0] == 'all') || options[:include_fields] == 'all'
1285
+ columns = all_fields
1286
+ else
1287
+ columns = options[:include_fields]
1288
+ end
1289
+ elsif options[:all_fields]
1290
+ columns = all_fields
1291
+ elsif default_columns
1292
+ columns = default_columns
1293
+ else
1294
+ columns = all_fields
1295
+ end
1296
+
1297
+ column_defs = build_column_definitions(columns)
1298
+
1299
+ if include_header
1300
+ headers = column_defs.collect {|column_def| column_def.label }
1301
+ if do_quotes
1302
+ headers = headers.collect {|it| quote_csv_value(it) }
1303
+ end
1304
+ out << headers.join(delim)
1305
+ out << newline
1306
+ end
1307
+ lines = []
1308
+ records.each do |obj|
1309
+ if obj
1310
+ cells = []
1311
+ column_defs.each do |column_def|
1312
+ label = column_def.label
1313
+ value = column_def.display_method.call(obj)
1314
+ value = value.is_a?(String) ? value : JSON.fast_generate(value)
1315
+ # value = get_object_value(obj, column_def)
1316
+ if do_quotes
1317
+ cells << quote_csv_value(value)
1318
+ else
1319
+ cells << value.to_s
1320
+ end
1321
+ end
1322
+ end
1323
+ line = cells.join(delim)
1324
+ lines << line
1325
+ end
1326
+ out << lines.join(newline)
1327
+ #out << delim
1328
+ out
1329
+
1330
+ end
1331
+
1332
+ # deprecated, replaced by as_csv(records, columns, options, object_key)
1333
+ def records_as_csv(records, options={}, default_columns=nil)
1334
+ as_csv(records, default_columns, options)
1335
+ end
1336
+
1337
+ def as_json(data, options={}, object_key=nil)
1338
+ out = ""
1339
+ if !data
1340
+ return "null" # "No data"
1341
+ end
1342
+ if options[:reverse]
1343
+ if data.is_a?(Array)
1344
+ data.reverse!
1345
+ elsif data.is_a?(Hash)
1346
+ data.values.each {|v| v.reverse! if v.is_a?(Array) }
1347
+ end
1348
+ end
1349
+ if options[:include_fields]
1350
+ data = transform_data_for_field_options(data, options, object_key)
1351
+ end
1352
+
1353
+ do_pretty = options.key?(:pretty_json) ? options[:pretty_json] : true
1354
+ if do_pretty
1355
+ out << JSON.pretty_generate(data)
1356
+ else
1357
+ out << JSON.fast_generate(data)
1358
+ end
1359
+ #out << "\n"
1360
+ out
1361
+ end
1362
+
1363
+ def as_yaml(data, options={}, object_key=nil)
1364
+ out = ""
1365
+ if !data
1366
+ return "null" # "No data"
1367
+ end
1368
+ if options[:reverse]
1369
+ if data.is_a?(Array)
1370
+ data.reverse!
1371
+ elsif data.is_a?(Hash)
1372
+ data.values.each {|v| v.reverse! if v.is_a?(Array) }
1373
+ end
1374
+ end
1375
+ if options[:include_fields]
1376
+ data = transform_data_for_field_options(data, options, object_key)
1377
+ end
1378
+ begin
1379
+ out << data.to_yaml
1380
+ rescue => err
1381
+ puts "failed to render YAML from data: #{data.inspect}"
1382
+ puts err.message
1383
+ end
1384
+ #out << "\n"
1385
+ out
1386
+ end
1387
+
1388
+
1389
+ # transform data for options --fields id,authority and --select id,authority
1390
+ # support traversing records with --raw-fields and the list command. Example: roles list --raw-fields roles.id,roles.authority
1391
+ def transform_data_for_field_options(data, options, object_key=nil)
1392
+ if options[:include_fields] && data.is_a?(Hash)
1393
+ if options[:raw_fields]
1394
+ row = (object_key && !options[:raw_fields]) ? data[object_key] : data
1395
+ records = [row].flatten()
1396
+ # look for an array in the first field only now...
1397
+ field_parts = options[:include_fields][0].to_s.split(".")
1398
+ field_context = field_parts[0]
1399
+ context_data = data[field_context]
1400
+ if field_parts.size > 1 && context_data.is_a?(Array)
1401
+ # inject all the root level properties to be selectable too..
1402
+ context_data = data.delete(field_context)
1403
+ # records = context_data
1404
+ records = context_data.collect {|it| it.is_a?(Hash) ? data.merge(it) : data }
1405
+ # hacky modifying options in place
1406
+ options[:include_fields_context] = field_context
1407
+ options[:include_fields] = options[:include_fields].collect {|it| it.sub(field_context+'.', '')}
1408
+ # data = filter_data(records, options[:include_fields])
1409
+ # data[field_context] = filter_data(records, options[:include_fields])
1410
+ data = {(field_context) => filter_data(records, options[:include_fields])}
1411
+ else
1412
+ data = filter_data(data, options[:include_fields])
1413
+ end
1414
+ else
1415
+ # By default, fields are relative to the object_key, so you can use -F id instead of requiring -F instance.id
1416
+ # So ironically it is the 'raw' options (:raw_fields == true) that has to do all this funny stuff to filter intuitively.
1417
+ if object_key
1418
+ # this removes everything but the object, makes sense when using --fields
1419
+ data = {(object_key) => filter_data(data[object_key], options[:include_fields])}
1420
+ # this preserves other fields eg. meta...
1421
+ # data[object_key] = filter_data(data[object_key], options[:include_fields])
1422
+ else
1423
+ data = filter_data(data, options[:include_fields])
1424
+ end
1425
+ end
1426
+ end
1427
+ return data
1428
+ end
1429
+
1430
+ def sleep_with_dots(sleep_seconds, dots=3, dot_chr=".")
1431
+ dot_interval = (sleep_seconds.to_f / dots.to_i)
1432
+ dots.to_i.times do |dot_index|
1433
+ sleep dot_interval
1434
+ print dot_chr
1435
+ end
1436
+ end
1437
+
1438
+ def print_to_file(txt, filename, overwrite=false, access_mode = 'w+')
1439
+ MtnCloud::Logging::DarkPrinter.puts "Writing #{txt.to_s.bytesize} bytes to file #{filename}" if MtnCloud::Logging.debug?
1440
+ outfile = nil
1441
+ begin
1442
+ full_filename = File.expand_path(filename)
1443
+ if File.exist?(full_filename)
1444
+ if !overwrite
1445
+ print "#{red}Output file '#{filename}' already exists.#{reset}\n"
1446
+ print "#{red}Use --overwrite to overwrite the existing file.#{reset}\n"
1447
+ return 1
1448
+ end
1449
+ end
1450
+ if Dir.exist?(full_filename)
1451
+ print "#{red}Output file '#{filename}' is invalid. It is the name of an existing directory.#{reset}\n"
1452
+ return 1
1453
+ end
1454
+ target_dir = File.dirname(full_filename)
1455
+ if !Dir.exist?(target_dir)
1456
+ FileUtils.mkdir_p(target_dir)
1457
+ end
1458
+ outfile = File.open(full_filename, access_mode)
1459
+ outfile.print(txt)
1460
+ return 0
1461
+ rescue => ex
1462
+ # puts_error "Error writing to outfile '#{filename}'. Error: #{ex}"
1463
+ print "#{red}Error writing to file '#{filename}'. Error: #{ex}#{reset}\n"
1464
+ return 1
1465
+ ensure
1466
+ outfile.close if outfile
1467
+ end
1468
+ end
1469
+
1470
+ def with_stdout_to_file(filename, overwrite=false, access_mode = 'w+', &block)
1471
+ MtnCloud::Logging::DarkPrinter.puts "Writing output to file #{filename}" if MtnCloud::Logging.debug?
1472
+ previous_stdout = my_terminal.stdout
1473
+ outfile = nil
1474
+ begin
1475
+ full_filename = File.expand_path(filename)
1476
+ if File.exist?(full_filename)
1477
+ if !overwrite
1478
+ print "#{red}Output file '#{filename}' already exists.#{reset}\n"
1479
+ print "#{red}Use --overwrite to overwrite the existing file.#{reset}\n"
1480
+ return 1
1481
+ end
1482
+ end
1483
+ if Dir.exist?(full_filename)
1484
+ print "#{red}Output file '#{filename}' is invalid. It is the name of an existing directory.#{reset}\n"
1485
+ return 1
1486
+ end
1487
+ target_dir = File.dirname(full_filename)
1488
+ if !Dir.exist?(target_dir)
1489
+ FileUtils.mkdir_p(target_dir)
1490
+ end
1491
+ outfile = File.open(full_filename, access_mode)
1492
+ # outfile.print(txt)
1493
+ # ok just redirect stdout to the file
1494
+ my_terminal.set_stdout(outfile)
1495
+ result = yield
1496
+ outfile.close if outfile
1497
+ my_terminal.set_stdout(previous_stdout)
1498
+ my_terminal.stdout.flush if my_terminal.stdout
1499
+ # this does not work here.. i dunno why yet, it works in ensure though...
1500
+ # print "#{cyan}Wrote #{File.size(full_filename)} bytes to file #{filename}\n"
1501
+ if result
1502
+ return result
1503
+ else
1504
+ return 0
1505
+ end
1506
+ rescue => ex
1507
+ # puts_error "Error writing to outfile '#{filename}'. Error: #{ex}"
1508
+ print_error "#{red}Error writing to file '#{filename}'. Error: #{ex}#{reset}\n"
1509
+ return 1
1510
+ ensure
1511
+ outfile.close if outfile
1512
+ my_terminal.set_stdout(previous_stdout) if previous_stdout != my_terminal.stdout
1513
+ end
1514
+ end
1515
+
1516
+ def format_percent(val, sig_dig=2, hide_zero=false)
1517
+ if val.nil?
1518
+ return ""
1519
+ end
1520
+ percent_value = val.to_f
1521
+ if percent_value == 0
1522
+ if hide_zero
1523
+ ""
1524
+ else
1525
+ return "0%"
1526
+ end
1527
+ else
1528
+ return percent_value.round(sig_dig).to_s + "%"
1529
+ end
1530
+ end
1531
+
1532
+ # returns 0.50 / s ie {{value}} / {{unit}}
1533
+ def format_rate(amount, unit='s', sig_dig=2)
1534
+ if amount.to_f == 0
1535
+ return "0.00" + " / " + unit.to_s
1536
+ else
1537
+ rtn = amount.to_f.round(2).to_s
1538
+ parts = rtn.split('.')
1539
+ # number_str = format_number(parts[0])
1540
+ number_str = parts[0].to_s
1541
+ decimal_str = "#{parts[1]}".ljust(sig_dig, "0")
1542
+ number_str + "." + decimal_str
1543
+ return number_str + "." + decimal_str + " / " + unit.to_s
1544
+ end
1545
+ end
1546
+
1547
+ # convert JSON or YAML string to a map
1548
+ def parse_json_or_yaml(config, parsers = [:json, :yaml])
1549
+ rtn = {success: false, data: nil, error: nil}
1550
+ error = nil
1551
+ config = config.strip
1552
+ if config[0..2] == "---"
1553
+ parsers = [:yaml]
1554
+ end
1555
+ # ok only parse json for strings that start with {, consolidated yaml can look like json and cause issues}
1556
+ if config[0] && config[0].chr == "{" && config[-1] && config[-1].chr == "}"
1557
+ parsers = [:json]
1558
+ end
1559
+ parsers.each do |parser|
1560
+ if parser == :yaml
1561
+ begin
1562
+ # todo: one method to parse and return Hash
1563
+ # load does not raise an exception, it just returns the bad string
1564
+ #YAML.parse(config)
1565
+ config_map = YAML.load(config)
1566
+ if !config_map.is_a?(Hash)
1567
+ raise "Failed to parse config as YAML"
1568
+ end
1569
+ rtn[:data] = config_map
1570
+ rtn[:success] = true
1571
+ break
1572
+ rescue => ex
1573
+ rtn[:error] = ex if rtn[:error].nil?
1574
+ end
1575
+ elsif parser == :json
1576
+ begin
1577
+ config_map = JSON.parse(config)
1578
+ rtn[:data] = config_map
1579
+ rtn[:success] = true
1580
+ break
1581
+ rescue => ex
1582
+ rtn[:error] = ex if rtn[:error].nil?
1583
+ end
1584
+ end
1585
+ end
1586
+ return rtn
1587
+ end
1588
+
1589
+ def parse_yaml_or_json(config, parsers = [:yaml, :json])
1590
+ parse_json_or_yaml(config, parsers)
1591
+ end
1592
+
1593
+ def format_option_types_table(option_types, options={}, domain_name=nil)
1594
+ show_option_source = option_types.find {|it| !it['optionList'].to_s.empty? || !it['optionSource'].to_s.empty? }
1595
+ show_default = option_types.find {|it| !it['defaultValue'].nil? }
1596
+ columns = [
1597
+ {"FIELD LABEL" => lambda {|it| it['fieldLabel'] } },
1598
+ {"FIELD NAME" => lambda {|it|
1599
+ if it['fieldContext'] && it['fieldContext'] != domain_name && it['fieldContext'] != 'domain'
1600
+ "#{it['fieldContext']}.#{it['fieldName']}"
1601
+ else
1602
+ "#{it['fieldName']}"
1603
+ end
1604
+ } },
1605
+ {"TYPE" => lambda {|it| it['type'] } },
1606
+ {"REQUIRED" => lambda {|it| format_boolean it['required'] } },
1607
+ show_option_source ? {"OPTION SOURCE" => lambda {|it|
1608
+ if it['optionList']
1609
+ it['optionList']['name']
1610
+ elsif it['optionSourceType']
1611
+ "#{it['optionSourceType']}/#{it['optionSource']}"
1612
+ else
1613
+ "#{it['optionSource']}"
1614
+ end
1615
+ } } : nil,
1616
+ show_default ? {"DEFAULT" => lambda {|it| it['defaultValue'] } } : nil,
1617
+ ].compact
1618
+ as_pretty_table(option_types, columns, options)
1619
+ end
1620
+
1621
+ def format_simple_option_types_table(option_types, options={})
1622
+ as_pretty_table(option_types, {
1623
+ "LABEL" => lambda {|it| it['fieldLabel'] },
1624
+ "NAME" => lambda {|it| it['fieldName'] },
1625
+ "TYPE" => lambda {|it| it['type'] },
1626
+ "REQUIRED" => lambda {|it| format_boolean it['required'] },
1627
+ "DEFAULT" => lambda {|it| it['defaultValue'] },
1628
+ }, options)
1629
+ end
1630
+
1631
+ end