morpheus-cli 6.0.0 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd7f59f72f0bc788526827effe2023844d0a99eb3485b27f276b296d604b00e0
4
- data.tar.gz: f10d8bd99db70dc0399475e5df17d93eab19b6ed45748a6edefcfb9fd5f5b945
3
+ metadata.gz: 4160be97bfb96acf0fe1091e9e8d6ecde617bd2aba547ef1cfab8a11d6134ecf
4
+ data.tar.gz: 5e0675e4e1061595e560e2395625e7a7546813e68e09a8237ccd33d92aeb948d
5
5
  SHA512:
6
- metadata.gz: fd82a4e9ff23ba92dd577667e28258b4cf04d1ee147210b6553cbda7e56c1aea5313a8f2c25a4703f7e67d3dfdeefcb3ca811fd9cd9037c6980062f8ee40475e
7
- data.tar.gz: c0ffcee9b0a774ffcb0df995b1fbd5247ea6fcc4dad3f1d2fa7ab3ceff6e9a4e89faf29e87a65c335a3eab6db2bce1368b60ef0453ed8fbe1e18f77186767154
6
+ metadata.gz: a2992a310163d78082f7d748ca4ae50c673c32e7ae782497bb89f721135ed66509549c124290e9b4bd644cad2e3838c878331427ddd1090325f8946c7d30e731
7
+ data.tar.gz: 48ec07412e0cdcad359ea2dc050cc5017a9a39e368fb662e50b880628213fc4681b85da9cd5d8503ea7426be0fbac77b5e61d2dd8bc397ab13f0ce21695d2758
data/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  FROM ruby:2.7.5
2
2
 
3
- RUN gem install morpheus-cli -v 6.0.0
3
+ RUN gem install morpheus-cli -v 6.0.1
4
4
 
5
5
  ENTRYPOINT ["morpheus"]
@@ -43,10 +43,10 @@ class Morpheus::JobsInterface < Morpheus::APIClient
43
43
  execute(method: :delete, url: url, headers: headers)
44
44
  end
45
45
 
46
- def execute_job(id, params={})
46
+ def execute_job(id, payload={}, params={})
47
47
  url = "#{base_path}/#{id}/execute"
48
48
  headers = { params: params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
49
- execute(method: :put, url: url, headers: headers)
49
+ execute(method: :put, url: url, headers: headers, payload: payload.to_json)
50
50
  end
51
51
 
52
52
  =begin
@@ -1020,7 +1020,7 @@ EOT
1020
1020
  end
1021
1021
 
1022
1022
  def apply(args)
1023
- default_refresh_interval = 10
1023
+ default_refresh_interval = 5
1024
1024
  params, payload, options = {}, {}, {}
1025
1025
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1026
1026
  opts.banner = subcommand_usage("[app] [options]")
@@ -716,6 +716,7 @@ EOT
716
716
  # "Content" => lambda {|it| it['content'] },
717
717
  "Enabled" => lambda {|it| format_boolean(it['enabled']) },
718
718
  "Featured" => lambda {|it| format_boolean(it['featured']) },
719
+ "Allow Quantity" => lambda {|it| format_boolean(it['allowQuantity']) },
719
720
  #"Config" => lambda {|it| it['config'] },
720
721
  "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
721
722
  "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
@@ -757,19 +758,20 @@ EOT
757
758
  {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 4},
758
759
  {'fieldName' => 'enabled', 'fieldLabel' => 'Enabled', 'type' => 'checkbox', 'defaultValue' => true, 'displayOrder' => 5},
759
760
  {'fieldName' => 'featured', 'fieldLabel' => 'Featured', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 6},
760
- {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'}, {'name' => 'Public', 'value' => 'public'}], 'defaultValue' => 'private', 'required' => true, 'displayOrder' => 7},
761
- {'fieldName' => 'layoutCode', 'fieldLabel' => 'Layout Code', 'type' => 'text', 'required' => false, 'displayOrder' => 8},
762
- {'fieldName' => 'iconPath', 'fieldLabel' => 'Logo', 'type' => 'select', 'optionSource' => 'iconList', 'displayOrder' => 9},
761
+ {'fieldName' => 'allowQuantity', 'fieldLabel' => 'Allow Quantity', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 7},
762
+ {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'}, {'name' => 'Public', 'value' => 'public'}], 'defaultValue' => 'private', 'required' => true, 'displayOrder' => 8},
763
+ {'fieldName' => 'layoutCode', 'fieldLabel' => 'Layout Code', 'type' => 'text', 'required' => false, 'displayOrder' => 9},
764
+ {'fieldName' => 'iconPath', 'fieldLabel' => 'Logo', 'type' => 'select', 'optionSource' => 'iconList', 'displayOrder' => 10},
763
765
  #{'fieldName' => 'optionTypes', 'fieldLabel' => 'Option Types', 'type' => 'text', 'description' => 'Option Types to include, comma separated list of names or IDs.', 'displayOrder' => 8},
764
- {'dependsOnCode' => 'catalogItemType.type:instance', 'fieldName' => 'config', 'fieldLabel' => 'Config', 'type' => 'code-editor', 'description' => 'JSON or YAML', 'required' => true, 'displayOrder' => 10},
765
- {'dependsOnCode' => 'catalogItemType.type:workflow', 'fieldName' => 'workflowConfig', 'fieldLabel' => 'Config', 'type' => 'textarea', 'description' => 'Enter configuration for the Workflow', 'required' => false, 'noParse' => true, 'displayOrder' => 10},
766
- {'dependsOnCode' => 'catalogItemType.type:blueprint', 'fieldName' => 'blueprint', 'fieldLabel' => 'Blueprint', 'type' => 'select', 'optionSource' => 'blueprints', 'description' => 'Choose a blueprint to apply to the catalog item.', 'required' => true, 'noParams' => true, 'displayOrder' => 11},
767
- {'dependsOnCode' => 'catalogItemType.type:blueprint', 'fieldName' => 'appSpec', 'fieldLabel' => 'App Spec', 'type' => 'code-editor', 'description' => 'Enter a spec in the for the App, the Scribe YAML format', 'required' => true, 'displayOrder' => 12},
768
- {'dependsOnCode' => 'catalogItemType.type:workflow', 'fieldName' => 'workflow', 'fieldLabel' => 'Workflow', 'type' => 'select', 'optionSource' => 'operationWorkflows', 'description' => 'Enter a spec in the for the App, the Scribe YAML format', 'required' => true, 'noParams' => true, 'displayOrder' => 13},
766
+ {'dependsOnCode' => 'catalogItemType.type:instance', 'fieldName' => 'config', 'fieldLabel' => 'Config', 'type' => 'code-editor', 'description' => 'JSON or YAML', 'required' => true, 'displayOrder' => 11},
767
+ {'dependsOnCode' => 'catalogItemType.type:workflow', 'fieldName' => 'workflowConfig', 'fieldLabel' => 'Config', 'type' => 'textarea', 'description' => 'Enter configuration for the Workflow', 'required' => false, 'noParse' => true, 'displayOrder' => 11},
768
+ {'dependsOnCode' => 'catalogItemType.type:blueprint', 'fieldName' => 'blueprint', 'fieldLabel' => 'Blueprint', 'type' => 'select', 'optionSource' => 'blueprints', 'description' => 'Choose a blueprint to apply to the catalog item.', 'required' => true, 'noParams' => true, 'displayOrder' => 12},
769
+ {'dependsOnCode' => 'catalogItemType.type:blueprint', 'fieldName' => 'appSpec', 'fieldLabel' => 'App Spec', 'type' => 'code-editor', 'description' => 'Enter a spec in the for the App, the Scribe YAML format', 'required' => true, 'displayOrder' => 13},
770
+ {'dependsOnCode' => 'catalogItemType.type:workflow', 'fieldName' => 'workflow', 'fieldLabel' => 'Workflow', 'type' => 'select', 'optionSource' => 'operationWorkflows', 'description' => 'Enter a spec in the for the App, the Scribe YAML format', 'required' => true, 'noParams' => true, 'displayOrder' => 14},
769
771
  {'dependsOnCode' => 'catalogItemType.type:workflow', 'fieldName' => 'context', 'fieldLabel' => 'Context Type', 'type' => 'select', 'optionSource' => lambda { |api_client, api_params|
770
772
  [{'name' => "Select", 'value' => ""}, {'name' => "None", 'value' => "appliance"}, {'name' => "Instance", 'value' => "instance"}, {'name' => "Server", 'value' => "server"}]
771
773
  }, 'description' => 'Context for operational workflow, determines target type', 'defaultValue' => 'Select', 'required' => false},
772
- {'fieldName' => 'content', 'fieldLabel' => 'Content', 'type' => 'code-editor', 'description' => 'Wiki Page Content describing the catalog item', 'displayOrder' => 14}
774
+ {'fieldName' => 'content', 'fieldLabel' => 'Content', 'type' => 'code-editor', 'description' => 'Wiki Page Content describing the catalog item', 'displayOrder' => 15}
773
775
  ]
774
776
  end
775
777
 
@@ -4946,7 +4946,7 @@ EOT
4946
4946
  end
4947
4947
 
4948
4948
  def apply(args)
4949
- default_refresh_interval = 10
4949
+ default_refresh_interval = 5
4950
4950
  params, payload, options = {}, {}, {}
4951
4951
  optparse = Morpheus::Cli::OptionParser.new do |opts|
4952
4952
  opts.banner = subcommand_usage("[instance] [options]")
@@ -280,17 +280,37 @@ class Morpheus::Cli::JobsCommand
280
280
  end
281
281
  options[:type] = 'morpheus.securityScan'
282
282
  end
283
- opts.on('--context-type [TYPE]', String, "Context type (instance|server|none). Default is none") do |val|
283
+ opts.on('--context-type [TYPE]', String, "Context type (instance|instance-label|server|server-label|none). Default is none") do |val|
284
+ val = val.to_s.downcase
284
285
  params['targetType'] = (val == 'none' ? 'appliance' : val)
285
286
  end
287
+ opts.on('--target-type [TYPE]', String, "alias for --context-type") do |val|
288
+ val = val.to_s.downcase
289
+ params['targetType'] = (val == 'none' ? 'appliance' : val)
290
+ end
291
+ opts.add_hidden_option('--target-type')
286
292
  opts.on('--instances [LIST]', Array, "Context instances(s), comma separated list of instance IDs. Incompatible with --servers") do |list|
287
293
  params['targetType'] = 'instance'
288
294
  params['targets'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq.collect {|it| {'refId' => it.to_i}}
289
295
  end
296
+ opts.on('--instance-label LABEL', String, "Instance Label") do |val|
297
+ if params['targetType'] && params['targetType'] != 'instance-label'
298
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{params['targetType']})")
299
+ end
300
+ params['targetType'] = 'instance-label'
301
+ params['instanceLabel'] = val
302
+ end
290
303
  opts.on('--servers [LIST]', Array, "Context server(s), comma separated list of server IDs. Incompatible with --instances") do |list|
291
304
  params['targetType'] = 'server'
292
305
  params['targets'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq.collect {|it| {'refId' => it.to_i}}
293
306
  end
307
+ opts.on('--server-label LABEL', String, "Server Label") do |val|
308
+ if params['targetType'] && params['targetType'] != 'server-label'
309
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{params['targetType']})")
310
+ end
311
+ params['targetType'] = 'server-label'
312
+ params['serverLabel'] = val
313
+ end
294
314
  opts.on('-S', '--schedule [SCHEDULE]', String, "Job execution schedule type name or ID") do |val|
295
315
  options[:schedule] = val
296
316
  end
@@ -465,8 +485,15 @@ class Morpheus::Cli::JobsCommand
465
485
  end
466
486
  end
467
487
  params['targets'] = targets.collect {|it| {'refId' => it}}
488
+ elsif params['targetType'] == 'instance-label'
489
+ if params['instanceLabel'].nil?
490
+ params['instanceLabel'] = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'instance-label', 'fieldName' => 'instanceLabel', 'fieldLabel' => 'Instance Label', 'type' => 'text', 'required' => true, 'description' => 'Instance Label'}], options[:options], @api_client)['instanceLabel']
491
+ end
492
+ elsif params['targetType'] == 'server-label'
493
+ if params['serverLabel'].nil?
494
+ params['serverLabel'] = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'server-label', 'fieldName' => 'serverLabel', 'fieldLabel' => 'Server Label', 'type' => 'text', 'required' => true, 'description' => 'Server Label'}], options[:options], @api_client)['serverLabel']
495
+ end
468
496
  end
