morpheus-cli 5.3.0.1 → 5.3.2

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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/README.md +1 -3
  4. data/lib/morpheus/api/account_groups_interface.rb +0 -6
  5. data/lib/morpheus/api/accounts_interface.rb +1 -7
  6. data/lib/morpheus/api/api_client.rb +155 -119
  7. data/lib/morpheus/api/appliance_settings_interface.rb +6 -9
  8. data/lib/morpheus/api/approvals_interface.rb +5 -8
  9. data/lib/morpheus/api/apps_interface.rb +0 -7
  10. data/lib/morpheus/api/archive_buckets_interface.rb +9 -16
  11. data/lib/morpheus/api/archive_files_interface.rb +0 -6
  12. data/lib/morpheus/api/auth_interface.rb +4 -4
  13. data/lib/morpheus/api/backup_settings_interface.rb +5 -8
  14. data/lib/morpheus/api/blueprints_interface.rb +1 -7
  15. data/lib/morpheus/api/budgets_interface.rb +0 -6
  16. data/lib/morpheus/api/certificate_types_interface.rb +14 -0
  17. data/lib/morpheus/api/certificates_interface.rb +9 -0
  18. data/lib/morpheus/api/cloud_datastores_interface.rb +0 -6
  19. data/lib/morpheus/api/cloud_folders_interface.rb +1 -7
  20. data/lib/morpheus/api/cloud_policies_interface.rb +0 -6
  21. data/lib/morpheus/api/cloud_resource_pools_interface.rb +0 -6
  22. data/lib/morpheus/api/clouds_interface.rb +0 -6
  23. data/lib/morpheus/api/clusters_interface.rb +39 -42
  24. data/lib/morpheus/api/containers_interface.rb +0 -6
  25. data/lib/morpheus/api/custom_instance_types_interface.rb +0 -6
  26. data/lib/morpheus/api/cypher_interface.rb +0 -6
  27. data/lib/morpheus/api/datastores_interface.rb +4 -7
  28. data/lib/morpheus/api/deploy_interface.rb +1 -6
  29. data/lib/morpheus/api/environments_interface.rb +0 -6
  30. data/lib/morpheus/api/execute_schedules_interface.rb +0 -6
  31. data/lib/morpheus/api/execution_request_interface.rb +0 -6
  32. data/lib/morpheus/api/file_copy_request_interface.rb +2 -9
  33. data/lib/morpheus/api/group_policies_interface.rb +0 -6
  34. data/lib/morpheus/api/groups_interface.rb +0 -7
  35. data/lib/morpheus/api/guidance_interface.rb +9 -12
  36. data/lib/morpheus/api/health_interface.rb +0 -6
  37. data/lib/morpheus/api/image_builder_boot_scripts_interface.rb +0 -6
  38. data/lib/morpheus/api/image_builder_image_builds_interface.rb +0 -6
  39. data/lib/morpheus/api/image_builder_interface.rb +3 -9
  40. data/lib/morpheus/api/image_builder_preseed_scripts_interface.rb +0 -6
  41. data/lib/morpheus/api/instance_types_interface.rb +0 -7
  42. data/lib/morpheus/api/instances_interface.rb +8 -19
  43. data/lib/morpheus/api/integration_types_interface.rb +14 -0
  44. data/lib/morpheus/api/integrations_interface.rb +36 -21
  45. data/lib/morpheus/api/invoice_line_items_interface.rb +4 -9
  46. data/lib/morpheus/api/jobs_interface.rb +11 -14
  47. data/lib/morpheus/api/key_pairs_interface.rb +0 -6
  48. data/lib/morpheus/api/library_cluster_layouts_interface.rb +0 -6
  49. data/lib/morpheus/api/library_container_scripts_interface.rb +0 -6
  50. data/lib/morpheus/api/library_container_templates_interface.rb +0 -6
  51. data/lib/morpheus/api/library_container_types_interface.rb +0 -6
  52. data/lib/morpheus/api/library_container_upgrades_interface.rb +0 -6
  53. data/lib/morpheus/api/library_instance_types_interface.rb +0 -6
  54. data/lib/morpheus/api/library_layouts_interface.rb +0 -6
  55. data/lib/morpheus/api/library_spec_template_types_interface.rb +0 -6
  56. data/lib/morpheus/api/library_spec_templates_interface.rb +0 -6
  57. data/lib/morpheus/api/license_interface.rb +0 -6
  58. data/lib/morpheus/api/load_balancers_interface.rb +0 -6
  59. data/lib/morpheus/api/log_settings_interface.rb +9 -12
  60. data/lib/morpheus/api/logs_interface.rb +0 -6
  61. data/lib/morpheus/api/monitoring_alerts_interface.rb +0 -6
  62. data/lib/morpheus/api/monitoring_apps_interface.rb +0 -6
  63. data/lib/morpheus/api/monitoring_checks_interface.rb +0 -6
  64. data/lib/morpheus/api/monitoring_contacts_interface.rb +0 -6
  65. data/lib/morpheus/api/monitoring_groups_interface.rb +0 -6
  66. data/lib/morpheus/api/monitoring_incidents_interface.rb +0 -6
  67. data/lib/morpheus/api/monitoring_interface.rb +6 -12
  68. data/lib/morpheus/api/network_domain_records_interface.rb +0 -6
  69. data/lib/morpheus/api/network_domains_interface.rb +0 -6
  70. data/lib/morpheus/api/network_groups_interface.rb +0 -6
  71. data/lib/morpheus/api/network_pool_ips_interface.rb +0 -6
  72. data/lib/morpheus/api/network_pool_servers_interface.rb +0 -6
  73. data/lib/morpheus/api/network_pools_interface.rb +0 -6
  74. data/lib/morpheus/api/network_proxies_interface.rb +0 -6
  75. data/lib/morpheus/api/network_routers_interface.rb +0 -6
  76. data/lib/morpheus/api/network_security_servers_interface.rb +6 -9
  77. data/lib/morpheus/api/network_services_interface.rb +14 -14
  78. data/lib/morpheus/api/network_subnets_interface.rb +0 -6
  79. data/lib/morpheus/api/network_types_interface.rb +1 -7
  80. data/lib/morpheus/api/networks_interface.rb +0 -6
  81. data/lib/morpheus/api/option_type_lists_interface.rb +18 -12
  82. data/lib/morpheus/api/option_types_interface.rb +0 -6
  83. data/lib/morpheus/api/options_interface.rb +0 -6
  84. data/lib/morpheus/api/packages_interface.rb +0 -6
  85. data/lib/morpheus/api/policies_interface.rb +1 -8
  86. data/lib/morpheus/api/power_schedules_interface.rb +0 -6
  87. data/lib/morpheus/api/price_sets_interface.rb +8 -11
  88. data/lib/morpheus/api/prices_interface.rb +12 -15
  89. data/lib/morpheus/api/processes_interface.rb +0 -6
  90. data/lib/morpheus/api/provision_types_interface.rb +0 -6
  91. data/lib/morpheus/api/provisioning_license_types_interface.rb +0 -6
  92. data/lib/morpheus/api/provisioning_licenses_interface.rb +0 -6
  93. data/lib/morpheus/api/provisioning_settings_interface.rb +6 -9
  94. data/lib/morpheus/api/read_interface.rb +23 -0
  95. data/lib/morpheus/api/reports_interface.rb +0 -6
  96. data/lib/morpheus/api/rest_interface.rb +12 -10
  97. data/lib/morpheus/api/roles_interface.rb +7 -6
  98. data/lib/morpheus/api/security_group_rules_interface.rb +0 -7
  99. data/lib/morpheus/api/security_groups_interface.rb +0 -6
  100. data/lib/morpheus/api/server_types_interface.rb +0 -6
  101. data/lib/morpheus/api/servers_interface.rb +7 -6
  102. data/lib/morpheus/api/service_plans_interface.rb +11 -14
  103. data/lib/morpheus/api/storage_providers_interface.rb +9 -16
  104. data/lib/morpheus/api/subnet_types_interface.rb +1 -7
  105. data/lib/morpheus/api/subnets_interface.rb +0 -6
  106. data/lib/morpheus/api/task_sets_interface.rb +0 -6
  107. data/lib/morpheus/api/tasks_interface.rb +0 -6
  108. data/lib/morpheus/api/user_groups_interface.rb +0 -6
  109. data/lib/morpheus/api/user_settings_interface.rb +38 -18
  110. data/lib/morpheus/api/user_sources_interface.rb +0 -6
  111. data/lib/morpheus/api/users_interface.rb +0 -6
  112. data/lib/morpheus/api/vdi_allocations_interface.rb +9 -0
  113. data/lib/morpheus/api/vdi_apps_interface.rb +9 -0
  114. data/lib/morpheus/api/vdi_gateways_interface.rb +9 -0
  115. data/lib/morpheus/api/vdi_interface.rb +28 -0
  116. data/lib/morpheus/api/vdi_pools_interface.rb +19 -0
  117. data/lib/morpheus/api/virtual_images_interface.rb +0 -6
  118. data/lib/morpheus/api/whitelabel_settings_interface.rb +8 -11
  119. data/lib/morpheus/api/wiki_interface.rb +0 -6
  120. data/lib/morpheus/cli.rb +9 -2
  121. data/lib/morpheus/cli/access_token_command.rb +1 -1
  122. data/lib/morpheus/cli/account_groups_command.rb +4 -4
  123. data/lib/morpheus/cli/apps.rb +68 -84
  124. data/lib/morpheus/cli/archives_command.rb +5 -5
  125. data/lib/morpheus/cli/blueprints_command.rb +5 -5
  126. data/lib/morpheus/cli/boot_scripts_command.rb +1 -1
  127. data/lib/morpheus/cli/catalog_item_types_command.rb +13 -13
  128. data/lib/morpheus/cli/certificates_command.rb +575 -0
  129. data/lib/morpheus/cli/change_password_command.rb +4 -4
  130. data/lib/morpheus/cli/cli_command.rb +63 -7
  131. data/lib/morpheus/cli/clouds.rb +3 -2
  132. data/lib/morpheus/cli/clusters.rb +3 -3
  133. data/lib/morpheus/cli/commands/standard/man_command.rb +4 -5
  134. data/lib/morpheus/cli/credentials.rb +4 -11
  135. data/lib/morpheus/cli/environments_command.rb +1 -1
  136. data/lib/morpheus/cli/execute_schedules_command.rb +3 -3
  137. data/lib/morpheus/cli/hosts.rb +253 -232
  138. data/lib/morpheus/cli/image_builder_command.rb +6 -6
  139. data/lib/morpheus/cli/instance_types.rb +1 -1
  140. data/lib/morpheus/cli/instances.rb +196 -186
  141. data/lib/morpheus/cli/integrations_command.rb +1155 -42
  142. data/lib/morpheus/cli/invoices_command.rb +75 -67
  143. data/lib/morpheus/cli/key_pairs.rb +2 -2
  144. data/lib/morpheus/cli/library_cluster_layouts_command.rb +2 -3
  145. data/lib/morpheus/cli/library_container_scripts_command.rb +4 -5
  146. data/lib/morpheus/cli/library_container_templates_command.rb +5 -1
  147. data/lib/morpheus/cli/library_container_types_command.rb +8 -9
  148. data/lib/morpheus/cli/library_instance_types_command.rb +6 -7
  149. data/lib/morpheus/cli/library_layouts_command.rb +9 -5
  150. data/lib/morpheus/cli/library_option_lists_command.rb +72 -20
  151. data/lib/morpheus/cli/library_option_types_command.rb +8 -4
  152. data/lib/morpheus/cli/library_spec_templates_command.rb +3 -4
  153. data/lib/morpheus/cli/library_upgrades_command.rb +6 -6
  154. data/lib/morpheus/cli/license.rb +2 -2
  155. data/lib/morpheus/cli/load_balancers.rb +1 -1
  156. data/lib/morpheus/cli/login.rb +10 -1
  157. data/lib/morpheus/cli/mixins/option_source_helper.rb +15 -16
  158. data/lib/morpheus/cli/mixins/print_helper.rb +33 -18
  159. data/lib/morpheus/cli/mixins/provisioning_helper.rb +4 -4
  160. data/lib/morpheus/cli/mixins/vdi_helper.rb +246 -0
  161. data/lib/morpheus/cli/network_domains_command.rb +2 -2
  162. data/lib/morpheus/cli/network_routers_command.rb +22 -9
  163. data/lib/morpheus/cli/networks_command.rb +2 -2
  164. data/lib/morpheus/cli/option_types.rb +39 -34
  165. data/lib/morpheus/cli/policies_command.rb +0 -1
  166. data/lib/morpheus/cli/power_schedules_command.rb +3 -3
  167. data/lib/morpheus/cli/preseed_scripts_command.rb +1 -1
  168. data/lib/morpheus/cli/remote.rb +2 -2
  169. data/lib/morpheus/cli/reports_command.rb +5 -2
  170. data/lib/morpheus/cli/roles.rb +224 -64
  171. data/lib/morpheus/cli/security_group_rules.rb +1 -1
  172. data/lib/morpheus/cli/setup.rb +0 -1
  173. data/lib/morpheus/cli/subnets_command.rb +11 -2
  174. data/lib/morpheus/cli/tenants_command.rb +3 -3
  175. data/lib/morpheus/cli/user_groups_command.rb +3 -3
  176. data/lib/morpheus/cli/user_settings_command.rb +268 -57
  177. data/lib/morpheus/cli/user_sources_command.rb +3 -3
  178. data/lib/morpheus/cli/users.rb +3 -3
  179. data/lib/morpheus/cli/vdi_allocations_command.rb +159 -0
  180. data/lib/morpheus/cli/vdi_apps_command.rb +317 -0
  181. data/lib/morpheus/cli/vdi_command.rb +359 -0
  182. data/lib/morpheus/cli/vdi_gateways_command.rb +290 -0
  183. data/lib/morpheus/cli/vdi_pools_command.rb +571 -0
  184. data/lib/morpheus/cli/version.rb +1 -1
  185. data/lib/morpheus/cli/virtual_images.rb +1 -1
  186. data/lib/morpheus/cli/whoami.rb +0 -15
  187. data/lib/morpheus/cli/wiki_command.rb +1 -1
  188. data/lib/morpheus/rest_client.rb +30 -0
  189. data/lib/morpheus/terminal.rb +15 -7
  190. metadata +18 -2
