morpheus-cli 5.3.2.1 → 5.3.4

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +12 -0
  4. data/lib/morpheus/api/clouds_interface.rb +4 -11
  5. data/lib/morpheus/api/instances_interface.rb +18 -5
  6. data/lib/morpheus/api/load_balancer_pools_interface.rb +4 -4
  7. data/lib/morpheus/api/load_balancer_profiles_interface.rb +10 -0
  8. data/lib/morpheus/api/load_balancer_virtual_servers_interface.rb +4 -4
  9. data/lib/morpheus/api/network_routers_interface.rb +21 -0
  10. data/lib/morpheus/api/network_servers_interface.rb +42 -0
  11. data/lib/morpheus/api/rest_interface.rb +2 -1
  12. data/lib/morpheus/api/virtual_images_interface.rb +23 -2
  13. data/lib/morpheus/api/virtual_servers_interface.rb +9 -0
  14. data/lib/morpheus/cli/apps.rb +3 -2
  15. data/lib/morpheus/cli/cli_command.rb +14 -6
  16. data/lib/morpheus/cli/cli_registry.rb +55 -2
  17. data/lib/morpheus/cli/cloud_resource_pools_command.rb +170 -134
  18. data/lib/morpheus/cli/clouds.rb +22 -40
  19. data/lib/morpheus/cli/clusters.rb +51 -33
  20. data/lib/morpheus/cli/hosts.rb +0 -1
  21. data/lib/morpheus/cli/instances.rb +372 -150
  22. data/lib/morpheus/cli/invoices_command.rb +117 -133
  23. data/lib/morpheus/cli/library_cluster_layouts_command.rb +20 -0
  24. data/lib/morpheus/cli/library_option_lists_command.rb +3 -3
  25. data/lib/morpheus/cli/load_balancer_pools.rb +111 -0
  26. data/lib/morpheus/cli/load_balancer_virtual_servers.rb +136 -0
  27. data/lib/morpheus/cli/load_balancers.rb +0 -155
  28. data/lib/morpheus/cli/mixins/load_balancers_helper.rb +2 -2
  29. data/lib/morpheus/cli/mixins/provisioning_helper.rb +155 -112
  30. data/lib/morpheus/cli/mixins/rest_command.rb +53 -37
  31. data/lib/morpheus/cli/mixins/secondary_rest_command.rb +488 -0
  32. data/lib/morpheus/cli/monitoring_checks_command.rb +2 -0
  33. data/lib/morpheus/cli/network_routers_command.rb +291 -7
  34. data/lib/morpheus/cli/network_scopes_command.rb +442 -0
  35. data/lib/morpheus/cli/networks_command.rb +3 -3
  36. data/lib/morpheus/cli/option_parser.rb +25 -17
  37. data/lib/morpheus/cli/option_types.rb +42 -15
  38. data/lib/morpheus/cli/subnets_command.rb +7 -2
  39. data/lib/morpheus/cli/tasks.rb +25 -2
  40. data/lib/morpheus/cli/vdi_pools_command.rb +4 -1
  41. data/lib/morpheus/cli/version.rb +1 -1
  42. data/lib/morpheus/cli/virtual_images.rb +251 -29
  43. data/lib/morpheus/cli.rb +9 -1
  44. data/morpheus-cli.gemspec +1 -1
  45. metadata +11 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e27f28e852866d25457815c852eec19a3f65bd3b0ae8a33d8e055d990f631e79
4
- data.tar.gz: 5270f5c81e872491cbf9e1e59f33cf78bf233c152c1347fa61cb01c544073e34
3
+ metadata.gz: 7e1172b58bb99b6348f15121c97f5c3d8db818c25fa977cd33835f7a3645da44
4
+ data.tar.gz: bd216d539aadf61f968cee0dabb6d36361bd79dbdd4ac86527a80872f64620ec
5
5
  SHA512:
6
- metadata.gz: 172818b1030b243ebb2c9c7e316b0cefe173b5a198ff8184f7c0b7bce275954e80109b45c426530ef1587635199e6cc17dedc248011cc11626e2d0f5e8f3f1c1
7
- data.tar.gz: 45bdeca37c208f7e7889bd9ab87b0012fea6623faf1b80628fef2ad0df55182388c8e1cd647f9fdd51628d48a4d6cca715e718bb655c8612683fe4ffc9ede3f8
6
+ metadata.gz: 24e82961eae144b2b9a00f555313ae653b6c1f844a9be549553481c7e91789011858075acfc76efabfeb3a503f211f4ddc76c79a8a33cff1d4bf07a355179753
7
+ data.tar.gz: 0cf97e82534466142e81d7a20ff7672578de00179cfab36a1e94e6900a24193090e1dd1c91d6c6d409000ac92623ed06ae9e6f5f346937c2912b42b5fedac561
data/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  FROM ruby:2.5.1
2
2
 
