morpheus-cli 9.0.1 → 9.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/affinity_groups_interface.rb +46 -0
  4. data/lib/morpheus/api/api_client.rb +17 -0
  5. data/lib/morpheus/api/clouds_interface.rb +42 -0
  6. data/lib/morpheus/api/clusters_interface.rb +55 -1
  7. data/lib/morpheus/api/distributed_workers_interface.rb +41 -0
  8. data/lib/morpheus/api/host_vm_groups_interface.rb +37 -0
  9. data/lib/morpheus/api/servers_interface.rb +8 -0
  10. data/lib/morpheus/api/virtual_switches_interface.rb +9 -0
  11. data/lib/morpheus/cli/commands/affinity_groups_command.rb +554 -0
  12. data/lib/morpheus/cli/commands/backups_command.rb +11 -0
  13. data/lib/morpheus/cli/commands/clouds.rb +421 -29
  14. data/lib/morpheus/cli/commands/clusters.rb +507 -20
  15. data/lib/morpheus/cli/commands/distributed_workers_command.rb +282 -0
  16. data/lib/morpheus/cli/commands/host_vm_groups_command.rb +423 -0
  17. data/lib/morpheus/cli/commands/hosts.rb +72 -1
  18. data/lib/morpheus/cli/commands/instances.rb +12 -1
  19. data/lib/morpheus/cli/commands/networks_command.rb +35 -3
  20. data/lib/morpheus/cli/commands/virtual_switches_command.rb +336 -0
  21. data/lib/morpheus/cli/mixins/affinity_helper.rb +118 -0
  22. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +143 -4
  23. data/lib/morpheus/cli/option_types.rb +36 -1
  24. data/lib/morpheus/cli/version.rb +1 -1
  25. data/test/api/affinity_groups_interface_test.rb +43 -0
  26. data/test/api/clouds_interface_test.rb +67 -0
  27. data/test/api/host_vm_groups_interface_test.rb +35 -0
  28. data/test/cli/affinity_groups_cloud_cluster_scope_test.rb +270 -0
  29. data/test/cli/affinity_groups_cluster_scope_test.rb +171 -0
  30. data/test/cli/affinity_groups_test.rb +57 -0
  31. data/test/cli/affinity_groups_violations_scope_test.rb +141 -0
  32. data/test/cli/affinity_helper_test.rb +367 -0
  33. data/test/cli/backups_test.rb +212 -0
  34. data/test/cli/clusters_uuid_test.rb +131 -0
  35. data/test/cli/distributed_workers_test.rb +68 -0
  36. data/test/cli/host_vm_groups_cluster_scope_test.rb +187 -0
  37. data/test/cli/host_vm_groups_test.rb +63 -0
  38. data/test/cli/infrastructure_helper_cluster_resolver_test.rb +250 -0
  39. metadata +39 -2