@@ -2,16 +2,18 @@ require 'morpheus/cli/cli_command'
2
2
 
3
3
  class Morpheus::Cli::IntegrationsCommand
4
4
  include Morpheus::Cli::CliCommand
5
- include Morpheus::Cli::AccountsHelper
6
5
 
7
6
  set_command_name :'integrations'
7
+ set_command_description "Integrations: View and manage integrations"
8
8
 
9
- register_subcommands :list
10
- set_default_subcommand :list
9
+ register_subcommands :list, :get, :add, :update, :remove, :refresh
10
+ register_subcommands :list_objects, :get_object, :add_object, :remove_object
11
+ register_subcommands :list_types, :get_type
11
12
 
12
13
  def connect(opts)
13
14
  @api_client = establish_remote_appliance_connection(opts)
14
15
  @integrations_interface = @api_client.integrations
16
+ @integration_types_interface = @api_client.integration_types
15
17
  end
16
18
 
17
19
  def handle(args)
@@ -22,68 +24,1012 @@ class Morpheus::Cli::IntegrationsCommand
22
24
  options = {}
23
25
  params = {}
24
26
  optparse = Morpheus::Cli::OptionParser.new do |opts|
25
- opts.banner = subcommand_usage()
26
- build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
27
+ opts.banner = subcommand_usage("[search]")
28
+ opts.on('-t', '--type CODE', "Filter by type code(s), see `list-types` for available type codes") do |val|
29
+ params['type'] = val
30
+ end
31
+ opts.on('--url URL', String, "Filter by url") do |val|
32
+ params['url'] = val
33
+ end
34
+ build_standard_list_options(opts, options)
27
35
  opts.footer = "List integrations."
28
36
  end
29
37
  optparse.parse!(args)
38
+ # verify_args!(args:args, optparse:optparse, count:0)
39
+ if args.count > 0
40
+ options[:phrase] = args.join(" ")
41
+ end
30
42
  connect(options)
31
- if args.count != 0
32
- raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
33
- return 1
43
+ params.merge!(parse_list_options(options))
44
+ @integrations_interface.setopts(options)
45
+ if options[:dry_run]
46
+ print_dry_run @integrations_interface.dry.list(params)
47
+ return
48
+ end
49
+ json_response = @integrations_interface.list(params)
50
+ render_response(json_response, options, integration_list_key) do
51
+ integrations = json_response[integration_list_key]
52
+ print_h1 "Morpheus Integrations", parse_list_subtitles(options), options
53
+ if integrations.empty?
54
+ print cyan,"No integrations found.",reset,"\n"
55
+ else
56
+ list_columns = {
57
+ "ID" => 'id',
58
+ "Name" => 'name',
59
+ "Type" => lambda {|it| format_integration_type(it) },
60
+ "URL" => lambda {|it| it['url'] },
61
+ "Username" => lambda {|it| it['username'] },
62
+ "Enabled" => lambda {|it| format_boolean(it['enabled']) },
63
+ "Status Date" => lambda {|it| format_local_dt(it['statusDate']) },
64
+ "Status" => lambda {|it| format_integration_status(it) },
65
+ # "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
66
+ # "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
67
+ }.upcase_keys!
68
+ print as_pretty_table(integrations, list_columns, options)
69
+ print_results_pagination(json_response)
70
+ end
71
+ print reset,"\n"
34
72
  end
73
+ return 0, nil
74
+ end
35
75
 
36
- begin
37
- params.merge!(parse_list_options(options))
38
- @integrations_interface.setopts(options)
39
- if options[:dry_run]
40
- print_dry_run @integrations_interface.dry.list(params)
41
- return
76
+ def get(args)
77
+ params = {}
78
+ options = {}
79
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
80
+ opts.banner = subcommand_usage("[integration]")
81
+ opts.on('--objects', 'Display exposed objects for the integration.') do
82
+ options[:show_objects] = true
42
83
  end
