morpheus-cli 8.0.13 → 8.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aad3ce7f814765f24cd6d8c0c54c234d6c745be616536a92b3005e127740f2d0
4
- data.tar.gz: af0212f0f02ee8f85133c36790493e09542112e9e21ee183692cce150a029e29
3
+ metadata.gz: 1c5ef57fd8e0005ce881d8a2e8de0d8100e965447dd5e66dfded62ab50229c8a
4
+ data.tar.gz: 64705b005697dbab2f6b0c15fa58400182a8a2a063c5a99d93cb0364b101227c
5
5
  SHA512:
6
- metadata.gz: 648640393d42c0a3dc358a198baf5c513cbf48e8c58202143974f7bbd15e2acc06f7cd3c35305a6404ddabb9ed0ee64c16c4beb84bbda588d7c03df4debca69d
7
- data.tar.gz: 4a1b6e530ebcb825f2c1bc54e04ea29ad0ec249f1b3ddc65d1bfce2144db1f17c1389d2368ee9f4dbb4398f5be911b1509f418f6ee9a28c8c42bd1e4e95b3cc2
6
+ metadata.gz: feedeaf9d8c6241a0076af156b22182dc76ffb91feafbfd5fff9bae98016624f93400351d7fd37d4cb0fa16d42eb50444f5fd895d554bbb4214e6f8eb30c30e6
7
+ data.tar.gz: bbb9fb396e8b36bf35bf75a1afbcd08d3c0d000716486035c24d64a9540615a0de01e084ebce2dec11828d3345a5e9643b41c71b339bedbc92d17f8f79b10aa2
data/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  FROM ruby:2.7.5
2
2
 
3
- RUN gem install morpheus-cli -v 8.0.13
3
+ RUN gem install morpheus-cli -v 8.1.1
4
4
 
5
5
  ENTRYPOINT ["morpheus"]
data/bin/morpheus CHANGED
@@ -2,11 +2,14 @@
2
2
  require 'morpheus'
3
3
 
4
4
  # arguments
5
- args = ARGV
5
+ args = ARGV.dup
6
6
 
7
- # input pipe
8
- # append piped data as arguments
9
- if !$stdin.tty?
7
+ # input pipe: only read stdin as arguments when --stdin is explicitly passed.
8
+ # Previously stdin was always consumed when not a TTY, which caused morpheus
9
+ # invoked inside a piped bash script (e.g. base64 -d <<< <script> | bash -l)
10
+ # to consume the remaining script lines as CLI arguments (MORPH-6237).
11
+ # Usage: echo 42 | morpheus instances get --stdin
12
+ if args.delete('--stdin') && !$stdin.tty?
10
13
  pipe_data = $stdin.read
11
14
  if pipe_data
12
15
  args += pipe_data.split
@@ -412,6 +412,14 @@ class Morpheus::APIClient
412
412
  Morpheus::ServersInterface.new(common_interface_options).setopts(@options)
413
413
  end
414
414
 
415
+ def systems
416
+ Morpheus::SystemsInterface.new(common_interface_options).setopts(@options)
417
+ end
418
+
419
+ def system_types
420
+ Morpheus::SystemTypesInterface.new(common_interface_options).setopts(@options)
421
+ end
422
+
415
423
  def server_devices
416
424
  Morpheus::ServerDevicesInterface.new(common_interface_options).setopts(@options)
417
425
  end
@@ -2,58 +2,69 @@ require 'morpheus/api/api_client'
2
2
  # this may change to just /api/image-builds
3
3
  class Morpheus::ImageBuilderImageBuildsInterface < Morpheus::APIClient
4
4
 
5
+ def base_path
6
+ "#{@base_url}/api/image-builds"
7
+ end
8
+
5
9
  def get(id, params={})
6
10
  raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
7
- url = "#{@base_url}/api/image-builds/#{id}"
11
+ url = "#{base_path}/#{id}"
8
12
  headers = { params: params, authorization: "Bearer #{@access_token}" }