3
- RUN gem install morpheus-cli -v 5.3.2.1
3
+ RUN gem install morpheus-cli -v 5.3.4
4
4
 
5
5
  ENTRYPOINT ["morpheus"]
@@ -486,6 +486,10 @@ class Morpheus::APIClient
486
486
  Morpheus::LoadBalancerPoolsInterface.new(common_interface_options).setopts(@options)
487
487
  end
488
488
 
489
+ def virtual_servers
490
+ Morpheus::VirtualServersInterface.new(common_interface_options).setopts(@options)
491
+ end
492
+
489
493
  def tasks
490
494
  Morpheus::TasksInterface.new(common_interface_options).setopts(@options)
491
495
  end
@@ -836,6 +840,14 @@ class Morpheus::APIClient
836
840
  Morpheus::VdiGatewaysInterface.new(common_interface_options).setopts(@options)
837
841
  end
838
842
 
843
+ def network_servers
844
+ Morpheus::NetworkServersInterface.new(common_interface_options).setopts(@options)
845
+ end
846
+
847
+ def rest(endpoint)
848
+ Morpheus::RestInterface.new(common_interface_options).setopts(@options.merge({base_path: "#{@base_url}/api/#{endpoint}"}))
849
+ end
850
+
839
851
  # add new interfaces here
840
852
 
841
853
  protected
@@ -24,17 +24,10 @@ class Morpheus::CloudsInterface < Morpheus::APIClient
24
24
  execute(opts)
25
25
  end
26
26
 
27
- def get(params=nil)
28
- url = "#{@base_url}/api/zones"
29
- headers = { params: {}, authorization: "Bearer #{@access_token}" }
30
-
31
- if params.is_a?(Hash)
32
- headers[:params].merge!(params)
33
- elsif params.is_a?(Numeric)
34
- url = "#{@base_url}/api/zones/#{params}"
35
- elsif params.is_a?(String)
36
- headers[:params]['name'] = params
37
- end
27
+ def get(id, params={})
28
+ validate_id!(id)
29
+ url = "#{@base_url}/api/zones/#{id}"
30
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
38
31
  opts = {method: :get, url: url, headers: headers}
39
32
  execute(opts)
40
33
  end
@@ -57,11 +57,24 @@ class Morpheus::InstancesInterface < Morpheus::APIClient
57
57
  execute(opts)
58
58
  end
59
59
 