43
- json_response = @integrations_interface.list(params)
84
+ build_standard_get_options(opts, options)
85
+ opts.footer = <<-EOT
86
+ Get details about a specific integration.
87
+ [integration] is required. This is the name or id of an integration.
88
+ EOT
89
+ end
90
+ optparse.parse!(args)
91
+ verify_args!(args:args, optparse:optparse, min:1)
92
+ connect(options)
93
+ params.merge!(parse_query_options(options))
94
+ id_list = parse_id_list(args)
95
+ return run_command_for_each_arg(id_list) do |arg|
96
+ _get(arg, params, options)
97
+ end
98
+ end
44
99
 
45
- render_result = render_with_format(json_response, options, 'integrations')
46
- return 0 if render_result
100
+ def _get(id, params, options)
101
+ integration = nil
102
+ if id.to_s !~ /\A\d{1,}\Z/
103
+ integration = find_integration_by_name_or_id(id)
104
+ return 1, "integration not found for #{id}" if integration.nil?
105
+ id = integration['id']
106
+ end
107
+ if options[:show_objects]
108
+ params['objects'] = true
109
+ end
110
+ @integrations_interface.setopts(options)
111
+ if options[:dry_run]
112
+ print_dry_run @integrations_interface.dry.get(id, params)
113
+ return
114
+ end
115
+ json_response = @integrations_interface.get(id, params)
116
+ integration = json_response[integration_object_key]
117
+ render_response(json_response, options, integration_object_key) do
118
+ print_h1 "Integration Details", [], options
119
+ print cyan
120
+ show_columns = {
121
+ "ID" => 'id',
122
+ "Name" => 'name',
123
+ "Type" => lambda {|it| format_integration_type(it) },
124
+ "URL" => lambda {|it| it['url'] },
125
+ "Host" => lambda {|it| it['host'] },
126
+ "Port" => lambda {|it| it['port'] },
127
+ "Username" => lambda {|it| it['username'] },
128
+ "Password" => lambda {|it| it['password'] },
129
+ "Token" => lambda {|it| it['token'] },
130
+ "Service Key" => lambda {|it| it['serviceKey'] ? it['serviceKey']['name'] : nil },
131
+ "Auth Key" => lambda {|it| it['authKey'] ? it['authKey']['name'] : nil },
132
+ "Enabled" => lambda {|it| format_boolean(it['enabled']) },
133
+ "Status Date" => lambda {|it| format_local_dt(it['statusDate']) },
134
+ "Status" => lambda {|it| format_integration_status(it) },
135
+ # "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
136
+ # "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
137
+ }
138
+ show_columns.delete("URL") if integration['url'].nil?
139
+ show_columns.delete("Host") if integration['host'].nil?
140
+ show_columns.delete("Port") if integration['port'].nil?
141
+ show_columns.delete("Password") if integration['password'].nil?
142
+ show_columns.delete("Token") if integration['token'].nil?
143
+ show_columns.delete("Service Key") if integration['serviceKey'].nil?
144
+ show_columns.delete("Auth Key") if integration['authKey'].nil?
145
+ print_description_list(show_columns, integration, options)
47
146
 
48
- title = "Morpheus Integrations"
49
- subtitles = []
50
- subtitles += parse_list_subtitles(options)
51
- print_h1 title, subtitles
147
+ if options[:show_objects]
148
+ # they are loaded above with ?objects=true
149
+ integration_objects = integration['objects']
150
+ if integration_objects.nil?
151
+ objects_json_response = @integrations_interface.list_objects(integration['id'], {})
152
+ integration_objects = objects_json_response[integration_object_list_key]
153
+ end
154
+ cloud_objects = integration_objects.select {|it| it['refType'] == "ComputeZone" }
155
+ library_objects = integration_objects.select {|it| it['refType'] == "InstanceTypeLayout" || it['refType'] == "InstanceType" }
156
+ blueprint_objects = integration_objects.select {|it| it['refType'] == "AppTemplate" }
157
+ catalog_objects = integration_objects.select {|it| it['refType'] == "CatalogItemType" }
158
+ if integration_objects.empty?
159
+ print reset,"\n"
160
+ print cyan,"No objects found.",reset,"\n"
161
+ else
162
+ # Exposed Clouds
163
+ if !cloud_objects.empty?
164
+ print_h2 "Exposed Clouds", [], options
165
+ list_columns = {
166
+ # "ID" => 'id',
167
+ "Name" => 'name',
168
+ # "Category" => 'category',
169
+ # "Ref Type" => 'refType',
170
+ # "Cloud ID" => 'refId',
171
+ "Group" => lambda {|it| it['group']['name'] rescue nil },
172
+ }.upcase_keys!
173
+ print as_pretty_table(cloud_objects, list_columns, options)
174
+ end
52
175
 
53
- integrations = json_response['integrations']
176
+ # Exposed Libraries
177
+ if !library_objects.empty?
178
+ # print_h2 "Exposed Libraries", [], options
179
+ print_h2 "Exposed Layouts", [], options
180
+ list_columns = {
181
+ # "ID" => 'id',
182
+ "Name" => 'name',
183
+ "Version" => lambda {|it| it['layout']['instanceVersion'] rescue nil },
184
+ "Instance Type" => lambda {|it| it['layout']['instanceType']['name'] rescue nil },
185
+ "Provision Type" => lambda {|it| it['layout']['provisionType']['name'] rescue nil },
186
+ }.upcase_keys!
187
+ print as_pretty_table(library_objects, list_columns, options)
188
+ end
54
189
 
55
- if integrations.empty?
56
- print cyan,"No integrations found.",reset,"\n"
57
- else
58
- rows = integrations.collect do |it|
59
- {
60
- id: it['id'],
61
- name: it['name'],
62
- status: format_integration_status(it),
63
- last_updated: format_local_dt(it['statusDate']),
64
- type: it['integrationType'] ? it['integrationType']['name'] : ''
65
- }
190
+ # Exposed Blueprints
191
+ if !blueprint_objects.empty?
192
+ print_h2 "Exposed Blueprints", [], options
193
+ list_columns = {
194
+ # "ID" => 'id',
195
+ "Name" => 'name',
196
+ # "Type" => lambda {|it| it['blueprint']['type'] rescue nil },
197
+ "Blueprint" => lambda {|it| it['blueprint']['name'] rescue nil },
198
+ "Group" => lambda {|it| it['group']['name'] rescue nil },
199
+ }.upcase_keys!
200
+ print as_pretty_table(blueprint_objects, list_columns, options)
201
+ end
202
+
203
+ # Exposed Catalog Items
204
+ if !catalog_objects.empty?
205
+ print_h2 "Exposed Catalog Items", [], options
206
+ list_columns = {
207
+ # "ID" => 'id',
208
+ "Name" => 'name',
209
+ }.upcase_keys!
210
+ print as_pretty_table(catalog_objects, list_columns, options)
211
+ end
66
212
  end
67
- columns = [ :id, :name, :status, :last_updated, :type]
68
- print as_pretty_table(rows, columns)
69
213
  end
70
214
  print reset,"\n"