469
-
470
497
  # schedule
471
498
  if options[:schedule].nil?
472
499
  options[:schedule] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'scheduleMode', 'fieldLabel' => "Schedule", 'type' => 'select', 'required' => true, 'selectOptions' => job_options['schedules'], 'defaultValue' => job_options['schedules'].first['name']}], options[:options], @api_client, {})['scheduleMode']
@@ -554,17 +581,37 @@ class Morpheus::Cli::JobsCommand
554
581
  options[:security_package] = val
555
582
  end
556
583
  end
557
- opts.on('--context-type [TYPE]', String, "Context type (instance|server|none). Default is none") do |val|
584
+ opts.on('--context-type [TYPE]', String, "Context type (instance|instance-label|server|server-label|none). Default is none") do |val|
585
+ val = val.to_s.downcase
586
+ params['targetType'] = (val == 'none' ? 'appliance' : val)
587
+ end
588
+ opts.on('--target-type [TYPE]', String, "alias for --context-type") do |val|
589
+ val = val.to_s.downcase
558
590
  params['targetType'] = (val == 'none' ? 'appliance' : val)
559
591
  end
592
+ opts.add_hidden_option('--target-type')
560
593
  opts.on('--instances [LIST]', Array, "Context instances(s), comma separated list of instance IDs. Incompatible with --servers") do |list|
561
594
  params['targetType'] = 'instance'
562
595
  options[:targets] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip.to_i }.compact.uniq.collect {|it| {'refId' => it.to_i}}
563
596
  end
597
+ opts.on('--instance-label LABEL', String, "Instance Label") do |val|
598
+ if params['targetType'] && params['targetType'] != 'instance-label'
599
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{params['targetType']})")
600
+ end
601
+ params['targetType'] = 'instance-label'
602
+ params['instanceLabel'] = val
603
+ end
564
604
  opts.on('--servers [LIST]', Array, "Context server(s), comma separated list of server IDs. Incompatible with --instances") do |list|
565
605
  params['targetType'] = 'server'
566
606
  options[:targets] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip.to_i }.compact.uniq.collect {|it| {'refId' => it.to_i}}
567
607
  end
608
+ opts.on('--server-label LABEL', String, "Server Label") do |val|
609
+ if params['targetType'] && params['targetType'] != 'server-label'
610
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{params['targetType']})")
611
+ end
612
+ params['targetType'] = 'server-label'
613
+ params['serverLabel'] = val
614
+ end
568
615
  opts.on('--schedule [SCHEDULE]', String, "Job execution schedule type name or ID") do |val|
