morpheus-cli 5.3.2.1 → 5.3.4

Sign up to get free protection for your applications and to get access to all the features.
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