71
- return 0
72
- rescue RestClient::Exception => e
73
- print_rest_exception(e, options)
74
- exit 1
215
+ end
216
+ return 0, nil
217
+ end
218
+
219
+ def add(args)
220
+ options = {}
221
+ params = {}
222
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
223
+ opts.banner = subcommand_usage("[name] -t CODE [options]")
224
+ # opts.on('-t', '--type CODE', "Integration Type code, see `#{command_name} list-types` for available type codes") do |val|
225
+ # options[:options]['type'] = val
226
+ # end
227
+ build_option_type_options(opts, options, add_integration_option_types)
228
+ build_option_type_options(opts, options, add_integration_advanced_option_types)
229
+ build_standard_add_options(opts, options)
230
+ opts.footer = <<-EOT
231
+ Create a new integration.
232
+ [name] is required. This is the name of the new integration
233
+ Configuration options vary by integration type.
234
+ EOT
235
+ end
236
+ optparse.parse!(args)
237
+ verify_args!(args:args, optparse:optparse, min:0, max:1)
238
+ options[:options]['name'] = args[0] if args[0]
239
+ connect(options)
240
+ payload = {}
241
+ if options[:payload]
242
+ payload = options[:payload]
243
+ payload.deep_merge!({integration_object_key => parse_passed_options(options)})
244
+ else
245
+ payload.deep_merge!({integration_object_key => parse_passed_options(options)})
246
+ # Type prompt first
247
+ #params['type'] = Morpheus::Cli::OptionTypes.no_prompt([{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => [{'name' => 'Instance', 'value' => 'instance'}, {'name' => 'Blueprint', 'value' => 'blueprint'}, {'name' => 'Workflow', 'value' => 'workflow'}], 'defaultValue' => 'instance', 'required' => true}], options[:options], @api_client, options[:params])['type']
248
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(add_integration_option_types(), options[:options], @api_client, options[:params])
249
+ params.deep_merge!(v_prompt)
250
+ advanced_config = Morpheus::Cli::OptionTypes.no_prompt(add_integration_advanced_option_types, options[:options], @api_client, options[:params])
251
+ advanced_config.deep_compact!
252
+ params.deep_merge!(advanced_config)
253
+
254
+ # lookup type by name or code to validate it exists and to prompt for its optionTypes
255
+ # set integration.type=code because the api expects it that way.
256
+ if params['type'].to_s.empty?
257
+ raise_command_error "missing required option: --type TYPE", args, optparse
258
+ end
259
+ integration_type = find_integration_type_by_name_or_code_id(params['type'])
260
+ if integration_type.nil?
261
+ return 1, "integration type not found for #{params['type']}"
262
+ end
263
+ params['type'] = integration_type['code']
264
+ config_option_types = integration_type['optionTypes']
265
+ if config_option_types.nil?
266
+ config_option_types = @integration_types_interface.option_types(integration_type['id'])['optionTypes']
267
+ end
268
+ if config_option_types.nil?
269
+ print yellow,"No option types found for integration type: #{integration_type['name']} (#{integration_type['code']})", reset, "\n"
270
+ end
271
+ if config_option_types && config_option_types.size > 0
272
+ # optionTypes do not need fieldContext: 'integration'
273
+ config_option_types.each do |opt|
274
+ if opt['fieldContext'] == 'integration' || opt['fieldContext'] == 'domain'
275
+ opt['fieldContext'] = nil
276
+ end
277
+ end
278
+ config_prompt = Morpheus::Cli::OptionTypes.prompt(config_option_types, options[:options], @api_client, options[:params])
279
+ config_prompt.deep_compact!
280
+ params.deep_merge!(config_prompt)
281
+ end
282
+ # convert checkbox "on" and "off" to true and false
283
+ params.booleanize!
284
+ payload[integration_object_key].deep_merge!(params)
285
+ end
286
+ @integrations_interface.setopts(options)
287
+ if options[:dry_run]
288
+ print_dry_run @integrations_interface.dry.create(payload)
289
+ return 0, nil
290
+ end
291
+ json_response = @integrations_interface.create(payload)
292
+ integration = json_response[integration_object_key]
293
+ render_response(json_response, options, integration_object_key) do
294
+ print_green_success "Added integration #{integration['name']}"
295
+ return _get(integration["id"], {}, options)
296
+ end
297
+ return 0, nil
298
+ end
299
+
300
+ def update(args)
301
+ options = {}
302
+ params = {}
303
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
304
+ opts.banner = subcommand_usage("[integration] [options]")
305
+ build_option_type_options(opts, options, update_integration_option_types)
306
+ build_option_type_options(opts, options, update_integration_advanced_option_types)
307
+ opts.on(nil, '--no-refresh', "Skip refresh on update.") do
308
+ params['refresh'] = 'false'
309
+ end
310
+ build_standard_update_options(opts, options)
311
+ opts.footer = <<-EOT
312
+ Update an integration.
313
+ [integration] is required. This is the name or id of an integration.
314
+ EOT
315
+ end
316
+ optparse.parse!(args)
317
+ verify_args!(args:args, optparse:optparse, count:1)
318
+ connect(options)
319
+ integration = find_integration_by_name_or_id(args[0])
320
+ return 1 if integration.nil?
321
+ payload = {}
322
+ if options[:payload]
323
+ payload = options[:payload]
324
+ payload.deep_merge!({integration_object_key => parse_passed_options(options)})
325
+ else
326
+ payload.deep_merge!({integration_object_key => parse_passed_options(options)})
327
+ # do not prompt on update
328
+ v_prompt = Morpheus::Cli::OptionTypes.no_prompt(update_integration_option_types, options[:options], @api_client, options[:params])
329
+ v_prompt.deep_compact!
330
+ params.deep_merge!(v_prompt)
331
+ advanced_config = Morpheus::Cli::OptionTypes.no_prompt(update_integration_advanced_option_types, options[:options], @api_client, options[:params])
332
+ advanced_config.deep_compact!
333
+ params.deep_merge!(advanced_config)
334
+ # convert checkbox "on" and "off" to true and false
335
+ params.booleanize!
336
+ # massage association params a bit
337
+
338
+ payload.deep_merge!({integration_object_key => params})
339
+ if payload[integration_object_key].empty? # || options[:no_prompt]
340
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
341
+ end
342
+ end
343
+ @integrations_interface.setopts(options)
344
+ if options[:dry_run]
345
+ print_dry_run @integrations_interface.dry.update(integration['id'], payload)
346
+ return
347
+ end
348
+ json_response = @integrations_interface.update(integration['id'], payload)
349
+ integration = json_response[integration_object_key]
350
+ render_response(json_response, options, integration_object_key) do
351
+ print_green_success "Updated integration #{integration['name']}"
352
+ return _get(integration["id"], {}, options)
353
+ end
354
+ return 0, nil
355
+ end
356
+
357
+ def refresh(args)
358
+ options = {}
359
+ query_params = {}
360
+ params = {}
361
+ payload = {}
362
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
363
+ opts.banner = subcommand_usage("[integration] [options]")
364
+ build_option_type_options(opts, options, update_integration_option_types)
365
+ build_option_type_options(opts, options, update_integration_advanced_option_types)
366
+ build_standard_update_options(opts, options)
367
+ opts.footer = <<-EOT
368
+ Refresh an integration.
369
+ [integration] is required. This is the name or id of an integration.
370
+ EOT
371
+ end
372
+ optparse.parse!(args)
373
+ verify_args!(args:args, optparse:optparse, count:1)
374
+ connect(options)
375
+ query_params.merge!(parse_query_options(options))
376
+ integration = find_integration_by_name_or_id(args[0])
377
+ return 1 if integration.nil?
378
+ payload = {}
379
+ if options[:payload]
380
+ payload = options[:payload]
381
+ payload.deep_merge!({integration_object_key => parse_passed_options(options)})
382
+ else
383
+ payload.deep_merge!({integration_object_key => parse_passed_options(options)})
384
+ end
385
+ @integrations_interface.setopts(options)
386
+ if options[:dry_run]
387
+ print_dry_run @integrations_interface.dry.refresh(integration['id'], query_params, payload)
388
+ return
389
+ end
390
+ json_response = @integrations_interface.refresh(integration['id'], query_params, payload)
391
+ integration = json_response[integration_object_key]
392
+ render_response(json_response, options, integration_object_key) do
393
+ print_green_success "Refreshed integration #{integration['name']}"
394
+ return _get(integration["id"], {}, options)
395
+ end
396
+ return 0, nil
397
+ end
398
+
399
+ def remove(args)
400
+ options = {}
401
+ params = {}
402
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
403
+ opts.banner = subcommand_usage("[integration] [options]")
404
+ build_standard_remove_options(opts, options)
405
+ opts.footer = <<-EOT
406
+ Delete an integration.
407
+ [integration] is required. This is the name or id of an integration.
408
+ EOT
409
+ end
410
+ optparse.parse!(args)
411
+ verify_args!(args:args, optparse:optparse, count:1)
412
+ connect(options)
413
+ integration = find_integration_by_name_or_id(args[0])
414
+ return 1 if integration.nil?
415
+ @integrations_interface.setopts(options)
416
+ if options[:dry_run]
417
+ print_dry_run @integrations_interface.dry.destroy(integration['id'], params)
418
+ return
419
+ end
420
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the integration #{integration['name']}?")
421
+ return 9, "aborted command"
422
+ end
423
+ json_response = @integrations_interface.destroy(integration['id'], params)
424
+ render_response(json_response, options) do
425
+ print_green_success "Removed integration #{integration['name']}"
426
+ end
427
+ return 0, nil
428
+ end
429
+
430
+
431
+ def list_types(args)
432
+ options = {}
433
+ params = {}
434
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
435
+ opts.banner = subcommand_usage("[search]")
436
+ opts.on('--optionTypes [true|false]', String, "Include optionTypes in the response. Default is false.") do |val|
437
+ params['optionTypes'] = (val.to_s == '' || val.to_s == 'on' || val.to_s == 'true')
438
+ end
439
+ build_standard_list_options(opts, options)
440
+ opts.footer = "List integration types."
441
+ end
442
+ optparse.parse!(args)
443
+ connect(options)
444
+ # verify_args!(args:args, optparse:optparse, count:0)
445
+ if args.count > 0
446
+ options[:phrase] = args.join(" ")
447
+ end
448
+ params.merge!(parse_list_options(options))
449
+ @integration_types_interface.setopts(options)
450
+ if options[:dry_run]
451
+ print_dry_run @integration_types_interface.dry.list(params)
452
+ return
453
+ end
454
+ json_response = @integration_types_interface.list(params)
455
+ render_response(json_response, options, integration_type_list_key) do
456
+ integration_types = json_response[integration_type_list_key]
457
+ print_h1 "Morpheus Integration Types", parse_list_subtitles(options), options
458
+ if integration_types.empty?
459
+ print cyan,"No integration types found.",reset,"\n"
460
+ else
461
+ list_columns = integration_type_column_definitions.upcase_keys!
462
+ print as_pretty_table(integration_types, list_columns, options)
463
+ print_results_pagination(json_response)
464
+ end
465
+ print reset,"\n"
466
+ end
467
+ return 0, nil
468
+ end
469
+
470
+ def get_type(args)
471
+ params = {'optionTypes' => true}
472
+ options = {}
473
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
474
+ opts.banner = subcommand_usage("[type]")
475
+ build_standard_get_options(opts, options)
476
+ opts.on('--optionTypes [true|false]', String, "Include optionTypes in the response. Default is true.") do |val|
477
+ params['optionTypes'] = (val.to_s == '' || val.to_s == 'on' || val.to_s == 'true')
478
+ end
479
+ opts.footer = <<-EOT
480
+ Get details about a specific integration type.
481
+ [type] is required. This is the name or id of an integration type.
482
+ EOT
483
+ end
484
+ optparse.parse!(args)
485
+ verify_args!(args:args, optparse:optparse, min:1)
486
+ connect(options)
487
+ params.merge!(parse_query_options(options))
488
+ id_list = parse_id_list(args)
489
+ return run_command_for_each_arg(id_list) do |arg|
490
+ _get_type(arg, params, options)
491
+ end
492
+ end
493
+
494
+ def _get_type(id, params, options)
495
+ integration_type = nil
496
+ if id.to_s !~ /\A\d{1,}\Z/
497
+ integration_type = find_integration_type_by_name_or_code(id)
498
+ return 1, "integration type not found for name or code '#{id}'" if integration_type.nil?
499
+ id = integration_type['id']
500
+ end
501
+ # /api/integration-types does not return optionTypes by default, use ?optionTypes=true
502
+ @integration_types_interface.setopts(options)
503
+ if options[:dry_run]
504
+ print_dry_run @integration_types_interface.dry.get(id, params)
505
+ return
506
+ end
507
+ json_response = @integration_types_interface.get(id, params)
508
+ integration_type = json_response[integration_type_object_key]
509
+ render_response(json_response, options, integration_type_object_key) do
510
+ print_h1 "Integration Type Details", [], options
511
+ print cyan
512
+ show_columns = integration_type_column_definitions
513
+ print_description_list(show_columns, integration_type)
514
+
515
+ if integration_type['optionTypes'] && integration_type['optionTypes'].size > 0
516
+ print_h2 "Option Types"
517
+ opt_columns = [
518
+ # {"ID" => lambda {|it| it['id'] } },
519
+ {"FIELD NAME" => lambda {|it| (it['fieldContext'] && it['fieldContext'] != 'integration') ? [it['fieldContext'], it['fieldName']].join('.') : it['fieldName'] } },
520
+ {"FIELD LABEL" => lambda {|it| it['fieldLabel'] } },
521
+ {"TYPE" => lambda {|it| it['type'] } },
522
+ {"DEFAULT" => lambda {|it| it['defaultValue'] } },
523
+ {"REQUIRED" => lambda {|it| format_boolean it['required'] } },
524
+ # {"DESCRIPTION" => lambda {|it| it['description'] }, # do it!
525
+ ]
526
+ print as_pretty_table(integration_type['optionTypes'], opt_columns)
527
+ else
528
+ # print cyan,"No option types found for this integration type.","\n",reset
529
+ end
530
+
531
+ print reset,"\n"
532
+ end
533
+ return 0, nil
534
+ end
535
+
536
+ ## Integration Objects
537
+
538
+ def list_objects(args)
539
+ options = {}
540
+ params = {}
541
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
542
+ opts.banner = subcommand_usage("[integration] [search]")
543
+ opts.on('-t', '--type CODE', "Filter by types: cloud, layout, blueprint, catalog") do |val|
544
+ params['type'] = [params['type'], val].compact.flatten.collect {|it| it.to_s.strip.split(",") }.flatten.collect {|it| it.to_s.strip }
545
+ end
546
+ build_standard_list_options(opts, options)
547
+ opts.footer = <<-EOT
548
+ List integration objects.
549
+ [integration] is required. This is the name or id of an integration.
550
+ EOT
551
+ end
552
+ optparse.parse!(args)
553
+ verify_args!(args:args, optparse:optparse, min:1)
554
+ connect(options)
555
+
556
+ integration = find_integration_by_name_or_id(args[0])
557
+ return 1, "integration not found for #{args[0]}" if integration.nil?
558
+
559
+ if args.count > 1
560
+ options[:phrase] = args[1..-1].join(" ")
561
+ end
562
+ params.merge!(parse_list_options(options))
563
+ @integrations_interface.setopts(options)
564
+ if options[:dry_run]
565
+ print_dry_run @integrations_interface.dry.list_objects(integration['id'], params)
566
+ return 0, nil
567
+ end
568
+ json_response = @integrations_interface.list_objects(integration['id'], params)
569
+ render_response(json_response, options, integration_list_key) do
570
+ integration_objects = json_response[integration_object_list_key]
571
+ print_h1 "Integration Objects [#{integration['name']}]", parse_list_subtitles(options), options
572
+ if integration_objects.empty?
573
+ print cyan,"No objects found.",reset,"\n"
574
+ else
575
+ list_columns = {
576
+ "ID" => 'id',
577
+ "Name" => 'name',
578
+ # "Category" => 'category',
579
+ # "Ref Type" => 'refType',
580
+ # "Ref ID" => 'refId',
581
+ # "Type" => lambda {|it| it['type'] },
582
+ "Type" => lambda {|it| it['type'].to_s.capitalize },
583
+ "Ref ID" => 'refId',
584
+ }.upcase_keys!
585
+ print as_pretty_table(integration_objects, list_columns, options)
586
+ print_results_pagination(json_response)
587
+ end
588
+ print reset,"\n"
589
+ end
590
+ return 0, nil
591
+ end
592
+
593
+ def get_object(args)
594
+ params = {}
595
+ options = {}
596
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
597
+ opts.banner = subcommand_usage("[integration] [object]")
598
+ opts.on( '-c', '--config', "Display config only, for blueprint objects" ) do
599
+ options[:show_config] = true
600
+ end
601
+ build_standard_get_options(opts, options)
602
+ opts.footer = <<-EOT
603
+ Get details about a specific integration object.
604
+ [integration] is required. This is the name or id of an integration.
605
+ [object] is required. This is the name or id of an integration object.
606
+ EOT
607
+ end
608
+ optparse.parse!(args)
609
+ verify_args!(args:args, optparse:optparse, min:1)
610
+ connect(options)
611
+ optparse.parse!(args)
612
+ verify_args!(args:args, optparse:optparse, min:2)
613
+ connect(options)
614
+ integration = find_integration_by_name_or_id(args[0])
615
+ return 1, "integration not found for #{args[0]}" if integration.nil?
616
+ params.merge!(parse_query_options(options))
617
+ id_list = parse_id_list(args[1..-1])
618
+ return run_command_for_each_arg(id_list) do |arg|
619
+ _get_object(integration, arg, params, options)
75
620
  end