569
616
  options[:schedule] = val
570
617
  end
@@ -594,11 +594,15 @@ EOT
594
594
  type_id = nil
595
595
  workflow_context = nil
596
596
  workflow_target = nil
597
+ quantity = nil
597
598
  optparse = Morpheus::Cli::OptionParser.new do |opts|
598
599
  opts.banner = subcommand_usage("[type] [options]")
599
600
  opts.on('-t', '--type TYPE', String, "Catalog Item Type Name or ID") do |val|
600
601
  type_id = val.to_s
601
602
  end
603
+ opts.on('--quantity QUANTITY', String, "Quantity for this catalog item. Will be overridden to 1 if quantity not allowed.") do |val|
604
+ quantity = val.to_s
605
+ end
602
606
  opts.on('--validate','--validate', "Validate Only. Validates the configuration and skips adding the item.") do
603
607
  options[:validate_only] = true
604
608
  end
@@ -648,6 +652,16 @@ EOT
648
652
  payload[add_item_object_key]['type'] = {'name' => catalog_item_type['name']}
649
653
  #payload[add_item_object_key]['type'] = {'id' => catalog_item_type['id']}
650
654
 
655
+ if quantity
656
+ payload[add_item_object_key].deep_merge!({'quantity' => quantity})
657
+ else
658
+ if catalog_item_type['allowQuantity']
659
+ quantity_option_type = {'fieldName' => 'quantity', 'fieldLabel' => 'Quantity', 'type' => 'number', 'defaultValue' => 1, 'required' => true, 'displayOrder' => 1}
660
+ quantity_prompt = Morpheus::Cli::OptionTypes.prompt( [quantity_option_type], options[:options], @api_client, options[:params])['quantity']
661
+ payload[add_item_object_key].deep_merge!({'quantity' => quantity_prompt})
662
+ end
663
+ end
664
+
651
665
  # this is silly, need to load by id to get optionTypes
652
666
  # maybe do ?name=foo&includeOptionTypes=true
653
667
  if catalog_item_type['optionTypes'].nil?
@@ -926,11 +940,15 @@ EOT
926
940
  type_id = nil
927
941
  workflow_context = nil
928
942
  workflow_target = nil
943
+ quantity = nil
929
944
  optparse = Morpheus::Cli::OptionParser.new do |opts|
930
945
  opts.banner = subcommand_usage("[type] [options]")
931
946
  opts.on('-t', '--type TYPE', String, "Catalog Item Type Name or ID") do |val|
932
947
  type_id = val.to_s
933
948
  end
949
+ opts.on('--quantity QUANTITY', String, "Quantity for this catalog item. Will be overridden to 1 if quantity not allowed.") do |val|
950
+ quantity = val.to_s
951
+ end
934
952
  opts.on('--validate','--validate', "Validate Only. Validates the configuration and skips creating the order.") do
935
953
  options[:validate_only] = true
936
954
  end
@@ -991,6 +1009,17 @@ EOT
991
1009
  item_payload['type'] = {'name' => catalog_item_type['name']}
992
1010
  #payload[add_item_object_key]['type'] = {'id' => catalog_item_type['id']}
993
1011
 
1012
+ if quantity
1013
+ item_payload.deep_merge!({'quantity' => quantity})
1014
+ else
1015
+ if catalog_item_type['allowQuantity']
1016
+ quantity_option_type = {'fieldName' => 'quantity', 'fieldLabel' => 'Quantity', 'type' => 'number', 'defaultValue' => 1, 'required' => true, 'displayOrder' => 1}
1017
+ quantity_prompt = Morpheus::Cli::OptionTypes.prompt( [quantity_option_type], options[:options], @api_client, options[:params])['quantity']
1018
+ item_payload.deep_merge!({'quantity' => quantity_prompt})
1019
+ end
1020
+ end
1021
+
1022
+
994
1023
  # this is silly, need to load by id to get optionTypes
995
1024
  # maybe do ?name=foo&includeOptionTypes=true
996
1025
  if catalog_item_type['optionTypes'].nil?
@@ -1200,6 +1229,7 @@ EOT
1200
1229
  # "Blueprint" => lambda {|it| it['blueprint'] ? it['blueprint']['name'] : nil },
1201
1230
  # "Enabled" => lambda {|it| format_boolean(it['enabled']) },
1202
1231
  "Featured" => lambda {|it| format_boolean(it['featured']) },
1232
+ "Allow Quantity" => lambda {|it| format_boolean(it['allowQuantity']) },
1203
1233
  #"Config" => lambda {|it| it['config'] },
1204
1234
  # "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
1205
1235
  # "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
@@ -1408,7 +1438,7 @@ EOT
1408
1438
  {"ID" => lambda {|it| it['id'] } },
1409
1439
  #{"NAME" => lambda {|it| it['name'] } },
1410
1440
  {"Type" => lambda {|it| it['type']['name'] rescue '' } },
1411
- #{"Qty" => lambda {|it| it['quantity'] } },
1441
+ {"Qty" => lambda {|it| it['quantity'] } },
1412
1442
  {"Price" => lambda {|it| it['price'] ? format_money(it['price'] , it['currency'], {sigdig:options[:sigdig] || default_sigdig}) : "No pricing configured" } },
