morpheus-cli 5.3.0 → 5.3.1.1

Sign up to get free protection for your applications and to get access to all the features.
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 +0 -6
  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 +7 -4
  133. data/lib/morpheus/cli/commands/standard/history_command.rb +4 -5
  134. data/lib/morpheus/cli/commands/standard/man_command.rb +4 -5
  135. data/lib/morpheus/cli/credentials.rb +4 -11
  136. data/lib/morpheus/cli/environments_command.rb +1 -1
  137. data/lib/morpheus/cli/execute_schedules_command.rb +3 -3
  138. data/lib/morpheus/cli/hosts.rb +253 -232
  139. data/lib/morpheus/cli/image_builder_command.rb +6 -6
  140. data/lib/morpheus/cli/instance_types.rb +1 -1
  141. data/lib/morpheus/cli/instances.rb +196 -186
  142. data/lib/morpheus/cli/integrations_command.rb +1155 -42
  143. data/lib/morpheus/cli/invoices_command.rb +75 -67
  144. data/lib/morpheus/cli/key_pairs.rb +2 -2
  145. data/lib/morpheus/cli/library_container_scripts_command.rb +1 -1
  146. data/lib/morpheus/cli/library_container_templates_command.rb +1 -1
  147. data/lib/morpheus/cli/library_container_types_command.rb +6 -6
  148. data/lib/morpheus/cli/library_instance_types_command.rb +4 -4
  149. data/lib/morpheus/cli/library_layouts_command.rb +5 -5
  150. data/lib/morpheus/cli/library_option_lists_command.rb +4 -4
  151. data/lib/morpheus/cli/library_option_types_command.rb +4 -4
  152. data/lib/morpheus/cli/library_upgrades_command.rb +6 -6
  153. data/lib/morpheus/cli/license.rb +2 -2
  154. data/lib/morpheus/cli/load_balancers.rb +1 -1
  155. data/lib/morpheus/cli/login.rb +10 -1
  156. data/lib/morpheus/cli/mixins/option_source_helper.rb +15 -16
  157. data/lib/morpheus/cli/mixins/print_helper.rb +33 -18
  158. data/lib/morpheus/cli/mixins/provisioning_helper.rb +4 -4
  159. data/lib/morpheus/cli/mixins/vdi_helper.rb +246 -0
  160. data/lib/morpheus/cli/network_domains_command.rb +2 -2
  161. data/lib/morpheus/cli/network_routers_command.rb +22 -9
  162. data/lib/morpheus/cli/networks_command.rb +2 -2
  163. data/lib/morpheus/cli/option_types.rb +39 -34
  164. data/lib/morpheus/cli/policies_command.rb +0 -1
  165. data/lib/morpheus/cli/power_schedules_command.rb +3 -3
  166. data/lib/morpheus/cli/preseed_scripts_command.rb +1 -1
  167. data/lib/morpheus/cli/remote.rb +2 -2
  168. data/lib/morpheus/cli/reports_command.rb +5 -2
  169. data/lib/morpheus/cli/roles.rb +224 -64
  170. data/lib/morpheus/cli/security_group_rules.rb +1 -1
  171. data/lib/morpheus/cli/service_plans_command.rb +4 -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