60
- def cancel_removal(id, params = {})
61
- url = "#{@base_url}/api/instances/#{id}/cancel-removal"
62
- headers = {:params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
63
- opts = {method: :put, url: url, headers: headers}
64
- execute(opts)
60
+ def extend_shutdown(id, params={}, payload={}, headers={})
61
+ execute(method: :put, url: "#{base_path}/#{id}/extend-shutdown", params: params, payload: payload, headers: headers)
62
+ end
63
+
64
+ def extend_expiration(id, params={}, payload={}, headers={})
65
+ execute(method: :put, url: "#{base_path}/#{id}/extend-expiration", params: params, payload: payload, headers: headers)
66
+ end
67
+
68
+ def cancel_shutdown(id, params={}, payload={}, headers={})
69
+ execute(method: :put, url: "#{base_path}/#{id}/cancel-shutdown", params: params, payload: payload, headers: headers)
70
+ end
71
+
72
+ def cancel_expiration(id, params={}, payload={}, headers={})
73
+ execute(method: :put, url: "#{base_path}/#{id}/cancel-expiration", params: params, payload: payload, headers: headers)
74
+ end
75
+
76
+ def cancel_removal(id, params={}, payload={}, headers={})
77
+ execute(method: :put, url: "#{base_path}/#{id}/cancel-removal", params: params, payload: payload, headers: headers)
65
78
  end
66
79
 
67
80
  def stop(id, params={})
@@ -1,9 +1,9 @@
1
- require 'morpheus/api/secondary_rest_interface'
1
+ require 'morpheus/api/rest_interface'
2
2
 
3
- class Morpheus::LoadBalancerPoolsInterface < Morpheus::SecondaryRestInterface
3
+ class Morpheus::LoadBalancerPoolsInterface < Morpheus::RestInterface
4
4
 
5
- def base_path(load_balancer_id)
6
- "/api/load-balancers/#{load_balancer_id}/pools"
5
+ def base_path
6
+ "/api/load-balancer-pools"
7
7
  end
8
8
 
9
9
  end
@@ -0,0 +1,10 @@
1
+ require 'morpheus/api/rest_interface'
2
+
3
+ class Morpheus::LoadBalancerProfilesInterface < Morpheus::RestInterface
4
+
5
+ def base_path
6
+ "/api/load-balancer-profiles"
7
+ end
8
+
9
+ end
10
+
@@ -1,9 +1,9 @@
1
- require 'morpheus/api/secondary_rest_interface'
1
+ require 'morpheus/api/rest_interface'
2
2
 
3
- class Morpheus::LoadBalancerVirtualServersInterface < Morpheus::SecondaryRestInterface
3
+ class Morpheus::LoadBalancerVirtualServersInterface < Morpheus::RestInterface
4
4
 
5
- def base_path(load_balancer_id)
6
- "/api/load-balancers/#{load_balancer_id}/virtual-servers"
5
+ def base_path
6
+ "/api/load-balancer-virtual-servers"
7
7
  end
8
8
 
9
9
  end
@@ -143,6 +143,27 @@ class Morpheus::NetworkRoutersInterface < Morpheus::APIClient
143
143
  execute(opts)
144
144
  end
145
145
 
146
+ def create_bgp_neighbor(router_id, payload={})
147
+ url = "#{@base_url}/api/networks/routers/#{router_id}/bgp-neighbors"
148
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
149
+ opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
150
+ execute(opts)
151
+ end
152
+
153
+ def update_bgp_neighbor(router_id, nat_id, payload={})
154
+ url = "#{@base_url}/api/networks/routers/#{router_id}/bgp-neighbors/#{nat_id}"
155
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
156
+ opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
157
+ execute(opts)
158
+ end
159
+
160
+ def destroy_bgp_neighbor(router_id, nat_id, payload={})
161
+ url = "#{@base_url}/api/networks/routers/#{router_id}/bgp-neighbors/#{nat_id}"
162
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
163
+ opts = {method: :delete, url: url, headers: headers, payload: payload.to_json}
164
+ execute(opts)
165
+ end
166
+
146
167
  def update_permissions(router_id, payload)
147
168
  url = "#{@base_url}/api/networks/routers/#{router_id}/permissions"
148
169
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
@@ -0,0 +1,42 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ class Morpheus::NetworkServersInterface < Morpheus::RestInterface
4
+
5
+ def base_path
6
+ "/api/networks/servers"
7
+ end
8
+
9
+ def list_scopes(server_id, params={}, headers={})
10
+ validate_id!(server_id)
11
+ execute(method: :get, url: "#{base_path}/#{server_id}/scopes", params: params, headers: headers)
12
+ end
13
+
14
+ def get_scope(server_id, scope_id, params={}, headers={})
15
+ validate_id!(server_id)
16
+ validate_id!(scope_id)
17
+ execute(method: :get, url: "#{base_path}/#{server_id}/scopes/#{scope_id}", params: params, headers: headers)
18
+ end
19
+
20
+ def create_scope(server_id, payload, params={}, headers={})
21
+ validate_id!(server_id)
22
+ execute(method: :post, url: "#{base_path}/#{server_id}/scopes", params: params, payload: payload, headers: headers)
23
+ end
24
+
25
+ def update_scope(server_id, scope_id, payload, params={}, headers={})
26
+ validate_id!(server_id)
27
+ validate_id!(scope_id)
28
+ execute(method: :put, url: "#{base_path}/#{server_id}/scopes/#{scope_id}", params: params, payload: payload, headers: headers)
29
+ end
30
+
31
+ def destroy_scope(server_id, scope_id, params={}, headers={})
32
+ validate_id!(server_id)
33
+ validate_id!(scope_id)
34
+ execute(method: :delete, url: "#{base_path}/#{server_id}/scopes/#{scope_id}", params: params, headers: headers)
35
+ end
36
+
37
+ def update_scope_permissions(server_id, scope_id, payload, params={}, headers={})
38
+ validate_id!(server_id)
39
+ validate_id!(scope_id)
40
+ execute(method: :put, url: "#{base_path}/#{server_id}/scopes/#{scope_id}", payload: payload.to_json, params: params, headers: headers)
41
+ end
42
+ end
@@ -7,7 +7,8 @@ class Morpheus::RestInterface < Morpheus::APIClient
7
7
  # subclasses should override in your interface
8
8
  # Example: "/api/things"
9
9
  def base_path
10
- raise "#{self.class} has not defined base_path!"
10
+ raise "#{self.class} has not defined base_path!" if @options[:base_path].nil?
11
+ @options[:base_path]
11
12
  end
12
13
 
13
14
  def list(params={}, headers={})
@@ -45,10 +45,10 @@ class Morpheus::VirtualImagesInterface < Morpheus::APIClient
45
45
  execute(method: :put, url: url, headers: headers, payload: payload.to_json)
46
46
  end
47
47
 
48
- def destroy(id)
48
+ def destroy(id, params={})
49
49
  url = "#{@base_url}/api/virtual-images/#{id}"
50
50
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
51
- execute(method: :delete, url: url, headers: headers)
51
+ execute(method: :delete, url: url, params: params, headers: headers)
52
52
  end
53
53
 
54
54
  # multipart file upload
@@ -165,4 +165,25 @@ class Morpheus::VirtualImagesInterface < Morpheus::APIClient
165
165
  execute(method: :delete, url: url, headers: headers)
166
166
  end
167
167
 
168
+ def location_base_path(resource_id)
169
+ "/api/virtual-images/#{resource_id}/locations"
170
+ end
171
+
172
+ def list_locations(resource_id, params={}, headers={})
173
+ validate_id!(resource_id)
174
+ execute(method: :get, url: "#{location_base_path(resource_id)}", params: params, headers: headers)
175
+ end
176
+
177
+ def get_location(resource_id, id, params={}, headers={})
178
+ validate_id!(resource_id)
179
+ validate_id!(id)
180
+ execute(method: :get, url: "#{location_base_path(resource_id)}/#{id}", params: params, headers: headers)
181
+ end
182
+
183
+ def destroy_location(resource_id, id, params = {}, headers={})
184
+ validate_id!(resource_id)
185
+ validate_id!(id)
186
+ execute(method: :delete, url: "#{location_base_path(resource_id)}/#{id}", params: params, headers: headers)
187
+ end
188
+
168
189
  end
@@ -0,0 +1,9 @@
1
+ require 'morpheus/api/rest_interface'
2
+
3
+ class Morpheus::VirtualServersInterface < Morpheus::RestInterface
4
+
5
+ def base_path
6
+ "/api/load-balancer-virtual-servers"
7
+ end
8
+
9
+ end
@@ -438,7 +438,7 @@ class Morpheus::Cli::Apps
438
438
  instance_prompt_options[:default_cloud] = cloud ? cloud['name'] : nil
439
439
  instance_prompt_options[:environment] = selected_environment ? selected_environment['code'] : nil
440
440
  instance_prompt_options[:default_security_groups] = scoped_instance_config['securityGroups'] ? scoped_instance_config['securityGroups'] : nil
441
-
441
+
442
442
  instance_prompt_options[:no_prompt] = options[:no_prompt]
443
443
  #instance_prompt_options[:always_prompt] = options[:no_prompt] != true # options[:always_prompt]
444
444
  instance_prompt_options[:options] = scoped_instance_config # meh, actually need to make these default values instead..
@@ -460,8 +460,9 @@ class Morpheus::Cli::Apps
460
460
  instance_prompt_options[:locked_fields] = scoped_instance_config['lockedFields']
461
461
  instance_prompt_options[:for_app] = true
462
462
  # this provisioning helper method handles all (most) of the parsing and prompting
463
+ scoped_instance_config = Marshal.load( Marshal.dump(scoped_instance_config) )
463
464
  instance_config_payload = prompt_new_instance(instance_prompt_options)
464
-
465
+
465
466
  # strip all empty string and nil
466
467
  instance_config_payload.deep_compact!
467
468
  # use the blueprint config as the base
@@ -215,7 +215,8 @@ module Morpheus
215
215
  opts.on(arg1, arg2, description) do |val|
216
216
  if option_type['type'] == 'checkbox'
217
217
  val = (val.to_s != 'false' && val.to_s != 'off')
218
- else
218
+ elsif option_type['dataType'] != 'string'
219
+ # 'dataType': 'string' added to cli to avoid auto conversion to JSON
219
220
  # attempt to parse JSON, this allows blank arrays for multiSelect like --tenants []
220
221
  if (val.to_s[0] == '{' && val.to_s[-1] == '}') || (val.to_s[0] == '[' && val.to_s[-1] == ']')
221
222
  begin
@@ -1202,11 +1203,18 @@ module Morpheus
1202
1203
  # this could go be done in optparse.parse instead perhaps
1203
1204
  def verify_args!(opts={})
1204
1205
  args = opts[:args] || []
1205
- if opts[:count]
1206
- if args.count < opts[:count]
1207
- raise_args_error("not enough arguments, expected #{opts[:count]} and got #{args.count == 0 ? '0' : args.count.to_s + ': '}#{args.join(', ')}", args, opts[:optparse])
1208
- elsif args.count > opts[:count]
1209
- raise_args_error("too many arguments, expected #{opts[:count]} and got #{args.count == 0 ? '0' : args.count.to_s + ': '}#{args.join(', ')}", args, opts[:optparse])
1206
+ count = opts[:count]
1207
+ # simplify output for verify_args!(min:2, max:2) or verify_args!(max:0)
1208
+ if opts[:min] && opts[:max] && opts[:min] == opts[:max]
1209
+ count = opts[:min]
1210
+ elsif opts[:max] == 0
1211
+ count = 0
1212
+ end
1213
+ if count
1214
+ if args.count < count
1215
+ raise_args_error("not enough arguments, expected #{count} and got #{args.count == 0 ? '0' : args.count.to_s + ': '}#{args.join(', ')}", args, opts[:optparse])
1216
+ elsif args.count > count
1217
+ raise_args_error("too many arguments, expected #{count} and got #{args.count == 0 ? '0' : args.count.to_s + ': '}#{args.join(', ')}", args, opts[:optparse])
1210
1218
  end
1211
1219
  else
1212
1220
  if opts[:min]
@@ -77,14 +77,26 @@ module Morpheus
77
77
 
78
78
  def exec_command(command_name, args)
79
79
  #puts "exec_command(#{command_name}, #{args})"
80
- found_alias_command = instance.get_alias(command_name)
81
80
  if has_alias?(command_name)
82
81
  exec_alias(command_name, args)
83
82
  elsif has_command?(command_name)
84
83
  instance.get(command_name).new.handle(args)
85
84
  else
86
85
  # todo: need to just return error instead of raise
87
- raise CommandNotFoundError.new("'#{command_name}' is not a morpheus command.")
86
+ msg = "'#{command_name}' is not a morpheus command."
87
+ suggestions = find_command_suggestions(command_name)
88
+ if suggestions && suggestions.size == 1
89
+ msg += "\nThe most similar command is:\n"
90
+ suggestions.first(5).each do |suggestion|
91
+ msg += "\t" + suggestion + "\n"
92
+ end
93
+ elsif suggestions && suggestions.size > 1
94
+ msg += "\nThe most similar commands are:\n"
95
+ suggestions.first(5).each do |suggestion|
96
+ msg += "\t" + suggestion + "\n"
97
+ end
98
+ end
99
+ raise CommandNotFoundError.new(msg)
88
100
  end
89
101
  end
90
102
 
@@ -269,6 +281,47 @@ module Morpheus
269
281
  return exit_code, err
270
282
  end
271
283
 
284
+ def cached_command_list
285
+ @cached_command_list ||= (all.keys + all_aliases.keys).collect { |it| it.to_s }.sort
286
+ end
287
+
288
+ def clear_cached_command_list
289
+ @cached_command_list = nil
290
+ end
291
+
292
+ # find suggested commands (or aliases) for a name that was not found
293
+ # First this looks for the plural of the original guess
294
+ # Then pop characters off the end looking for partial matches
295
+ # as long as the guess is at least 3 characters
296
+ def find_command_suggestions(command_name)
297
+ every_command = cached_command_list
298
+ guess = command_name
299
+ suggestions = []
300
+ while guess.size >= 3
301
+ plural_guess = guess.pluralize
302
+ if every_command.include?(guess)
303
+ suggestions << guess
304
+ end
305
+ if every_command.include?(plural_guess)
306
+ suggestions << plural_guess
307
+ end
308
+ # if every_command.include?(guess)
309
+ # suggestions << plural_guess
310
+ # else
311
+ guess_regexp = /^#{Regexp.escape(guess)}/i
312
+ every_command.each do |it|
313
+ if it =~ guess_regexp
314
+ suggestions << it
315
+ end
316
+ end
317
+ # end
318
+ guess = guess[0..-2]
319
+ end
320
+ suggestions.uniq!
321
+ suggestions.sort! { |x,y| [x.split('-').size, x] <=> [y.split('-').size, y] }
322
+ return suggestions
323
+ end
324
+
272
325
  end
273
326
 
274
327
  end