1413
1443
  {"Status" => lambda {|it|
1414
1444
  status_string = format_catalog_item_status(it)
@@ -1438,7 +1468,7 @@ EOT
1438
1468
  {"ID" => lambda {|it| it['id'] } },
1439
1469
  #{"NAME" => lambda {|it| it['name'] } },
1440
1470
  {"TYPE" => lambda {|it| it['type']['name'] rescue '' } },
1441
- #{"QTY" => lambda {|it| it['quantity'] } },
1471
+ {"QTY" => lambda {|it| it['quantity'] } },
1442
1472
  {"PRICE" => lambda {|it| it['price'] ? format_money(it['price'] , it['currency'], {sigdig:options[:sigdig] || default_sigdig}) : "No pricing configured" } },
1443
1473
  {"STATUS" => lambda {|it|
1444
1474
  status_string = format_catalog_item_status(it)
@@ -171,13 +171,21 @@ class Morpheus::Cli::ServicePlanCommand
171
171
  ranges = (service_plan['config'] ? service_plan['config']['ranges'] : nil) || {}
172
172
 
173
173
  if (ranges['minStorage'] && ranges['minStorage'] != '') || (ranges['maxStorage'] && ranges['maxStorage'] != '')
174
- description_cols['Custom Storage Range'] = lambda {|it|
174
+ description_cols['Custom Total Storage Range'] = lambda {|it|
175
175
  get_range(
176
176
  ranges['minStorage'] && ranges['minStorage'] != '' ? "#{ranges['minStorage']} #{(it['config'] && it['config']['storageSizeType'] ? it['config']['storageSizeType'] : 'GB').upcase}" : nil,
177
177
  ranges['maxStorage'] && ranges['maxStorage'] != '' ? "#{ranges['maxStorage']} #{(it['config'] && it['config']['storageSizeType'] ? it['config']['storageSizeType'] : 'GB').upcase}" : nil,
178
178
  )
179
179
  }
180
180
  end
181
+ if (ranges['minPerDiskSize'] && ranges['minPerDiskSize'] != '') || (ranges['maxPerDiskSize'] && ranges['maxPerDiskSize'] != '')
182
+ description_cols['Custom Per Disk Range'] = lambda {|it|
183
+ get_range(
184
+ ranges['minPerDiskSize'] && ranges['minPerDiskSize'] != '' ? "#{ranges['minPerDiskSize']} GB" : nil,
185
+ ranges['maxPerDiskSize'] && ranges['maxPerDiskSize'] != '' ? "#{ranges['maxPerDiskSize']} GB" : nil
186
+ )
187
+ }
188
+ end
181
189
  if (ranges['minMemory'] && ranges['minMemory'] != '') || (ranges['maxMemory'] && ranges['maxMemory'] != '')
182
190
  description_cols['Custom Memory Range'] = lambda {|it|
183
191
  get_range(
@@ -264,6 +272,9 @@ class Morpheus::Cli::ServicePlanCommand
264
272
  opts.on('--disks [NUMBER]', Integer, "Max disks allowed" ) do |val|
265
273
  params['maxDisks'] = val.to_i || 1
266
274
  end
275
+ opts.on('--cores-per-socket [NUMBER]', Integer, "Cores Per Socket") do |val|
276
+ params['coresPerSocket'] = val.to_i || 1
277
+ end
267
278
  opts.on('--custom-cores [on|off]', String, "Can be used to enable / disable customizable cores. Default is on") do |val|
268
279
  params['customCores'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
269
280
  end
@@ -285,17 +296,17 @@ class Morpheus::Cli::ServicePlanCommand
285
296
  opts.on('--price-sets [LIST]', Array, 'Price set(s), comma separated list of price set IDs') do |list|
286
297
  params['priceSets'] = list.collect {|it| it.to_s.strip.empty? || !it.to_i ? nil : it.to_s.strip}.compact.uniq.collect {|it| {'id' => it.to_i}}
287
298
  end
288
- opts.on('--min-storage NUMBER', String, "Min storage. Assumes GB unless optional modifier specified, ex: 512MB") do |val|
289
- # Storage doesn't get converted to bytes
290
- bytes = parse_bytes_param(val, '--min-storage', 'GB', true)
291
- ((params['config'] ||= {})['ranges'] ||= {})['minStorage'] = bytes[:number]
292
- (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
299
+ opts.on('--min-storage NUMBER', String, "Min total storage in GB.") do |val|
300
+ ((params['config'] ||= {})['ranges'] ||= {})['minStorage'] = val.to_i
293
301
  end
294
- opts.on('--max-storage NUMBER', String, "Max storage. Assumes GB unless optional modifier specified, ex: 512MB") do |val|
295
- # Storage doesn't get converted to bytes
296
- bytes = parse_bytes_param(val.to_s, '--max-storage', 'GB', true)
297
- ((params['config'] ||= {})['ranges'] ||= {})['maxStorage'] = bytes[:number]
298
- (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
302
+ opts.on('--max-storage NUMBER', String, "Max total storage in GB.") do |val|
303
+ ((params['config'] ||= {})['ranges'] ||= {})['maxStorage'] = val.to_i
304
+ end
305
+ opts.on('--min-per-disk-size NUMBER', String, "Min per disk size in GB.") do |val|
306
+ ((params['config'] ||= {})['ranges'] ||= {})['minPerDiskSize'] = val.to_i
307
+ end
308
+ opts.on('--max-per-disk-size NUMBER', String, "Max per disk size in GB.") do |val|
309
+ ((params['config'] ||= {})['ranges'] ||= {})['maxPerDiskSize'] = val.to_i
299
310
  end
300
311
  opts.on('--min-memory NUMBER', String, "Min memory. Assumes MB unless optional modifier specified, ex: 1GB") do |val|
301
312
  # Memory does get converted to bytes
@@ -396,13 +407,31 @@ class Morpheus::Cli::ServicePlanCommand
396
407
  true
397
408
  )
398
409
  params['maxMemory'] = bytes[:bytes]
399
- # (params['config'] ||= {})['memorySizeType'] = bytes[:unit].downcase
400
410
  rescue
401
411
  print "Invalid Value... Please try again.\n"
402
412
  end
413
+ params['customMaxMemory'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'customMaxMemory', 'type' => 'checkbox', 'fieldLabel' => 'Custom Max Memory', 'required' => false, 'description' => 'Custom Max Memory', 'defaultValue' => false}],options[:options],@api_client,{}, options[:no_prompt])['customMaxMemory']
403
414
  end
404
415
  end
405
416
 
417
+ # add'n options
418
+ addn_options = [
419
+ {'fieldName' => 'maxCores', 'fieldLabel' => 'Core Count', 'type' => 'number', 'required' => true, 'defaultValue' => 1, 'displayOrder' => 1},
420
+ {'fieldName' => 'customCores', 'fieldLabel' => 'Custom Cores', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 2},
421
+ {'fieldName' => 'coresPerSocket', 'fieldLabel' => 'Cores Per Socket', 'type' => 'number', 'required' => true, 'defaultValue' => 1, 'displayOrder' => 3},
422
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'minStorage', 'fieldLabel' => 'Min Total Storage (GB)', 'type' => 'number', 'displayOrder' => 1},
423
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'maxStorage', 'fieldLabel' => 'Max Total Storage (GB)', 'type' => 'number', 'displayOrder' => 2},
424
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'minPerDiskSize', 'fieldLabel' => 'Min Per Disk Size (GB)', 'type' => 'number', 'displayOrder' => 3},
425
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'maxPerDiskSize', 'fieldLabel' => 'Max Per Disk Size (GB)', 'type' => 'number', 'displayOrder' => 4},
426
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'minMemory', 'fieldLabel' => 'Min Memory (GB)', 'type' => 'number', 'displayOrder' => 5},
427
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'maxMemory', 'fieldLabel' => 'Max Memory (GB)', 'type' => 'number', 'displayOrder' => 6},
428
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'minCores', 'fieldLabel' => 'Min Cores', 'type' => 'number', 'displayOrder' => 7},
429
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'maxCores', 'fieldLabel' => 'Max Cores', 'type' => 'number', 'displayOrder' => 8}
430
+ ]
431
+
432
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(addn_options, options[:options], @api_client, params)
433
+ params.deep_merge!(v_prompt)
434
+
406
435
  # price sets
407
436
  if params['priceSets'].nil? && !options[:no_prompt]
408
437
  price_sets = []
@@ -517,17 +546,17 @@ class Morpheus::Cli::ServicePlanCommand
517
546
  opts.on('--price-sets [LIST]', Array, 'Price set(s), comma separated list of price set IDs') do |list|
518
547
  params['priceSets'] = list.collect {|it| it.to_s.strip.empty? || !it.to_i ? nil : it.to_s.strip}.compact.uniq.collect {|it| {'id' => it.to_i}}
519
548
  end