9
13
  opts = {method: :get, url: url, headers: headers}
10
14
  execute(opts)
11
15
  end
12
16
 
13
17
  def list(params={})
14
- url = "#{@base_url}/api/image-builds"
18
+ url = "#{base_path}"
15
19
  headers = { params: params, authorization: "Bearer #{@access_token}" }
16
20
  opts = {method: :get, url: url, headers: headers}
17
21
  execute(opts)
18
22
  end
19
23
 
20
24
  def create(payload)
21
- url = "#{@base_url}/api/image-builds"
25
+ url = "#{base_path}"
22
26
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
23
27
  opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
24
28
  execute(opts)
25
29
  end
26
30
 
27
31
  # def validate_save(payload)
28
- # url = "#{@base_url}/api/image-builds/validate-save"
32
+ # url = "#{base_path}/validate-save"
29
33
  # headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
30
34
  # opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
31
35
  # execute(opts)
32
36
  # end
33
37
 
34
38
  def update(id, payload)
35
- url = "#{@base_url}/api/image-builds/#{id}"
39
+ url = "#{base_path}/#{id}"
36
40
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
37
41
  opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
38
42
  execute(opts)
39
43
  end
40
44
 
41
45
  def destroy(id, params={})
42
- url = "#{@base_url}/api/image-builds/#{id}"
46
+ url = "#{base_path}/#{id}"
43
47
  headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
44
48
  opts = {method: :delete, url: url, headers: headers}
45
49
  execute(opts)
46
50
  end
47
51
 
48
52
  def run(id, params={})
49
- url = "#{@base_url}/api/image-builds/#{id}/run"
53
+ url = "#{base_path}/#{id}/run"
50
54
  headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
51
55
  opts = {method: :post, url: url, headers: headers}
52
56
  execute(opts)
53
57
  end
54
58
 
55
59
  def list_executions(id, params={})
56
- url = "#{@base_url}/api/image-builds/#{id}/list-executions"
60
+ url = "#{base_path}/#{id}/list-executions"
61
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
62
+ opts = {method: :get, url: url, headers: headers}
63
+ execute(opts)
64
+ end
65
+
66
+ def create_options(params={})
67
+ url = "#{base_path}/create-options"
57
68
  headers = { params: params, authorization: "Bearer #{@access_token}" }
58
69
  opts = {method: :get, url: url, headers: headers}
59
70
  execute(opts)
@@ -118,6 +118,20 @@ class Morpheus::RolesInterface < Morpheus::APIClient
118
118
  execute(method: :put, url: url, headers: headers, payload: payload.to_json)
119
119
  end
120
120
 
121
+ def update_cluster_type(account_id, id, options)
122
+ url = build_url(account_id, id) + "/update-cluster-type"
123
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
124
+ payload = options
125
+ execute(method: :put, url: url, headers: headers, payload: payload.to_json)
126
+ end
127
+
128
+ def validate(account_id, options, params={})
129
+ url = "#{@base_url}/api/roles/validate"
130
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
131
+ payload = options
132
+ execute(method: :post, url: url, headers: headers, payload: payload.to_json, params: params)
133
+ end
134
+
121
135
  private
122
136
 
123
137
  def build_url(account_id=nil, role_id=nil)