76
621
  end
77
622
 
623
+ def _get_object(integration, id, params, options)
624
+ integration_object = nil
625
+ if id.to_s !~ /\A\d{1,}\Z/
626
+ integration_object = find_integration_object_by_name_or_id(integration['id'], id)
627
+ return 1, "integration object not found for #{id}" if integration_object.nil?
628
+ id = integration_object['id']
629
+ end
630
+ @integrations_interface.setopts(options)
631
+ if options[:dry_run]
632
+ print_dry_run @integrations_interface.dry.get_object(integration['id'], id, params)
633
+ return
634
+ end
635
+ json_response = @integrations_interface.get_object(integration['id'], id, params)
636
+ integration_object = json_response[integration_object_object_key]
637
+ config = integration_object['config']
638
+ # export just the config as json (default) or yaml
639
+ if options[:show_config]
640
+ unless options[:json] || options[:yaml] || options[:csv]
641
+ options[:json] = :true
642
+ end
643
+ return render_with_format(config, options)
644
+ end
645
+ render_response(json_response, options, integration_object_object_key) do
646
+ print_h1 "Integration Object Details", [], options
647
+ print cyan
648
+ if integration_object['type'] == 'cloud'
649
+ show_columns = {
650
+ "Integration" => lambda {|it| integration['name'] },
651
+ "Object ID" => 'id',
652
+ "Name" => 'name',
653
+ "Type" => lambda {|it| it['type'].to_s.capitalize },
654
+ # "Cloud" => lambda {|it| it['cloud']['name'] rescue nil },
655
+ # "Ref Type" => 'refType',
656
+ "Ref ID" => 'refId',
657
+ # "Reference" => lambda {|it| ("#{it['refType']}: #{it['refId']}" rescue nil) },
658
+ "Group" => lambda {|it| it['group']['name'] rescue nil },
659
+ }
660
+ print_description_list(show_columns, integration_object, options)
661
+ print reset,"\n"
662
+ elsif integration_object['type'] == 'layout'
663
+ show_columns = {
664
+ "Integration" => lambda {|it| integration['name'] },
665
+ "Object ID" => 'id',
666
+ "Name" => 'name',
667
+ "Type" => lambda {|it| it['type'].to_s.capitalize },
668
+ # "Layout" => lambda {|it| it['layout']['name'] rescue nil },
669
+ # "Ref Type" => 'refType',
670
+ "Ref ID" => 'refId',
671
+ # "Reference" => lambda {|it| ("#{it['refType']}: #{it['refId']}" rescue nil) },
672
+ "Provision Type" => lambda {|it| it['layout']['provisionType']['name'] rescue nil },
673
+ "Instance Type" => lambda {|it| it['layout']['instanceType']['name'] rescue nil },
674
+ "Version" => lambda {|it| it['layout']['instanceVersion'] rescue nil },
675
+ }
676
+ print_description_list(show_columns, integration_object, options)
677
+ print reset,"\n"
678
+ elsif integration_object['type'] == 'blueprint'
679
+ show_columns = {
680
+ "Integration" => lambda {|it| integration['name'] },
681
+ "Object ID" => 'id',
682
+ "Name" => 'name',
683
+ "Type" => lambda {|it| it['type'].to_s.capitalize },
684
+ # "Ref Type" => 'refType',
685
+ "Ref ID" => 'refId',
686
+ # "Reference" => lambda {|it| ("#{it['refType']}: #{it['refId']}" rescue nil) },
687
+ # "Blueprint Type" => lambda {|it| it['blueprint']['type'] rescue nil },
688
+ "Blueprint" => lambda {|it| it['blueprint']['name'] rescue nil },
689
+ "Group" => lambda {|it| it['group']['name'] rescue nil },
690
+ "Default Cloud" => lambda {|it| it['defaultCloud']['name'] rescue nil },
691
+ "Environment" => lambda {|it| it['environment'] rescue nil },
692
+ }
693
+ print_description_list(show_columns, integration_object, options)
694
+ # print reset,"\n"
695
+ # print_h2 "App Spec"
696
+ print_h2 "Config"
697
+ if config
698
+ # config_string = integration_object['config'] || ""
699
+ config_string = config.is_a?(Hash) ? JSON.pretty_generate(config) : config.to_s
700
+ #print reset,config_string,"\n",reset
701
+ config_lines = config_string.split("\n")
702
+ config_line_count = config_lines.size
703
+ max_lines = 10
704
+ if config_lines.size > max_lines
705
+ config_string = config_lines.first(max_lines).join("\n")
706
+ config_string << "\n\n"
707
+ config_string << "#{dark}(#{(config_line_count - max_lines)} more lines were not shown, use -c to show the config)#{reset}"
708
+ #config_string << "\n"
709
+ end
710
+ # strip --- yaml header
711
+ if config_string[0..3] == "---\n"
712
+ config_string = config_string[4..-1]
713
+ end
714
+ print reset,config_string.chomp("\n"),"\n",reset
715
+ else
716
+ print reset,"(blank)","\n",reset
717
+ end
718
+ print reset,"\n"
719
+ elsif integration_object['type'] == 'catalog'
720
+ show_columns = {
721
+ "Integration" => lambda {|it| integration['name'] },
722
+ "Object ID" => 'id',
723
+ "Name" => 'name',
724
+ "Type" => lambda {|it| it['type'].to_s.capitalize },
725
+ "Catalog Item" => lambda {|it| it['catalogItemType']['name'] rescue nil },
726
+ # "Ref Type" => 'refType',
727
+ # "Ref ID" => 'refId',
728
+ # "Reference" => lambda {|it| ("#{it['refType']}: #{it['refId']}" rescue nil) },
729
+ }
730
+ print_description_list(show_columns, integration_object, options)
731
+ print reset,"\n"
732
+ else
733
+ # Unknown type?
734
+ show_columns = {
735
+ "Integration" => lambda {|it| integration['name'] },
736
+ "Object ID" => 'id',
737
+ "Name" => 'name',
738
+ "Type" => lambda {|it| it['type'].to_s.capitalize },
739
+ "Ref Type" => 'refType',
740
+ "Ref ID" => 'refId',
741
+ }
742
+ print_description_list(show_columns, integration_object, options)
743
+ print reset,"\n"
744
+ end
745
+ end
746
+ return 0, nil
747
+ end
748
+
749
+ def add_object(args)
750
+ options = {}
751
+ params = {}
752
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
753
+ opts.banner = subcommand_usage("[integration] [name] -t CODE [options]")
754
+ # opts.on('-t', '--type CODE', "Integration ObjectType code, see `#{command_name} list-types` for available type codes") do |val|
755
+ # options[:options]['type'] = val
756
+ # end
757
+ build_option_type_options(opts, options, add_integration_object_option_types)
758
+ opts.on('--config-file FILE', String, "Config from a local JSON or YAML file") do |val|
759
+ options[:config_file] = val.to_s
760
+ file_content = nil
761
+ full_filename = File.expand_path(options[:config_file])
762
+ if File.exists?(full_filename)
763
+ file_content = File.read(full_filename)
764
+ else
765
+ print_red_alert "File not found: #{full_filename}"
766
+ return 1
767
+ end
768
+ parse_result = parse_json_or_yaml(file_content)
769
+ config_map = parse_result[:data]
770
+ if config_map.nil?
771
+ # todo: bubble up JSON.parse error message
772
+ raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:err]}"
773
+ #raise_command_error "Failed to parse config as valid YAML or JSON."
774
+ else
775
+ params['config'] = config_map
776
+ options[:options]['config'] = params['config'] # or file_content
777
+ end
778
+ end
779
+ # build_option_type_options(opts, options, add_integration_object_advanced_option_types)
780
+ build_standard_add_options(opts, options)
781
+ opts.footer = <<-EOT
782
+ Create a new integration object.
783
+ [integration] is required. This is the name or id of an integration.
784
+ [name] is required. This is the name of the new integration
785
+ Configuration options vary by integration type.
786
+ EOT
787
+ end
788
+ optparse.parse!(args)
789
+ verify_args!(args:args, optparse:optparse, min:1, max:2)
790
+ options[:options]['name'] = args[1] if args[1]
791
+ connect(options)
792
+ integration = find_integration_by_name_or_id(args[0])
793
+ return 1, "integration not found for #{args[0]}" if integration.nil?
794
+ payload = {}
795
+ if options[:payload]
796
+ payload = options[:payload]
797
+ payload.deep_merge!({integration_object_object_key => parse_passed_options(options)})
798
+ else
799
+ payload.deep_merge!({integration_object_object_key => parse_passed_options(options)})
800
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(add_integration_object_option_types(), options[:options], @api_client, options[:params])
801
+ v_prompt.deep_compact!
802
+ params.deep_merge!(v_prompt)
803
+ advanced_config = Morpheus::Cli::OptionTypes.no_prompt(add_integration_object_advanced_option_types, options[:options], @api_client, options[:params])
804
+ advanced_config.deep_compact!
805
+ params.deep_merge!(advanced_config)
806
+ params.booleanize!
807
+
808
+ # convert config string to a map
809
+ # config = params['config']
810
+ # if config && config.is_a?(String)
811
+ # parse_result = parse_json_or_yaml(config)
812
+ # config_map = parse_result[:data]
813
+ # if config_map.nil?
814
+ # # todo: bubble up JSON.parse error message
815
+ # raise_command_error "Failed to parse config as YAML or JSON. Error: #{parse_result[:err]}"
816
+ # #raise_command_error "Failed to parse config as valid YAML or JSON."
817
+ # else
818
+ # params['config'] = config_map
819
+ # end
820
+ # end
821
+ # if params['config']
822
+ # config_map = params.delete('config')
823
+ # params['config'] = as_json(config_map, {:pretty_json => true})
824
+ # end
825
+ # if options[:interactive_config]
826
+ # print_h2 "App Config"
827
+ # config_map = prompt_app_config(options)
828
+ # params['config'] = config_map
829
+ # end
830
+
831
+ payload[integration_object_object_key].deep_merge!(params)
832
+ end
833
+ @integrations_interface.setopts(options)
834
+ if options[:dry_run]
835
+ print_dry_run @integrations_interface.dry.create_object(integration['id'], payload)
836
+ return 0, nil
837
+ end
838
+ json_response = @integrations_interface.create_object(integration['id'], payload)
839
+ integration_object = json_response[integration_object_object_key]
840
+ render_response(json_response, options, integration_object_object_key) do
841
+ print_green_success "Added integration_object #{integration_object['name']}"
842
+ return _get_object(integration, integration_object["id"], {}, options)
843
+ end
844
+ return 0, nil
845
+ end
846
+
847
+ # def update_object(args)
848
+ # options = {}
849
+ # params = {}
850
+ # optparse = Morpheus::Cli::OptionParser.new do |opts|
851
+ # opts.banner = subcommand_usage("[integration] [object] [options]")
852
+ # build_option_type_options(opts, options, update_integration_option_types)
853
+ # build_option_type_options(opts, options, update_integration_advanced_option_types)
854
+ # build_standard_update_options(opts, options)
855
+ # opts.footer = <<-EOT
856
+ # Update an integration.
857
+ # [integration] is required. This is the name or id of an integration.
858
+ # [object] is required. This is the name or id of an integration object.
859
+ # EOT
860
+ # end
861
+ # optparse.parse!(args)
862
+ # verify_args!(args:args, optparse:optparse, count:2)
863
+ # connect(options)
864
+ # integration = find_integration_by_name_or_id(args[0])
865
+ # return 1, "integration not found for #{args[0]}" if integration.nil?
866
+ # integration_object = find_integration_object_by_name_or_id(integration['id'], args[1])
867
+ # return 1, "integration object not found for #{args[1]}" if integration_object.nil?
868
+ # payload = {}
869
+ # if options[:payload]
870
+ # payload = options[:payload]
871
+ # payload.deep_merge!({integration_object_object_key => parse_passed_options(options)})
872
+ # else
873
+ # payload.deep_merge!({integration_object_object_key => parse_passed_options(options)})
874
+ # # do not prompt on update
875
+ # v_prompt = Morpheus::Cli::OptionTypes.no_prompt(update_integration_object_option_types, options[:options], @api_client, options[:params])
876
+ # v_prompt.deep_compact!
877
+ # params.deep_merge!(v_prompt)
878
+ # advanced_config = Morpheus::Cli::OptionTypes.no_prompt(update_integration_object_advanced_option_types, options[:options], @api_client, options[:params])
879
+ # advanced_config.deep_compact!
880
+ # params.deep_merge!(advanced_config)
881
+ # # convert checkbox "on" and "off" to true and false
882
+ # params.booleanize!
883
+ # # massage association params a bit
884
+
885
+ # payload.deep_merge!({integration_object_object_key => params})
886
+ # if payload[integration_object_object_key].empty? # || options[:no_prompt]
887
+ # raise_command_error "Specify at least one option to update.\n#{optparse}"
888
+ # end
889
+ # end
890
+ # @integrations_interface.setopts(options)
891
+ # if options[:dry_run]
892
+ # print_dry_run @integrations_interface.dry.update_object(integration['id'], integration_object['id'], payload)
893
+ # return
894
+ # end
895
+ # json_response = @integrations_interface.update_object(integration['id'], integration_object['id'], payload)
896
+ # integration_object = json_response[integration_object_object_key]
897
+ # render_response(json_response, options, integration_object_object_key) do
898
+ # print_green_success "Updated integration object #{integration_object['name']}"
899
+ # return _get_object(integration, integration_object["id"], {}, options)
900
+ # end
901
+ # return 0, nil
902
+ # end
903
+
904
+ def remove_object(args)
905
+ options = {}
906
+ params = {}
907
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
908
+ opts.banner = subcommand_usage("[integration] [options]")
909
+ build_standard_remove_options(opts, options)
910
+ opts.footer = <<-EOT
911
+ Delete an integration object.
912
+ [integration] is required. This is the name or id of an integration.
913
+ [object] is required. This is the name or id of an integration object.
914
+ EOT
915
+ end
916
+ optparse.parse!(args)
917
+ verify_args!(args:args, optparse:optparse, count:2)
918
+ connect(options)
919
+ integration = find_integration_by_name_or_id(args[0])
920
+ return 1, "integration not found for #{args[0]}" if integration.nil?
921
+ integration_object = find_integration_object_by_name_or_id(integration['id'], args[1])
922
+ return 1, "integration object not found for #{args[1]}" if integration_object.nil?
923
+ params.merge!(parse_query_options(options))
924
+ @integrations_interface.setopts(options)
925
+ if options[:dry_run]
926
+ print_dry_run @integrations_interface.dry.destroy_object(integration['id'], integration_object['id'], params)
927
+ return
928
+ end
929
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the integration object #{integration_object['name']}?")
930
+ return 9, "aborted command"
931
+ end
932
+ json_response = @integrations_interface.destroy_object(integration['id'], integration_object['id'], params)
933
+ render_response(json_response, options) do
934
+ print_green_success "Removed integration object #{integration_object['name']}"
935
+ end
936
+ return 0, nil
937
+ end
938
+
78
939
  private