520
- opts.on('--min-storage NUMBER', String, "Min storage. Assumes GB unless optional modifier specified, ex: 512MB") do |val|
521
- # Storage doesn't get converted to bytes
522
- bytes = parse_bytes_param(val, '--min-storage', 'GB')
523
- ((params['config'] ||= {})['ranges'] ||= {})['minStorage'] = bytes[:number]
524
- (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
549
+ opts.on('--min-storage NUMBER', String, "Min total storage in GB.") do |val|
550
+ ((params['config'] ||= {})['ranges'] ||= {})['minStorage'] = val.to_i
525
551
  end
526
- opts.on('--max-storage NUMBER', String, "Max storage. Assumes GB unless optional modifier specified, ex: 512MB") do |val|
527
- # Storage doesn't get converted to bytes
528
- bytes = parse_bytes_param(val, '--max-storage', 'GB', true)
529
- ((params['config'] ||= {})['ranges'] ||= {})['maxStorage'] = bytes[:number]
530
- (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
552
+ opts.on('--max-storage NUMBER', String, "Max total storage in GB.") do |val|
553
+ ((params['config'] ||= {})['ranges'] ||= {})['maxStorage'] = val.to_i
554
+ end
555
+ opts.on('--min-per-disk-size NUMBER', String, "Min per disk size in GB.") do |val|
556
+ ((params['config'] ||= {})['ranges'] ||= {})['minPerDiskSize'] = val.to_i
557
+ end
558
+ opts.on('--max-per-disk-size NUMBER', String, "Max per disk size in GB.") do |val|
559
+ ((params['config'] ||= {})['ranges'] ||= {})['maxPerDiskSize'] = val.to_i
531
560
  end
532
561
  opts.on('--min-memory NUMBER', String, "Min memory. Assumes MB unless optional modifier specified, ex: 1GB") do |val|
533
562
  # Memory does get converted to bytes
@@ -359,7 +359,7 @@ class Morpheus::Cli::Shell
359
359
  end
360
360
  out << "See the available commands below.\n"
361
361
 
362
- out << "\nCommands:"
362
+ out << "\nCommands:\n"
363
363
  # commands = @morpheus_commands + @shell_commands
364
364
  # @morpheus_commands.sort.each {|cmd|
365
365
  # out << "\t#{cmd.to_s}\n"
@@ -442,7 +442,7 @@ class Morpheus::Cli::Tasks
442
442
  # Result Type
443
443
  if options[:options]['resultType']
444
444
  payload['task']['resultType'] = options[:options]['resultType']
445
- else
445
+ elsif task_type['hasResults']
446
446
  result_types_dropdown = [{"name" => "Value", "value" => "value"}, {"name" => "Exit Code", "value" => "exitCode"}, {"name" => "Key Value", "value" => "keyValue"}, {"name" => "JSON", "value" => "json"}]
447
447
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'resultType', 'fieldLabel' => 'Result Type', 'type' => 'select', 'selectOptions' => result_types_dropdown}], options[:options], @api_client)
448
448
  payload['task']['resultType'] = v_prompt['resultType'] unless v_prompt['resultType'].to_s.empty?
@@ -471,6 +471,7 @@ class Morpheus::Cli::Tasks
471
471
  end
472
472
  end
473
473
  end
474
+ process_special_task_option_typeaheads(task_option_types)
474
475
  # inject file_params into options for file-content prompt
475
476
  # or into taskOptions.script for types not yet using file-content
476
477
  unless file_params.empty?
@@ -859,38 +860,99 @@ class Morpheus::Cli::Tasks
859
860
  target_type = nil
860
861
  instance_ids = []
861
862
  instances = []
863
+ instance_label = nil
862
864
  server_ids = []
863
865
  servers = []
864
- default_refresh_interval = 10
866
+ server_label = nil
867
+ default_refresh_interval = 5
868
+ all_target_types = ['appliance', 'instance', 'instance-label', 'server', 'server-label']
865
869
  optparse = Morpheus::Cli::OptionParser.new do |opts|
866
- opts.banner = subcommand_usage("[task] --instance [instance] [options]")
867
- opts.on('--instance INSTANCE', String, "Instance name or id to execute the task on. This option can be passed more than once.") do |val|
870
+ opts.banner = subcommand_usage("[task] [options]")
871
+ opts.on('--context-type VALUE', String, "Context Type, #{ored_list(all_target_types)}") do |val|
872
+ val = val.downcase
873
+ val = 'appliance' if val == 'none'
874
+ if target_type && target_type != val
875
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
876
+ end
877
+ if !all_target_types.include?(val)
878
+ raise ::OptionParser::InvalidOption.new("'#{val}' is invalid. It must be one of the following: instance, instance-label, server, server-label or appliance")
879
+ end
880
+ target_type = val
881
+ end
882
+ opts.on('--target-type VALUE', String, "alias for context-type") do |val|
883
+ val = val.downcase
884
+ val = 'appliance' if val == 'none'
885
+ if target_type && target_type != val
886
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
887
+ end
888
+ if !all_target_types.include?(val)
889
+ raise ::OptionParser::InvalidOption.new("'#{val}' is invalid. It must be one of the following: instance, instance-label, server, server-label or appliance")
890
+ end
891
+ target_type = val
892
+ end
893
+ opts.add_hidden_option('--target-type')
894
+ opts.on('--instance INSTANCE', String, "Instance name or id to target for execution. This option can be passed more than once.") do |val|
895
+ if target_type && target_type != 'instance'
896
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
897
+ end
868
898
  target_type = 'instance'
869
899
  instance_ids << val
870
900
  end
871
- opts.on('--instances [LIST]', Array, "Instances, comma separated list of instance names or IDs.") do |list|
901
+ opts.on('--instances LIST', Array, "Instances, comma separated list of instance names or IDs.") do |list|
902
+ if target_type && target_type != 'instance'
903
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
904
+ end
872
905
  target_type = 'instance'
873
906
  instance_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
874
907
  end
875
- opts.on('--host HOST', String, "Host name or id to execute the task on. This option can be passed more than once.") do |val|
908
+ opts.on('--instance-label LABEL', String, "Instance Label") do |val|
909
+ if target_type && target_type != 'instance-label'
910
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
911
+ end
912
+ target_type = 'instance-label'
913
+ instance_label = val
914
+ end
915
+ opts.on('--server SERVER', String, "Server name or id to target for execution. This option can be passed more than once.") do |val|
916
+ if target_type && target_type != 'server'
917
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
918
+ end
876
919
  target_type = 'server'
877
920
  server_ids << val
878
921
  end
879
- opts.on('--hosts [LIST]', Array, "Hosts, comma separated list of host names or IDs.") do |list|
922
+ opts.on('--servers LIST', Array, "Servers, comma separated list of host names or IDs.") do |list|
923
+ if target_type && target_type != 'server'
924
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
925
+ end
880
926
  target_type = 'server'
881
927
  server_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
882
928
  end
883
- opts.on('--server HOST', String, "alias for --host") do |val|
929
+ opts.on('--server-label LABEL', String, "Server Label") do |val|
930
+ if target_type && target_type != 'server-label'
931
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
932
+ end
933
+ target_type = 'server-label'
934
+ server_label = val
935
+ end
936
+ opts.on('--host HOST', String, "alias for --server") do |val|
937
+ if target_type && target_type != 'server'
938
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
939
+ end
884
940
  target_type = 'server'
885
941
  server_ids << val
886
942
  end
887
- opts.on('--servers [LIST]', Array, "alias for --hosts") do |list|
943
+ opts.add_hidden_option('--host')
944
+ opts.on('--hosts HOSTS', Array, "alias for --servers") do |list|
945
+ if target_type && target_type != 'server'
946
+ raise ::OptionParser::InvalidOption.new("The --hosts option cannot be combined with another context (#{target_type})")
947
+ end
888
948
  target_type = 'server'
889
949
  server_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
890
950
  end
891
- opts.add_hidden_option('--server')
892
- opts.add_hidden_option('--servers')
951
+ opts.add_hidden_option('--hosts')
893
952
  opts.on('-a', '--appliance', "Execute on the appliance, the target is the appliance itself.") do
953
+ if target_type && target_type != 'appliance'
954
+ raise ::OptionParser::InvalidOption.new("The --appliance option cannot be combined with another context (#{target_type})")
955
+ end
894
956
  target_type = 'appliance'
895
957
  end
896
958
  opts.on('--config [TEXT]', String, "Custom config") do |val|