@@ -0,0 +1,13 @@
1
+ require 'morpheus/api/rest_interface'
2
+
3
+ class Morpheus::SystemTypesInterface < Morpheus::RestInterface
4
+
5
+ def base_path
6
+ "/api/infrastructure/system-types"
7
+ end
8
+
9
+ def list_layouts(type_id, params = {})
10
+ execute(method: :get, url: "#{base_path}/#{type_id}/layouts", params: params)
11
+ end
12
+
13
+ end
@@ -0,0 +1,23 @@
1
+ require 'morpheus/api/rest_interface'
2
+
3
+ class Morpheus::SystemsInterface < Morpheus::RestInterface
4
+
5
+ def base_path
6
+ "/api/infrastructure/systems"
7
+ end
8
+
9
+ def save_uninitialized(payload, params={}, headers={})
10
+ execute(method: :post, url: "#{base_path}/uninitialized", params: params, payload: payload, headers: headers)
11
+ end
12
+
13
+ def initialize_system(id, payload={}, params={}, headers={})
14
+ validate_id!(id)
15
+ execute(method: :put, url: "#{base_path}/#{CGI::escape(id.to_s)}/initialize", params: params, payload: payload, headers: headers)
16
+ end
17
+
18
+ def validate_system(id, params={}, headers={})
19
+ validate_id!(id)
20
+ execute(method: :get, url: "#{base_path}/#{CGI::escape(id.to_s)}/validate", params: params, headers: headers)
21
+ end
22
+
23
+ end
@@ -528,6 +528,8 @@ EOT
528
528
  {
529
529
  "ID" => 'id',
530
530
  "Name" => 'name',
531
+ "Type" => lambda {|it| format_backup_type_tag(it) },
532
+ "Location" => lambda {|it| format_backup_location_tag(it) },
531
533
  "Schedule" => lambda {|it| it['schedule']['name'] rescue '' },
532
534
  "Backup Job" => lambda {|it| it['job']['name'] rescue '' },
533
535
  "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
@@ -539,6 +541,8 @@ EOT
539
541
  {
540
542
  "ID" => 'id',
541
543
  "Name" => 'name',
544
+ "Backup Type" => lambda {|it| format_backup_type_tag(it) },
545
+ "Backup Location" => lambda {|it| format_backup_location_tag(it) },
542
546
  "Location Type" => lambda {|it|
543
547
  if it['locationType'] == "instance"
544
548
  "Instance"
@@ -589,6 +593,33 @@ EOT
589
593
  "#{result['backup']['name']} (#{format_local_dt(result['startDate'])})"
590
594
  end
591
595
 
596
+ # format backup type tag (manual or policy-based)
597
+ def format_backup_type_tag(backup)
598
+ # check if backup has a schdule or job associated with it
599
+ # manual backups typically dont have a schedule while policy-based do
600
+ if backup['cronExpression'] || (backup['schedule'] && backup['schedule']['id'])
601
+ "#{cyan}POLICY#{reset}"
602
+ elsif backup['job'] && backup['job']['id']
603
+ "#{cyan}POLICY#{reset}"
604
+ else
605
+ "#{yellow}MANUAL#{reset}"
606
+ end
607
+ end
608
+
609
+ # format backup location tag (local or remote)
610
+ def format_backup_location_tag(backup)
611
+ # check storage provider or backup provider indicating remote storage
612
+ if backup['storageProvider'] && backup['storageProvider']['id']
613
+ provider_type = backup['storageProvider']['type'] || backup['storageProvider']['providerType'] || ''
614
+ "#{green}REMOTE#{reset} (#{provider_type})"
615
+ elsif backup['backupProvider'] && backup['backupProvider']['id']
616
+ provider_type = backup['backupProvider']['type'] || backup['backupProvider']['providerType'] || ''
617
+ "#{green}REMOTE#{reset} (#{provider_type})"
618
+ else
619
+ "#{blue}LOCAL#{reset}"
620
+ end
621
+ end
622
+
592
623
  # prompt for an instance config (vdiPool.instanceConfig)
593
624
  def prompt_restore_instance_config(options)
594
625
  # use config if user passed one in..
@@ -651,9 +651,9 @@ class Morpheus::Cli::ImageBuilderCommand
651
651
  private
652
652
 
653
653
  def get_available_image_build_types()
654
- # todo: api call
655
654
  [
656
- {'name' => 'VMware', 'code' => 'vmware', 'instanceType' => {'code' => 'vmware'}}
655
+ {'name' => 'VMware', 'code' => 'vmware', 'instanceType' => {'code' => 'vmware'}},
656
+ {'name' => 'KVM', 'code' => 'kvm', 'instanceType' => {'code' => 'kvm'}}
657
657
  ]
658
658
  end
659
659
 
@@ -663,17 +663,6 @@ class Morpheus::Cli::ImageBuilderCommand
663
663
  }
664
664
  end
665
665
 
666
- def find_image_build_type(val)
667
- if val.nil? || val.to_s.empty?
668
- return nil
669
- else
670
- return get_available_image_build_types().find { |it|
671
- (it['code'].to_s.downcase == val.to_s.downcase) ||
672
- (it['name'].to_s.downcase == val.to_s.downcase)
673
- }
674
- end
675
- end
676
-
677
666
  def add_image_build_option_types(connected=true)
678
667
  [
679
668
  {'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => get_available_image_build_types_dropdown(), 'required' => true, 'description' => 'Choose the type of image build.'},
@@ -901,15 +890,28 @@ class Morpheus::Cli::ImageBuilderCommand
901
890
  def prompt_new_image_build(options={}, default_values={}, do_require=true)
902
891
  payload = {}
903
892
 
893
+ # load to create-options data for help with prompting
894
+ create_options = nil
895
+ begin
896
+ create_options = @image_builds_interface.create_options({})
897
+ rescue => ex
898
+ Morpheus::Logging::DarkPrinter.puts "Unable to load image build create options data" if Morpheus::Logging.debug?
899
+ end
904
900
  # Summary / Settings Tab
905
901
 
906
902
  # Image Build Type
903
+ image_build_types = nil
904
+ if create_options && create_options['imageBuildTypes']
905
+ image_build_types = create_options['imageBuildTypes'].collect {|it| it.merge({'value' => it['code']}) }
906
+ else
907
+ image_build_types = get_available_image_build_types_dropdown
908
+ end
907
909
  image_build_type = nil
908
910
  if options['type']
909
- image_build_type = find_image_build_type(options['type'])
911
+ image_build_type = image_build_types.find { |it| (it['code'].to_s.downcase == options['type'].to_s.downcase) || (it['name'].to_s.downcase == options['type'].to_s.downcase) }
910
912
  else
911
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => get_available_image_build_types_dropdown(), 'required' => do_require, 'description' => 'Choose the type of image build.', 'defaultValue' => default_values['type'], :fmt=>:natural}], options, @api_client)
912
- image_build_type = find_image_build_type(v_prompt['type'])
913
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => image_build_types, 'required' => do_require, 'description' => 'Choose the type of image build.', 'defaultValue' => default_values['type'], :fmt=>:natural}], options, @api_client)
914
+ image_build_type = Morpheus::Cli::OptionTypes.get_last_select()
913
915
  end