@@ -0,0 +1,282 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::DistributedWorkers
4
+ include Morpheus::Cli::CliCommand
5
+
6
+ register_subcommands :list, :get, :add, :update, :remove
7
+
8
+ def connect(opts)
9
+ @api_client = establish_remote_appliance_connection(opts)
10
+ @distributed_workers_interface = @api_client.distributed_workers
11
+ end
12
+
13
+ def handle(args)
14
+ handle_subcommand(args)
15
+ end
16
+
17
+ def list(args)
18
+ options = {}
19
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
20
+ opts.banner = subcommand_usage()
21
+ build_standard_list_options(opts, options)
22
+ opts.footer = "List distributed workers."
23
+ end
24
+ optparse.parse!(args)
25
+ connect(options)
26
+ verify_args!(args:args, optparse:optparse, count:0)
27
+ params = parse_list_options(options)
28
+ @distributed_workers_interface.setopts(options)
29
+ if options[:dry_run]
30
+ print_dry_run @distributed_workers_interface.dry.list(params)
31
+ return
32
+ end
33
+ json_response = @distributed_workers_interface.list(params)
34
+ render_response(json_response, options, 'distributedWorkers') do
35
+ distributed_workers = json_response['distributedWorkers']
36
+ print_h1 "Morpheus Distributed Workers", parse_list_subtitles(options), options
37
+ if distributed_workers.empty?
38
+ print cyan,"No distributed workers found.",reset,"\n"
39
+ else
40
+ rows = distributed_workers.collect do |distributed_worker|
41
+ {
42
+ id: distributed_worker['id'],
43
+ name: distributed_worker['name'],
44
+ enabled: format_boolean(distributed_worker['enabled']),
45
+ proxyPort: distributed_worker['proxyPort'],
46
+ proxyHostList: distributed_worker['proxyHostList'],
47
+ applianceUrl: distributed_worker['applianceUrl']
48
+ }
49
+ end
50
+ columns = [:id, :name, :enabled, {:proxyPort => {:display_name => "Proxy Port"} }, {:proxyHostList => {:display_name => "Proxy Host List"} }, {:applianceUrl => {:display_name => "Appliance URL"} }]
51
+ print as_pretty_table(rows, columns, options)
52
+ print_results_pagination(json_response)
53
+ end
54
+ print reset,"\n"
55
+ end
56
+ return 0, nil
57
+ end
58
+
59
+ def get(args)
60
+ options = {}
61
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
62
+ opts.banner = subcommand_usage("[distributed-worker]")
63
+ build_standard_get_options(opts, options)
64
+ opts.footer = "Get details about a distributed worker.\n[distributed-worker] is required. This is the name or id of a distributed worker."
65
+ end
66
+ optparse.parse!(args)
67
+ verify_args!(args:args, optparse:optparse, count:1)
68
+ connect(options)
69
+ _get(args[0], options)
70
+ end
71
+
72
+ def _get(id, options)
73
+ @distributed_workers_interface.setopts(options)
74
+ if options[:dry_run]
75
+ if id.to_s =~ /\A\d{1,}\Z/
76
+ print_dry_run @distributed_workers_interface.dry.get(id.to_i)
77
+ else
78
+ print_dry_run @distributed_workers_interface.dry.list({name: id})
79
+ end
80
+ return
81
+ end
82
+ distributed_worker = find_distributed_worker_by_name_or_id(id)
83
+ return 1 if distributed_worker.nil?
84
+ json_response = {'distributedWorker' => distributed_worker}
85
+ render_response(json_response, options, 'distributedWorker') do
86
+ render_distributed_worker_details(distributed_worker)
87
+ end
88
+ return 0, nil
89
+ end
90
+
91
+ def add(args)
92
+ options = {}
93
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
94
+ opts.banner = subcommand_usage("[name] [options]")
95
+ build_option_type_options(opts, options, add_distributed_worker_option_types)
96
+ build_standard_add_options(opts, options)
97
+ opts.footer = <<-EOT
98
+ Add a distributed worker.
99
+ [name] is required. This is the name of the new distributed worker.
100
+ The generated apiKey is only shown once, on creation. Be sure to save it.
101
+ EOT
102
+ end
103
+ optparse.parse!(args)
104
+ verify_args!(args:args, optparse:optparse, max:1)
105
+ connect(options)
106
+ options[:options] ||= {}
107
+ if args[0]
108
+ options[:options]['name'] ||= args[0]
109
+ end
110
+ payload = parse_payload(options, 'distributedWorker')
111
+ if payload.nil?
112
+ params = Morpheus::Cli::OptionTypes.prompt(add_distributed_worker_option_types, options[:options], @api_client, options[:params])
113
+ payload = {'distributedWorker' => params}
114
+ end
115
+ @distributed_workers_interface.setopts(options)
116
+ if options[:dry_run]
117
+ print_dry_run @distributed_workers_interface.dry.create(payload)
118
+ return
119
+ end
120
+ json_response = @distributed_workers_interface.create(payload)
121
+ render_response(json_response, options, 'distributedWorker') do
122
+ distributed_worker = json_response['distributedWorker']
123
+ print_green_success "Added distributed worker #{distributed_worker['name']}"
124
+ api_key = distributed_worker['apiKey']
125
+ if api_key
126
+ print "\n"
127
+ print_h2 "API Key"
128
+ print yellow, "Save this API Key now. It will not be shown again.", reset, "\n"
129
+ print cyan, api_key, reset, "\n"
130
+ end
131
+ render_distributed_worker_details(distributed_worker)
132
+ end
133
+ return 0, nil
134
+ end
135
+
136
+ def update(args)
137
+ options = {}
138
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
139
+ opts.banner = subcommand_usage("[distributed-worker] [options]")
140
+ build_option_type_options(opts, options, update_distributed_worker_option_types)
141
+ build_standard_update_options(opts, options)
142
+ opts.footer = <<-EOT
143
+ Update a distributed worker.
144
+ [distributed-worker] is required. This is the name or id of a distributed worker.
145
+ EOT
146
+ end
147
+ optparse.parse!(args)
148
+ verify_args!(args:args, optparse:optparse, count:1)
149
+ connect(options)
150
+ distributed_worker = find_distributed_worker_by_name_or_id(args[0])
151
+ return 1 if distributed_worker.nil?
152
+ payload = parse_payload(options, 'distributedWorker')
153
+ if payload.nil?
154
+ params = options[:options] || {}
155
+ params = params.select {|k,v| params[k].to_s != "" }
156
+ if params.empty?
157
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
158
+ end
159
+ payload = {'distributedWorker' => params}
160
+ end
161
+ payload['distributedWorker']['id'] = distributed_worker['id']
162
+ @distributed_workers_interface.setopts(options)
163
+ if options[:dry_run]
164
+ print_dry_run @distributed_workers_interface.dry.update(distributed_worker['id'], payload)
165
+ return
166
+ end
167
+ json_response = @distributed_workers_interface.update(distributed_worker['id'], payload)
168
+ render_response(json_response, options, 'distributedWorker') do
169
+ print_green_success "Updated distributed worker #{distributed_worker['name']}"
170
+ _get(distributed_worker['id'], options)
171
+ end
172
+ return 0, nil
173
+ end
174
+
175
+ def remove(args)
176
+ options = {}
177
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
178
+ opts.banner = subcommand_usage("[distributed-worker]")
179
+ build_standard_remove_options(opts, options)
180
+ opts.footer = <<-EOT
181
+ Delete a distributed worker.
182
+ [distributed-worker] is required. This is the name or id of a distributed worker.
183
+ EOT
184
+ end
185
+ optparse.parse!(args)
186
+ verify_args!(args:args, optparse:optparse, count:1)
187
+ connect(options)
188
+ distributed_worker = find_distributed_worker_by_name_or_id(args[0])
189
+ return 1 if distributed_worker.nil?
190
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the distributed worker #{distributed_worker['name']}?", options)
191
+ return 9, "aborted command"
192
+ end
193
+ @distributed_workers_interface.setopts(options)
194
+ if options[:dry_run]
195
+ print_dry_run @distributed_workers_interface.dry.destroy(distributed_worker['id'])
196
+ return
197
+ end
198
+ json_response = @distributed_workers_interface.destroy(distributed_worker['id'])
199
+ render_response(json_response, options, 'distributedWorker') do
200
+ print_green_success "Removed distributed worker #{distributed_worker['name']}"
201
+ end
202
+ return 0, nil
203
+ end
204
+
205
+ private
206
+
207
+ def render_distributed_worker_details(distributed_worker)
208
+ print_h1 "Distributed Worker Details"
209
+ print cyan
210
+ description_cols = {
211
+ "ID" => 'id',
212
+ "Name" => 'name',
213
+ "Description" => 'description',
214
+ "Enabled" => lambda {|it| format_boolean(it['enabled']) },
215
+ "Active" => lambda {|it| format_boolean(it['active']) },
216
+ "Proxy Port" => 'proxyPort',
217
+ "Proxy Host List" => 'proxyHostList',
218
+ "Appliance URL" => 'applianceUrl',
219
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
220
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
221
+ }
222
+ print_description_list(description_cols, distributed_worker)
223
+ print reset,"\n"
224
+ end
225
+
226
+ def find_distributed_worker_by_name_or_id(val)
227
+ if val.to_s =~ /\A\d{1,}\Z/
228
+ return find_distributed_worker_by_id(val)
229
+ else
230
+ return find_distributed_worker_by_name(val)
231
+ end
232
+ end
233
+
234
+ def find_distributed_worker_by_id(id)
235
+ begin
236
+ json_response = @distributed_workers_interface.get(id.to_i)
237
+ return json_response['distributedWorker']
238
+ rescue RestClient::Exception => e
239
+ if e.response && e.response.code == 404
240
+ print_red_alert "Distributed Worker not found by id #{id}"
241
+ return nil
242
+ else
243
+ raise e
244
+ end
245
+ end
246
+ end
247
+
248
+ def find_distributed_worker_by_name(name)
249
+ distributed_workers = @distributed_workers_interface.list({name: name.to_s})['distributedWorkers']
250
+ if distributed_workers.empty?
251
+ print_red_alert "Distributed Worker not found by name #{name}"
252
+ return nil
253
+ elsif distributed_workers.size > 1
254
+ print_red_alert "#{distributed_workers.size} distributed workers found by name #{name}"
255
+ rows = distributed_workers.collect do |it|
256
+ {id: it['id'], name: it['name']}
257
+ end
258
+ print "\n"
259
+ puts as_pretty_table(rows, [:id, :name], {color: red})
260
+ return nil
261
+ else
262
+ return distributed_workers[0]
263
+ end
264
+ end
265
+
266
+ def add_distributed_worker_option_types
267
+ [
268
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1, 'description' => 'A unique name for the distributed worker.'},
269
+ {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
270
+ {'fieldName' => 'enabled', 'fieldLabel' => 'Enabled', 'type' => 'checkbox', 'required' => false, 'defaultValue' => true, 'displayOrder' => 3},
271
+ {'fieldName' => 'proxyHostList', 'fieldLabel' => 'Proxy Host List', 'type' => 'text', 'required' => false, 'displayOrder' => 4, 'description' => 'A comma or space separated list of proxy hosts.'},
272
+ {'fieldName' => 'applianceUrl', 'fieldLabel' => 'Appliance URL', 'type' => 'text', 'required' => false, 'displayOrder' => 5},
273
+ ]
274
+ end
275
+
276
+ def update_distributed_worker_option_types
277
+ list = add_distributed_worker_option_types
278
+ list.each {|it| it['required'] = false; it.delete('defaultValue') }
279
+ list
280
+ end
281
+
282
+ end