@@ -921,46 +983,59 @@ class Morpheus::Cli::Tasks
921
983
  payload = options[:payload]
922
984
  payload.deep_merge!({'job' => passed_options}) unless passed_options.empty?
923
985
  else
924
- # always parse instances and/or hosts
925
- if instance_ids.size > 0 && server_ids.size > 0
926
- raise_command_error "Pass --instance or --host, not both.\n#{optparse}"
927
- elsif instance_ids.size > 0
986
+ # prompt for target type and target
987
+ if target_type.nil?
988
+ # todo: Need api to fetch available Context Types for taskId/workflowId
989
+ available_target_types = get_available_contexts_for_task(task)
990
+ default_target_type = available_target_types.first ? available_target_types.first['name'] : nil
991
+ if !available_target_types.empty?
992
+ default_target_type = available_target_types.first ? available_target_types.first['name'] : nil
993
+ target_type = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'context-type', 'fieldName' => 'targetType', 'fieldLabel' => 'Context Type', 'type' => 'select', 'selectOptions' => available_target_types, 'defaultValue' => default_target_type, 'required' => true, 'description' => 'Context Type determines the type of target(s) for the execution'}], options[:options], @api_client)['targetType']
994
+ end
995
+ end
996
+ if target_type
997
+ params['targetType'] = target_type
998
+ end
999
+ if target_type == 'instance'
1000
+ if instance_ids.empty?
1001
+ instance_ids_value = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'instances', 'fieldName' => 'instances', 'fieldLabel' => 'Instance(s)', 'type' => 'text', 'required' => true, 'description' => 'Instances, comma separated list of instance names or IDs.'}], options[:options], @api_client)['instances']
1002
+ instance_ids = parse_array(instance_ids_value)
1003
+ end
928
1004
  instance_ids.each do |instance_id|
929
1005
  instance = find_instance_by_name_or_id(instance_id)
930
1006
  return 1 if instance.nil?
931
1007
  instances << instance
932
1008
  end
933
1009
  params['instances'] = instances.collect {|it| it['id'] }
934
- elsif server_ids.size > 0
1010
+ elsif target_type == 'instance-label'
1011
+ if instance_label.nil?
1012
+ instance_label = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'instance-label', 'fieldName' => 'instanceLabel', 'fieldLabel' => 'Instance Label', 'type' => 'text', 'required' => true, 'description' => 'Instance Label'}], options[:options], @api_client)['instanceLabel']
1013
+ end
1014
+ # params['config'] ||= {}
1015
+ # params['config']['instanceLabel'] = instance_label
1016
+ params['instanceLabel'] = instance_label
1017
+ elsif target_type == 'server'
1018
+ if server_ids.empty?
1019
+ server_ids_value = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'servers', 'fieldName' => 'servers', 'fieldLabel' => 'Server(s)', 'type' => 'text', 'required' => true, 'description' => 'Servers, comma separated list of server names or IDs.'}], options[:options], @api_client)['servers']
1020
+ server_ids = parse_array(server_ids_value)
1021
+ end
935
1022
  server_ids.each do |server_id|
936
1023
  server = find_server_by_name_or_id(server_id)
937
1024
  return 1 if server.nil?
938
1025
  servers << server
939
1026
  end
940
1027
  params['servers'] = servers.collect {|it| it['id'] }
941
- end
942
- # validate requires inputs based on task executeTarget
943
- if task['executeTarget'] == 'resource'
944
- if instance_ids.empty? && server_ids.empty?
945
- # todo: prompt for Context: None,Instance,Server and then Instance(s) or Server(s)
946
- raise_command_error "missing required option: --instance or --host\n#{optparse}"
1028
+ elsif target_type == 'server-label'
1029
+ if server_label.nil?
1030
+ server_label = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'server-label', 'fieldName' => 'serverLabel', 'fieldLabel' => 'Server Label', 'type' => 'text', 'required' => true, 'description' => 'Server Label'}], options[:options], @api_client)['serverLabel']
947
1031
  end
948
- elsif task['executeTarget'] == 'local'
949
- # no targetType required for local
950
- elsif task['executeTarget'] == 'remote'
951
- # not sure about this one
952
- else
953
- # unknown executeTarget
1032
+ # params['config'] ||= {}
1033
+ # params['config']['serverLabel'] = server_label
1034
+ params['serverLabel'] = server_label
954
1035
  end
955
-
956
-
957
-
958
1036
  # todo: prompt to task optionTypes for customOptions
959
1037
  if task['optionTypes']
960
1038
 
961
- end
962
- if target_type
963
- params['targetType'] = target_type
964
1039
  end
965
1040
  job_payload = {}
966
1041
  job_payload.deep_merge!(params)
@@ -1293,4 +1368,24 @@ class Morpheus::Cli::Tasks
1293
1368
  end
1294
1369
  end
1295
1370
 
1371
+ def process_special_task_option_typeaheads(option_types)
1372
+ # massage special task typeaheads options
1373
+ # this makes us all sad
1374
+ option_types.each do |option_type|
1375
+ if option_type['type'] == 'typeahead'
1376
+ if ['operationalWorkflowName','containerScript','containerTemplate'].include?(option_type['code'])
1377
+ option_type.deep_merge!({'config' => {'valueField' => 'name'}})
1378
+ end
1379
+ elsif option_type['type'] == 'hidden'
1380
+ if ['operationalWorkflowId','containerScriptId','containerTemplateId'].include?(option_type['code'])
1381
+ option_type['processValue'] = lambda {|val|
1382
+ if val.to_s.empty?
1383
+ selected_option = Morpheus::Cli::OptionTypes.get_last_select()
1384
+ selected_option ? selected_option['value'] : nil
1385
+ end
1386
+ }
1387
+ end
1388
+ end
1389
+ end
1390
+ end
1296
1391
  end
@@ -550,40 +550,101 @@ class Morpheus::Cli::Workflows
550
550
  target_type = nil
551
551
  instance_ids = []
552
552
  instances = []
553
+ instance_label = nil
553
554
  server_ids = []
554
555
  servers = []
555
- default_refresh_interval = 10
556
+ server_label = nil
557
+ default_refresh_interval = 5
558
+ all_target_types = ['appliance', 'instance', 'instance-label', 'server', 'server-label']
556
559
  optparse = Morpheus::Cli::OptionParser.new do |opts|
557
- opts.banner = subcommand_usage("[workflow] --instance [instance] [options]")
558
- opts.on('--instance INSTANCE', String, "Instance name or id to execute the workflow on. This option can be passed more than once.") do |val|
560
+ opts.banner = subcommand_usage("[workflow] [options]")
561
+ opts.on('--context-type VALUE', String, "Context Type, #{ored_list(all_target_types)}") do |val|
562
+ val = val.downcase
563
+ val = 'appliance' if val == 'none'
564
+ if target_type && target_type != val
565
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
566
+ end
567
+ if !all_target_types.include?(val)
568
+ raise ::OptionParser::InvalidOption.new("'#{val}' is invalid. It must be one of the following: instance, instance-label, server, server-label or appliance")
569
+ end
570
+ target_type = val
571
+ end
572
+ opts.on('--target-type VALUE', String, "alias for context-type") do |val|
573
+ val = val.downcase
574
+ val = 'appliance' if val == 'none'
575
+ if target_type && target_type != val
576
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
577
+ end
578
+ if !all_target_types.include?(val)
579
+ raise ::OptionParser::InvalidOption.new("'#{val}' is invalid. It must be one of the following: instance, instance-label, server, server-label or appliance")
580
+ end
581
+ target_type = val
582
+ end
583
+ opts.add_hidden_option('--target-type')
584
+ opts.on('--instance INSTANCE', String, "Instance name or id to target for execution. This option can be passed more than once.") do |val|
585
+ if target_type && target_type != 'instance'
586
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
587
+ end
559
588
  target_type = 'instance'