914
916
  if !image_build_type
915
917
  print_red_alert "Image Build Type not found!"
@@ -171,11 +171,17 @@ class Morpheus::Cli::LibraryOptionListsCommand
171
171
  "Credentials" => lambda {|it| it['credential'] ? (it['credential']['type'] == 'local' ? '(Local)' : it['credential']['name']) : nil },
172
172
  "Username" => 'serviceUsername',
173
173
  "Password" => 'servicePassword',
174
+ "Inject System Auth Header" => lambda {|it| format_boolean it['injectExecutionLeaseAuth'] },
175
+ "Use Owner Authorization" => lambda {|it| format_boolean it['useOwnerAuth'] }
174
176
  }
175
177
  option_list_columns.delete("API Type") if option_type_list['type'] != 'api'
176
178
  option_list_columns.delete("Credentials") if !['rest','ldap'].include?(option_type_list['type']) # || !(option_type_list['credential'] && option_type_list['credential']['id'])
177
179
  option_list_columns.delete("Username") if !['rest','ldap'].include?(option_type_list['type']) || !(option_type_list['serviceUsername'])
178
180
  option_list_columns.delete("Password") if !['rest','ldap'].include?(option_type_list['type']) || !(option_type_list['servicePassword'])
181
+ option_list_columns.delete("Inject System Auth Header") if option_type_list['type'] != 'rest'
182
+ option_list_columns.delete("Use Owner Authorization") if option_type_list['type'] != 'rest'
183
+ option_list_columns.delete("Inject System Auth Header") if option_type_list['type'] != 'rest'
184
+ option_list_columns.delete("Use Owner Authorization") if option_type_list['type'] != 'rest'
179
185
  source_headers = []