79
940
 
941
+ def format_integration_type(integration)
942
+ (integration['integrationType']['name'] || integration['integrationType']['code']) rescue integration['integrationType'].to_s
943
+ end
944
+
945
+ def add_integration_option_types
946
+ [
947
+ {'code' => 'integration.type', 'shorthand' => '-t', 'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'optionSource' => lambda { |api_client, api_params|
948
+ # @integration_types_interface.list(max:-1)[integration_list_key].collect {|it|
949
+ get_available_integration_types().collect {|it|
950
+ {'name' => it['name'], 'value' => it['code']}
951
+ } }, 'required' => true, 'description' => "Integration Type code, see `#{command_name} list-types` for available type codes"},
952
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Name of the integration'},
953
+ # {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text'},
954
+ {'fieldName' => 'enabled', 'fieldLabel' => 'Enabled', 'type' => 'checkbox', 'defaultValue' => true, 'description' => 'Can be used to disable an integration'}
955
+ ]
956
+ end
957
+
958
+ def add_integration_advanced_option_types
959
+ []
960
+ end
961
+
962
+ def update_integration_option_types
963
+ list = add_integration_option_types.collect {|it|
964
+ it.delete('required')
965
+ it.delete('defaultValue')
966
+ it
967
+ }
968
+ list = list.reject {|it| ["type"].include? it['fieldName'] }
969
+ list
970
+ end
971
+
972
+ def update_integration_advanced_option_types
973
+ add_integration_advanced_option_types.collect {|it|
974
+ it.delete('required')
975
+ it.delete('defaultValue')
976
+ it
977
+ }
978
+ end
979
+
980
+ def integration_object_key
981
+ 'integration'
982
+ end
983
+
984
+ def integration_list_key
985
+ 'integrations'
986
+ end
987
+
988
+ def find_integration_by_name_or_id(val)
989
+ if val.to_s =~ /\A\d{1,}\Z/
990
+ return find_integration_by_id(val)
991
+ else
992
+ return find_integration_by_name(val)
993
+ end
994
+ end
995
+
996
+ def find_integration_by_id(id)
997
+ begin
998
+ json_response = @integrations_interface.get(id.to_i)
999
+ return json_response[integration_object_key]
1000
+ rescue RestClient::Exception => e
1001
+ if e.response && e.response.code == 404
1002
+ print_red_alert "integration not found by id '#{id}'"
1003
+ else
1004
+ raise e
1005
+ end
1006
+ end
1007
+ end
1008
+
1009
+ def find_integration_by_name(name)
1010
+ json_response = @integrations_interface.list({name: name.to_s})
1011
+ integrations = json_response[integration_list_key]
1012
+ if integrations.empty?
1013
+ print_red_alert "integration not found by name '#{name}'"
1014
+ return nil
1015
+ elsif integrations.size > 1
1016
+ print_red_alert "#{integrations.size} integrations found by name '#{name}'"
1017
+ puts_error as_pretty_table(integrations, [:id, :name], {color:red})
1018
+ print_red_alert "Try using ID instead"
1019
+ print reset,"\n"
1020
+ return nil
1021
+ else
1022
+ return integrations[0]
1023
+ end
1024
+ end
1025
+
80
1026
  def format_integration_status(integration, return_color=cyan)