560
589
  instance_ids << val
561
590
  end
562
- opts.on('--instances [LIST]', Array, "Instances, comma separated list of instance names or IDs.") do |list|
591
+ opts.on('--instances LIST', Array, "Instances, comma separated list of instance names or IDs.") do |list|
592
+ if target_type && target_type != 'instance'
593
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
594
+ end
563
595
  target_type = 'instance'
564
596
  instance_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
565
597
  end
566
- opts.on('--host HOST', String, "Host name or id to execute the workflow on. This option can be passed more than once.") do |val|
598
+ opts.on('--instance-label LABEL', String, "Instance Label") do |val|
599
+ if target_type && target_type != 'instance-label'
600
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
601
+ end
602
+ target_type = 'instance-label'
603
+ instance_label = val
604
+ end
605
+ opts.on('--server SERVER', String, "Server name or id to target for execution. This option can be passed more than once.") do |val|
606
+ if target_type && target_type != 'server'
607
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
608
+ end
567
609
  target_type = 'server'
568
610
  server_ids << val
569
611
  end
570
- opts.on('--hosts [LIST]', Array, "Hosts, comma separated list of host names or IDs.") do |list|
612
+ opts.on('--servers LIST', Array, "Servers, comma separated list of host names or IDs.") do |list|
613
+ if target_type && target_type != 'server'
614
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
615
+ end
571
616
  target_type = 'server'
572
617
  server_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
573
618
  end
574
- opts.on('--server HOST', String, "alias for --host") do |val|
619
+ opts.on('--server-label LABEL', String, "Server Label") do |val|
620
+ if target_type && target_type != 'server-label'
621
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
622
+ end
623
+ target_type = 'server-label'
624
+ server_label = val
625
+ end
626
+ opts.on('--host HOST', String, "alias for --server") do |val|
627
+ if target_type && target_type != 'server'
628
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
629
+ end
575
630
  target_type = 'server'
576
631
  server_ids << val
577
632
  end
578
- opts.on('--servers [LIST]', Array, "alias for --hosts") do |list|
633
+ opts.add_hidden_option('--host')
634
+ opts.on('--hosts HOSTS', Array, "alias for --servers") do |list|
635
+ if target_type && target_type != 'server'
636
+ raise ::OptionParser::InvalidOption.new("The --hosts option cannot be combined with another context (#{target_type})")
637
+ end
579
638
  target_type = 'server'
580
639
  server_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
581
640
  end
641
+ opts.add_hidden_option('--hosts')
582
642
  opts.on('-a', '--appliance', "Execute on the appliance, the target is the appliance itself.") do
643
+ if target_type && target_type != 'appliance'
644
+ raise ::OptionParser::InvalidOption.new("The --appliance option cannot be combined with another context (#{target_type})")
645
+ end
583
646
  target_type = 'appliance'
584
647
  end
585
- opts.add_hidden_option('--server')
586
- opts.add_hidden_option('--servers')
587
648
  opts.on('--config [TEXT]', String, "Custom config") do |val|
588
649
  params['customConfig'] = val.to_s
589
650
  end
@@ -611,27 +672,55 @@ class Morpheus::Cli::Workflows
611
672
  payload = options[:payload]
612
673
  payload.deep_merge!({'job' => passed_options}) unless passed_options.empty?
613
674
  else
614
- if instance_ids.size > 0 && server_ids.size > 0
615
- raise_command_error "Pass --instance or --host, not both.\n#{optparse}"
616
- elsif instance_ids.size > 0
675
+ # prompt for target type and target
676
+ if target_type.nil?
677
+ # todo: Need api to fetch available Context Types for taskId/workflowId
678
+ available_target_types = get_available_contexts_for_workflow(workflow)
679
+ default_target_type = available_target_types.first ? available_target_types.first['name'] : nil
680
+ if !available_target_types.empty?
681
+ default_target_type = available_target_types.first ? available_target_types.first['name'] : nil
682
+ target_type = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'context-type', 'fieldName' => 'targetType', 'fieldLabel' => 'Context Type', 'type' => 'select', 'selectOptions' => available_target_types, 'defaultValue' => default_target_type, 'required' => true, 'description' => 'Context Type determines the type of target(s) for the execution'}], options[:options], @api_client)['targetType']
683
+ end
684
+ end
685
+ if target_type
686
+ params['targetType'] = target_type
687
+ end
688
+ if target_type == 'instance'
689
+ if instance_ids.empty?
690
+ instance_ids_value = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'instances', 'fieldName' => 'instances', 'fieldLabel' => 'Instance(s)', 'type' => 'text', 'required' => true, 'description' => 'Instances, comma separated list of instance names or IDs.'}], options[:options], @api_client)['instances']
691
+ instance_ids = parse_array(instance_ids_value)
692
+ end
617
693
  instance_ids.each do |instance_id|
618
694
  instance = find_instance_by_name_or_id(instance_id)
619
695
  return 1 if instance.nil?
620
696
  instances << instance
621
697
  end
622
698
  params['instances'] = instances.collect {|it| it['id'] }
623
- elsif server_ids.size > 0
699
+ elsif target_type == 'instance-label'
700
+ if instance_label.nil?
701
+ instance_label = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'instance-label', 'fieldName' => 'instanceLabel', 'fieldLabel' => 'Instance Label', 'type' => 'text', 'required' => true, 'description' => 'Instance Label'}], options[:options], @api_client)['instanceLabel']
702
+ end
703
+ # params['config'] ||= {}
704
+ # params['config']['instanceLabel'] = instance_label
705
+ params['instanceLabel'] = instance_label
706
+ elsif target_type == 'server'
707
+ if server_ids.empty?
708
+ server_ids_value = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'servers', 'fieldName' => 'servers', 'fieldLabel' => 'Server(s)', 'type' => 'text', 'required' => true, 'description' => 'Servers, comma separated list of server names or IDs.'}], options[:options], @api_client)['servers']
709
+ server_ids = parse_array(server_ids_value)
710
+ end
624
711
  server_ids.each do |server_id|
625
712
  server = find_server_by_name_or_id(server_id)
626
713
  return 1 if server.nil?
627
714
  servers << server
628
715
  end
629
716
  params['servers'] = servers.collect {|it| it['id'] }
630
- elsif target_type == 'appliance'
631
- # cool, run it locally.
632
- else
633
- # cool, run it locally.
634
- #raise_command_error "missing required option: --instance, --host or --appliance\n#{optparse}"
717
+ elsif target_type == 'server-label'
718
+ if server_label.nil?
719
+ server_label = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'server-label', 'fieldName' => 'serverLabel', 'fieldLabel' => 'Server Label', 'type' => 'text', 'required' => true, 'description' => 'Server Label'}], options[:options], @api_client)['serverLabel']
720
+ end
721
+ # params['config'] ||= {}
722
+ # params['config']['serverLabel'] = server_label
723
+ params['serverLabel'] = server_label
635
724
  end
636
725
 
637
726
  # prompt to workflow optionTypes for customOptions
@@ -643,9 +732,6 @@ class Morpheus::Cli::Workflows
643
732
  }
644
733
  custom_options = Morpheus::Cli::OptionTypes.prompt(custom_option_types, options[:options], @api_client, {})
645
734
  end