180
186
  if option_type_list['config'] && option_type_list['config']['sourceHeaders']
181
187
  source_headers = option_type_list['config']['sourceHeaders'].collect do |header|
@@ -291,7 +297,7 @@ class Morpheus::Cli::LibraryOptionListsCommand
291
297
  end
292
298
  end
293
299
  # tweak payload for API
294
- ['ignoreSSLErrors', 'realTime'].each { |k|
300
+ ['ignoreSSLErrors', 'realTime', 'injectExecutionLeaseAuth', 'useOwnerAuth'].each { |k|
295
301
  list_payload[k] = ['on','true'].include?(list_payload[k].to_s) if list_payload.key?(k)
296
302
  }
297
303
  payload.deep_merge!({'optionTypeList' => list_payload})
@@ -348,7 +354,7 @@ class Morpheus::Cli::LibraryOptionListsCommand
348
354
  end
349
355
  end
350
356
  # tweak payload for API
351
- ['ignoreSSLErrors', 'realTime'].each { |k|
357
+ ['ignoreSSLErrors', 'realTime', 'injectExecutionLeaseAuth', 'useOwnerAuth'].each { |k|
352
358
  list_payload[k] = ['on','true'].include?(list_payload[k].to_s) if list_payload.key?(k)
353
359
  }
354
360
  payload.deep_merge!({'optionTypeList' => list_payload})
@@ -437,6 +443,8 @@ class Morpheus::Cli::LibraryOptionListsCommand
437
443
  {'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'sourceUrl', 'fieldLabel' => 'Source Url', 'type' => 'text', 'required' => true, 'description' => "A REST URL can be used to fetch list data and is cached in the appliance database.", 'displayOrder' => 6},
438
444
  {'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'ignoreSSLErrors', 'fieldLabel' => 'Ignore SSL Errors', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 7},
439
445
  {'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'realTime', 'fieldLabel' => 'Real Time', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 8},
446
+ {'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'injectExecutionLeaseAuth', 'switch' => 'inject-execution-lease-auth', 'fieldLabel' => 'Inject System Auth Header', 'type' => 'checkbox', 'defaultValue' => false, 'description' => 'Injects an authorization header using a system lease token when making the REST call.', 'displayOrder' => 21},
447
+ {'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'useOwnerAuth', 'switch' => 'use-owner-auth', 'fieldLabel' => 'Use Owner Authorization', 'type' => 'checkbox', 'defaultValue' => false, 'description' => 'Uses the authorization credentials of the owner of the option list.', 'displayOrder' => 22},
440
448
  {'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'sourceMethod', 'fieldLabel' => 'Source Method', 'type' => 'select', 'selectOptions' => [{'name' => 'GET', 'value' => 'GET'}, {'name' => 'POST', 'value' => 'POST'}], 'defaultValue' => 'GET', 'required' => true, 'displayOrder' => 9},
441
449
  {'dependsOnCode' => 'optionTypeList.type:rest|ldap', 'fieldName' => 'credential', 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Credential ID or use "local" to specify username and password', 'displayOrder' => 10, 'defaultValue' => "local", 'required' => true, :for_help_only => true}, # hacky way to render this but not prompt for it
442
450
  {'dependsOnCode' => 'optionTypeList.type:rest', 'fieldName' => 'serviceUsername', 'fieldLabel' => 'Username', 'type' => 'text', 'description' => "A Basic Auth Username for use when type is 'rest'.", 'displayOrder' => 11, "credentialFieldContext" => 'credential', "credentialFieldName" => 'username', "credentialType" => "username-password,oauth2"},