81
1027
  out = ""
82
1028
  status_string = integration['status']
83
- if integration['enabled'] == false
84
- out << "#{red}DISABLED#{integration['statusMessage'] ? "#{return_color} - #{integration['statusMessage']}" : ''}#{return_color}"
85
- elsif status_string.nil? || status_string.empty? || status_string == "unknown"
1029
+ if status_string.nil? || status_string.empty? || status_string == "unknown"
86
1030
  out << "#{white}UNKNOWN#{integration['statusMessage'] ? "#{return_color} - #{integration['statusMessage']}" : ''}#{return_color}"
1031
+ # elsif integration['enabled'] == false
1032
+ # out << "#{red}DISABLED#{integration['statusMessage'] ? "#{return_color} - #{integration['statusMessage']}" : ''}#{return_color}"
87
1033
  elsif status_string == 'ok'
88
1034
  out << "#{green}#{status_string.upcase}#{return_color}"
89
1035
  elsif status_string == 'error' || status_string == 'offline'
@@ -94,4 +1040,171 @@ class Morpheus::Cli::IntegrationsCommand
94
1040
  out
95
1041
  end
96
1042
 
1043
+
1044
+ def integration_type_column_definitions()
1045
+ {
1046
+ "ID" => 'id',
1047
+ "Code" => 'code',
1048
+ "Name" => 'name',
1049
+ # "Description" => 'description',
1050
+ "Category" => 'category',
1051
+ # "Enabled" => lambda {|it| format_boolean(it['enabled']) },
1052
+ "Creatable" => lambda {|it| format_boolean(it['creatable']) },
1053
+ }
1054
+ end
1055
+
1056
+ def integration_type_object_key
1057
+ 'integrationType'
1058
+ end
1059
+
1060
+ def integration_type_list_key
1061
+ 'integrationTypes'
1062
+ end
1063
+
1064
+ def find_integration_type_by_name_or_code_id(val, params={})
1065
+ if val.to_s =~ /\A\d{1,}\Z/
1066
+ return find_integration_type_by_id(val, params)
1067
+ else
1068
+ return find_integration_type_by_name_or_code(val)
1069
+ end
1070
+ end
1071
+
1072
+ def find_integration_type_by_id(id, params={})
1073
+ begin
1074
+ json_response = @integration_types_interface.get(id.to_i, params)
1075
+ return json_response[integration_object_key]
1076
+ rescue RestClient::Exception => e
1077
+ if e.response && e.response.code == 404
1078
+ print_red_alert "integration not found by id '#{id}'"
1079
+ else
1080
+ raise e
1081
+ end
1082
+ end
1083
+ end
1084
+
1085
+ def find_integration_type_by_name(name, params={})
1086
+ json_response = @integration_types_interface.list(params.merge({name: name.to_s}))
1087
+ integration_types = json_response[integration_type_list_key]
1088
+ if integration_types.empty?
1089
+ print_red_alert "integration type not found by name '#{name}'"
1090
+ return nil
1091
+ elsif integration_types.size > 1
1092
+ print_red_alert "#{integration_types.size} integration types found by name '#{name}'"
1093
+ puts_error as_pretty_table(integration_types, [:id, :code, :name], {color:red})
1094
+ print_red_alert "Try using ID instead"
1095
+ print reset,"\n"
1096
+ return nil
1097
+ else
1098
+ return integration_types[0]
1099
+ end
1100
+ end
1101
+
1102
+ def get_available_integration_types(refresh=false)
1103
+ if !@available_integration_types || refresh
1104
+ @available_integration_types = @integration_types_interface.list(max:10000)[integration_type_list_key]
1105
+ end
1106
+ return @available_integration_types
1107
+ end
1108
+
1109
+ def find_integration_type_by_name_or_code(name)
1110
+ records = get_available_integration_types()
1111
+ record = records.find { |z| z['name'].downcase == name.downcase || z['code'].downcase == name.downcase}
1112
+ record = record ? record : records.find { |z| z['id'].to_s == name.to_s }
1113
+ if record
1114
+ return record
1115
+ else
1116
+ print_red_alert "integration type not found by '#{name}'"
1117
+ return nil
1118
+ end
1119
+ end
1120
+
1121
+ ## Integration Object helpers
1122
+
1123
+ def add_integration_object_option_types
1124
+ [
1125
+ {'code' => 'integrationObject.type', 'shorthand' => '-t', 'switch' => 'type', 'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'optionSource' => 'integrationObjectTypes', 'required' => true, 'description' => "Integration Object Type eg. cloud, layout, blueprint, catalog", 'displayOrder' => 1},
1126
+ # {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => false, 'description' => 'Display Name of the integration object, default is the name of the referenced object', 'displayOrder' => 2},
1127
+ {'dependsOnCode' => 'integrationObject.type:cloud', 'switch' => 'group', 'fieldName' => 'group', 'fieldLabel' => 'Group', 'type' => 'select', 'optionSource' => 'groups', 'required' => true, 'description' => 'Group', 'displayOrder' => 3},
1128
+ {'dependsOnCode' => 'integrationObject.type:cloud', 'switch' => 'cloud', 'fieldName' => 'cloud', 'fieldLabel' => 'Cloud', 'type' => 'select', 'optionSource' => 'clouds', 'required' => true, 'description' => 'Cloud', 'displayOrder' => 4},
1129
+ {'dependsOnCode' => 'integrationObject.type:layout', 'switch' => 'instance-type', 'fieldName' => 'instanceType', 'fieldLabel' => 'Instance Type', 'type' => 'select', 'optionSource' => 'instanceTypes', 'required' => true, 'description' => 'Instance Type', 'displayOrder' => 5},
1130
+ {'dependsOnCode' => 'integrationObject.type:layout', 'switch' => 'technology', 'fieldName' => 'zoneType', 'fieldLabel' => 'Cloud Type', 'type' => 'select', 'optionSource' => 'zoneTypes', 'required' => true, 'description' => 'Cloud Type (Technology)', 'displayOrder' => 5},
1131
+ {'dependsOnCode' => 'integrationObject.type:layout', 'switch' => 'layout', 'fieldName' => 'layout', 'fieldLabel' => 'Layout', 'type' => 'select', 'optionSource' => 'layouts', 'required' => true, 'description' => 'Layout', 'displayOrder' => 6},
1132
+ {'dependsOnCode' => 'integrationObject.type:blueprint', 'fieldName' => 'name', 'fieldLabel' => 'Catalog Item Name', 'type' => 'text', 'required' => true, 'description' => 'Display Name of the integration object', 'displayOrder' => 7},
1133
+ {'dependsOnCode' => 'integrationObject.type:blueprint', 'switch' => 'blueprint', 'fieldName' => 'blueprint', 'fieldLabel' => 'Blueprint', 'type' => 'select', 'optionSource' => 'blueprints', 'required' => true, 'description' => 'Blueprint', 'displayOrder' => 8, 'noParams' => true},
1134
+ {'dependsOnCode' => 'integrationObject.type:blueprint', 'switch' => 'group', 'fieldName' => 'group', 'fieldLabel' => 'Group', 'type' => 'select', 'optionSource' => 'groups', 'required' => true, 'description' => 'Group', 'displayOrder' => 9},
1135
+ {'dependsOnCode' => 'integrationObject.type:blueprint', 'switch' => 'default-cloud', 'fieldName' => 'defaultCloud', 'fieldLabel' => 'Default Cloud', 'type' => 'select', 'optionSource' => 'clouds', 'required' => false, 'description' => 'Default Cloud', 'displayOrder' => 10},
1136
+ {'dependsOnCode' => 'integrationObject.type:blueprint', 'switch' => 'environment', 'fieldName' => 'environment', 'fieldLabel' => 'Environment', 'type' => 'select', 'optionSource' => 'environments', 'required' => false, 'description' => 'Environment', 'displayOrder' => 11},
1137
+ {'dependsOnCode' => 'integrationObject.type:blueprint', 'switch' => 'config', 'fieldName' => 'config', 'fieldLabel' => 'Config', 'type' => 'code-editor', 'required' => true, 'description' => 'Config JSON', 'displayOrder' => 12},
1138
+ {'dependsOnCode' => 'integrationObject.type:catalog', 'switch' => 'catalog', 'fieldName' => 'catalog', 'fieldLabel' => 'Catalog Item', 'type' => 'select', 'optionSource' => 'catalogItemTypes', 'required' => true, 'description' => 'Catalog Item', 'displayOrder' => 13},
1139
+ ]
1140
+ end
1141
+
1142
+ def add_integration_object_advanced_option_types
1143
+ []
1144
+ end
1145
+
1146
+ def update_integration_object_option_types
1147
+ list = add_integration_object_option_types.collect {|it|
1148
+ it.delete('required')
1149
+ it.delete('defaultValue')
1150
+ it
1151
+ }
1152
+ list = list.reject {|it| ["type"].include? it['fieldName'] }
1153
+ list
1154
+ end
1155
+
1156
+ def update_integration_object_advanced_option_types
1157
+ add_integration_advanced_option_types.collect {|it|
1158
+ it.delete('required')
1159
+ it.delete('defaultValue')
1160
+ it
1161
+ }
1162
+ end
1163
+
1164
+ def integration_object_object_key
1165
+ 'object'
1166
+ end
1167
+
1168
+ def integration_object_list_key
1169
+ 'objects'
1170
+ end
1171
+
1172
+ def find_integration_object_by_name_or_id(integration_id, val)
1173
+ if val.to_s =~ /\A\d{1,}\Z/
1174
+ return find_integration_object_by_id(integration_id, val)
1175
+ else
1176
+ return find_integration_object_by_name(integration_id, val)
1177
+ end
1178
+ end
1179
+
1180
+ def find_integration_object_by_id(integration_id, id)
1181
+ begin
1182
+ json_response = @integrations_interface.get_object(integration_id, id.to_i)
1183
+ return json_response[integration_object_object_key]
1184
+ rescue RestClient::Exception => e
1185
+ if e.response && e.response.code == 404
1186
+ print_red_alert "integration object not found by id '#{id}'"
1187
+ else
1188
+ raise e
1189
+ end
1190
+ end
1191
+ end
1192
+
1193
+ def find_integration_object_by_name(integration_id, name)
1194
+ json_response = @integrations_interface.list_objects(integration_id, {name: name.to_s})
1195
+ integration_objects = json_response[integration_object_list_key]
1196
+ if integration_objects.empty?
1197
+ print_red_alert "integration object not found by name '#{name}'"
1198
+ return nil
1199
+ elsif integration_objects.size > 1
1200
+ print_red_alert "#{integration_objects.size} integration object found by name '#{name}'"
1201
+ puts_error as_pretty_table(integration_objects, [:id, :name], {color:red})
1202
+ print_red_alert "Try using ID instead"
1203
+ print reset,"\n"
1204
+ return nil
1205
+ else
1206
+ return integration_objects[0]
1207
+ end
1208
+ end
1209
+
97
1210
  end