646
- if target_type
647
- params['targetType'] = target_type
648
- end
649
735
  job_payload = {}
650
736
  job_payload.deep_merge!(params)
651
737
  passed_options.delete('customOptions')
@@ -41,8 +41,8 @@ module Morpheus::Cli::JobsHelper
41
41
  "Created By" => lambda {|it| it[:created_by]},
42
42
  "Duration" => lambda {|it| it[:duration]},
43
43
  "Status" => lambda {|it| it[:status]},
44
- "Error" => lambda {|it| options[:details] ? it[:error] : truncate_string(it[:error], 32) },
45
- "Output" => lambda {|it| options[:details] ? it[:output] : truncate_string(it[:output], 32) }
44
+ "Error" => lambda {|it| options[:details] ? it[:error].to_s.strip : truncate_string(it[:error], 32).to_s.strip.gsub("\n", ' ') },
45
+ "Output" => lambda {|it| options[:details] ? it[:output].to_s.strip : truncate_string(it[:output], 32).to_s.strip.gsub("\n", ' ') }
46
46
  }
47
47
  print as_pretty_table(events.collect {|it| get_process_event_data(it)}, event_columns.upcase_keys!, options)
48
48
  end
@@ -147,7 +147,7 @@ module Morpheus::Cli::JobsHelper
147
147
  # refresh execution request until it is finished
148
148
  # returns json response data of the last execution request when status reached 'completed' or 'failed'
149
149
  def wait_for_job_execution(job_execution_id, options={}, print_output = true)
150
- refresh_interval = 10
150
+ refresh_interval = 5
151
151
  if options[:refresh_interval].to_i > 0
152
152
  refresh_interval = options[:refresh_interval]
153
153
  end
@@ -168,5 +168,32 @@ module Morpheus::Cli::JobsHelper
168
168
  end
169
169
 
170
170
 
171
+ def get_available_contexts_for_task(task)
172
+ #If task has target of resource, then CAN NOT run it local
173
+ targets = []
174
+ has_resource = task['executeTarget'] == 'resource'
175
+ if !has_resource
176
+ targets << {'name' => 'None', 'value' => 'appliance'}
177
+ end
178
+ targets << {'name' => 'Instance', 'value' => 'instance'}
179
+ targets << {'name' => 'Instance Label', 'value' => 'instance-label'}
180
+ targets << {'name' => 'Server', 'value' => 'server'}
181
+ targets << {'name' => 'Server Label', 'value' => 'server-label'}
182
+ return targets
183
+ end
184
+
185
+ def get_available_contexts_for_workflow(workflow)
186
+ #If any task has target of resource, then CAN NOT run it local
187
+ targets = []
188
+ has_resource = workflow['taskSetTasks'].find {|task| task['executeTarget'] == 'resource' }
189
+ if !has_resource
190
+ targets << {'name' => 'None', 'value' => 'appliance'}
191
+ end
192
+ targets << {'name' => 'Instance', 'value' => 'instance'}
193
+ targets << {'name' => 'Instance Label', 'value' => 'instance-label'}
194
+ targets << {'name' => 'Server', 'value' => 'server'}
195
+ targets << {'name' => 'Server Label', 'value' => 'server-label'}
196
+ return targets
197
+ end
171
198
 
172
199
  end
@@ -88,36 +88,12 @@ module Morpheus::Cli::ProcessesHelper
88
88
 
89
89
  # decolorize, remove newlines and truncate for table cell
90
90
  def format_process_error(process, max_length=20, return_color=cyan)
91
- #return max_length ? truncate_string(process['error'], max_length) : process['error']
92
- out = ""
93
- if process['error']
94
- # lines = process['error'].split("\n").collect {|line| reset + "#{line.to_s.strip}" }
95
- # lines = process['error'].split("\n").collect {|line| "#{line.to_s.strip}" }
96
- lines = [process['error']]
97
- out = lines.join(" ")
98
- if max_length
99
- out = truncate_string(out, max_length)
100
- end
101
- out << return_color if return_color
102
- end
103
- out
91
+ truncate_string(process['error'].to_s.strip.gsub("\n", " "), max_length)
104
92
  end
105
93
 
106
94
  # decolorize, remove newlines and truncate for table cell
107
95
  def format_process_output(process, max_length=20, return_color=cyan)
108
- return max_length ? truncate_string(process['output'], max_length) : process['output']
109
- out = ""
110
- if process['output']
111
- # lines = process['output'].split("\n").collect {|line| reset + "#{line.to_s.strip}" }
112
- # lines = process['error'].split("\n").collect {|line| "#{line.to_s.strip}" }
113
- lines = [process['output']]
114
- out = lines.join(" ")
115
- if max_length
116
- out = truncate_string(out, max_length)
117
- end
118
- out << return_color if return_color
119
- end
120
- out
96
+ truncate_string(process['output'].to_s.strip.gsub("\n", " "), max_length)
121
97
  end
122
98
 
123
99
  # format for either ETA/Duration
@@ -778,6 +778,7 @@ module Morpheus::Cli::ProvisioningHelper
778
778
 
779
779
  resource_pool_options = options_interface.options_for_source('zonePools', {groupId: group_id, siteId: group_id, zoneId: cloud_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], layoutId: layout["id"]}.merge(service_plan.nil? ? {} : {planId: service_plan["id"]}))['data']
780
780
  resource_pool = resource_pool_options.find {|opt| opt['id'] == options[:resource_pool].to_i} if options[:resource_pool]
781
+ pool_required = provision_type["zonePoolRequired"]
781
782
 
782
783
  if resource_pool
783
784
  pool_id = resource_pool['id']
@@ -785,7 +786,7 @@ module Morpheus::Cli::ProvisioningHelper
785
786
  if options[:default_resource_pool]
786
787
  default_resource_pool = resource_pool_options.find {|rp| rp['id'] == options[:default_resource_pool]}
787
788
  end
788
- resource_pool_option_type ||= {'fieldContext' => 'config', 'fieldName' => 'resourcePoolId', 'type' => 'select', 'fieldLabel' => 'Resource Pool', 'selectOptions' => resource_pool_options, 'required' => true, 'skipSingleOption' => true, 'description' => 'Select resource pool.', 'defaultValue' => default_resource_pool ? default_resource_pool['name'] : nil}
789
+ resource_pool_option_type ||= {'fieldContext' => 'config', 'fieldName' => 'resourcePoolId', 'type' => 'select', 'fieldLabel' => 'Resource Pool', 'selectOptions' => resource_pool_options, 'required' => pool_required, 'skipSingleOption' => true, 'description' => 'Select resource pool.', 'defaultValue' => default_resource_pool ? default_resource_pool['name'] : nil}
789
790
  resource_pool_prompt = Morpheus::Cli::OptionTypes.prompt([resource_pool_option_type],options[:options],api_client,{}, no_prompt, true)
790
791
  resource_pool_prompt.deep_compact!
791
792
  payload.deep_merge!(resource_pool_prompt)
@@ -814,7 +814,7 @@ module Morpheus
814
814
  api_params ||= {}
815
815
  api_params['query'] = query_value
816
816
  # skip refresh if you just hit enter
817
- if !query_value.empty?
817
+ if !query_value.empty? || (select_options.nil? || select_options.empty?)
818
818
  select_options = load_options(option_type, api_client, api_params, query_value)
819
819
  end
820
820
 
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "6.0.0"
4
+ VERSION = "6.0.1"
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: morpheus-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0
4
+ version: 6.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Estes
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2023-03-02 00:00:00.000000000 Z
14
+ date: 2023-03-14 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler