morpheus-cli 5.2.0 → 5.2.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 +4 -4
- data/.gitignore +1 -0
- data/lib/morpheus/cli/hosts.rb +31 -2
- data/lib/morpheus/cli/instances.rb +75 -44
- data/lib/morpheus/cli/invoices_command.rb +4 -3
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +12 -5
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/virtual_images.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4409a856333a1b2957649aac67f62677b943706754ebc810c614fa5f879b120d
|
4
|
+
data.tar.gz: bb968663da852f4ea631fc9b0c89c115f6fb1067049585953c33b7c24e4b284a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2be204c65dd3c2a35ce5ac7170e70a2297aa34ee1348db2fc736a5915a2678c0c45361269a801db228f202e27dcdcfaaf69afc7f49d9eded429c16d5381dd28f
|
7
|
+
data.tar.gz: a5018a327ae43078e4661c1c3744e75b6cc1adfc8854ca09d99a0a47f8e0678d2f8914b46d724bd8189011b13a42f2ac5779b3980611af1655cdebc4393a3e53
|
data/.gitignore
CHANGED
data/lib/morpheus/cli/hosts.rb
CHANGED
@@ -530,9 +530,10 @@ class Morpheus::Cli::Hosts
|
|
530
530
|
puts records_as_csv([json_response['server']], options)
|
531
531
|
return 0
|
532
532
|
end
|
533
|
-
server = json_response['server']
|
533
|
+
server = json_response['server'] || json_response['host'] || {}
|
534
534
|
#stats = server['stats'] || json_response['stats'] || {}
|
535
535
|
stats = json_response['stats'] || {}
|
536
|
+
tags = server['tags'] || server['metadata']
|
536
537
|
title = "Host Details"
|
537
538
|
print_h1 title, [], options
|
538
539
|
print cyan
|
@@ -541,6 +542,7 @@ class Morpheus::Cli::Hosts
|
|
541
542
|
"Name" => 'name',
|
542
543
|
"Hostname" => 'hostname',
|
543
544
|
"Description" => 'description',
|
545
|
+
"Tags" => lambda {|it| tags ? format_metadata(tags) : '' },
|
544
546
|
"Owner" => lambda {|it| it['owner'] ? it['owner']['username'] : '' },
|
545
547
|
"Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
546
548
|
#"Group" => lambda {|it| it['group'] ? it['group']['name'] : '' },
|
@@ -564,6 +566,7 @@ class Morpheus::Cli::Hosts
|
|
564
566
|
# server_columns.delete("Tenant") if multi_tenant != true
|
565
567
|
server_columns.delete("Cost") if server['hourlyCost'].to_f == 0
|
566
568
|
server_columns.delete("Price") if server['hourlyPrice'].to_f == 0 || server['hourlyPrice'] == server['hourlyCost']
|
569
|
+
server_columns.delete("Tags") if tags.nil? || tags.empty?
|
567
570
|
|
568
571
|
print_description_list(server_columns, server)
|
569
572
|
|
@@ -990,6 +993,19 @@ class Morpheus::Cli::Hosts
|
|
990
993
|
opts.on('--power-schedule-type ID', String, "Power Schedule Type ID") do |val|
|
991
994
|
params['powerScheduleType'] = val == "null" ? nil : val
|
992
995
|
end
|
996
|
+
opts.on('--tags LIST', String, "Tags in the format 'name:value, name:value'. This will add and remove tags.") do |val|
|
997
|
+
options[:tags] = val
|
998
|
+
end
|
999
|
+
opts.on('--metadata LIST', String, "Alias for --tags.") do |val|
|
1000
|
+
options[:tags] = val
|
1001
|
+
end
|
1002
|
+
opts.add_hidden_option('--metadata')
|
1003
|
+
opts.on('--add-tags TAGS', String, "Add Tags in the format 'name:value, name:value'. This will only add/update tags.") do |val|
|
1004
|
+
options[:add_tags] = val
|
1005
|
+
end
|
1006
|
+
opts.on('--remove-tags TAGS', String, "Remove Tags in the format 'name, name:value'. This removes tags, the :value component is optional and must match if passed.") do |val|
|
1007
|
+
options[:remove_tags] = val
|
1008
|
+
end
|
993
1009
|
# opts.on('--created-by ID', String, "Created By User ID") do |val|
|
994
1010
|
# params['createdById'] = val
|
995
1011
|
# end
|
@@ -1008,6 +1024,18 @@ class Morpheus::Cli::Hosts
|
|
1008
1024
|
new_group = nil
|
1009
1025
|
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
1010
1026
|
params.deep_merge!(passed_options) unless passed_options.empty?
|
1027
|
+
# metadata tags
|
1028
|
+
if options[:tags]
|
1029
|
+
params['tags'] = parse_metadata(options[:tags])
|
1030
|
+
else
|
1031
|
+
# params['tags'] = prompt_metadata(options)
|
1032
|
+
end
|
1033
|
+
if options[:add_tags]
|
1034
|
+
params['addTags'] = parse_metadata(options[:add_tags])
|
1035
|
+
end
|
1036
|
+
if options[:remove_tags]
|
1037
|
+
params['removeTags'] = parse_metadata(options[:remove_tags])
|
1038
|
+
end
|
1011
1039
|
payload = nil
|
1012
1040
|
if options[:payload]
|
1013
1041
|
payload = options[:payload]
|
@@ -1919,7 +1947,8 @@ class Morpheus::Cli::Hosts
|
|
1919
1947
|
snapshot_column_definitions = {
|
1920
1948
|
"ID" => lambda {|it| it['id'] },
|
1921
1949
|
"Name" => lambda {|it| it['name'] },
|
1922
|
-
"Description" => lambda {|it| it['
|
1950
|
+
"Description" => lambda {|it| it['description'] },
|
1951
|
+
# "Type" => lambda {|it| it['snapshotType'] },
|
1923
1952
|
"Date Created" => lambda {|it| format_local_dt(it['snapshotCreated']) },
|
1924
1953
|
"Status" => lambda {|it| format_snapshot_status(it) }
|
1925
1954
|
}
|
@@ -459,17 +459,58 @@ class Morpheus::Cli::Instances
|
|
459
459
|
options[:instance_name] = args[0]
|
460
460
|
end
|
461
461
|
|
462
|
-
# use active group by default
|
463
|
-
options[:group] ||= @active_group_id
|
464
|
-
options[:select_datastore] = true
|
465
|
-
options[:name_required] = true
|
466
462
|
begin
|
467
463
|
payload = nil
|
468
464
|
if options[:payload]
|
469
465
|
payload = options[:payload]
|
470
466
|
# support -O OPTION switch on top of --payload
|
471
467
|
payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
468
|
+
# obviously should support every option that prompt supports on top of -- payload as well
|
469
|
+
# group, cloud and type for now
|
470
|
+
# todo: also support :layout, service_plan, :resource_pool, etc.
|
471
|
+
group = nil
|
472
|
+
if options[:group]
|
473
|
+
group = find_group_by_name_or_id_for_provisioning(options[:group])
|
474
|
+
if group.nil?
|
475
|
+
return 1, "group not found by #{options[:group]}"
|
476
|
+
end
|
477
|
+
#payload["siteId"] = group["id"]
|
478
|
+
payload.deep_merge!({"instance" => {"site" => {"id" => group["id"]} } })
|
479
|
+
end
|
480
|
+
if options[:cloud]
|
481
|
+
group_id = group ? group["id"] : ((payload["instance"] && payload["instance"]["site"].is_a?(Hash)) ? payload["instance"]["site"]["id"] : nil)
|
482
|
+
cloud = find_cloud_by_name_or_id_for_provisioning(group_id, options[:cloud])
|
483
|
+
if cloud.nil?
|
484
|
+
return 1, "cloud not found by #{options[:cloud]}"
|
485
|
+
end
|
486
|
+
payload["zoneId"] = cloud["id"]
|
487
|
+
payload.deep_merge!({"instance" => {"cloud" => cloud["name"] } })
|
488
|
+
end
|
489
|
+
if options[:cloud]
|
490
|
+
group_id = group ? group["id"] : ((payload["instance"] && payload["instance"]["site"].is_a?(Hash)) ? payload["instance"]["site"]["id"] : nil)
|
491
|
+
cloud = find_cloud_by_name_or_id_for_provisioning(group_id, options[:cloud])
|
492
|
+
if cloud.nil?
|
493
|
+
return 1, "cloud not found by #{options[:cloud]}"
|
494
|
+
end
|
495
|
+
payload["zoneId"] = cloud["id"]
|
496
|
+
payload.deep_merge!({"instance" => {"cloud" => cloud["name"] } })
|
497
|
+
end
|
498
|
+
if options[:instance_type_code]
|
499
|
+
# should just use find_instance_type_by_name_or_id
|
500
|
+
# note that the api actually will match name name or code
|
501
|
+
instance_type = (options[:instance_type_code].to_s =~ /\A\d{1,}\Z/) ? find_instance_type_by_id(options[:instance_type_code]) : find_instance_type_by_code(options[:instance_type_code])
|
502
|
+
if instance_type.nil?
|
503
|
+
return 1, "instance type not found by #{options[:cloud]}"
|
504
|
+
end
|
505
|
+
payload.deep_merge!({"instance" => {"type" => instance_type["code"] } })
|
506
|
+
payload.deep_merge!({"instance" => {"instanceType" => {"code" => instance_type["code"]} } })
|
507
|
+
end
|
508
|
+
|
472
509
|
else
|
510
|
+
# use active group by default
|
511
|
+
options[:group] ||= @active_group_id
|
512
|
+
options[:select_datastore] = true
|
513
|
+
options[:name_required] = true
|
473
514
|
# prompt for all the instance configuration options
|
474
515
|
# this provisioning helper method handles all (most) of the parsing and prompting
|
475
516
|
# and it relies on the method to exit non-zero on error, like a bad CLOUD or TYPE value
|
@@ -562,16 +603,17 @@ class Morpheus::Cli::Instances
|
|
562
603
|
opts.on('--group GROUP', String, "Group Name or ID") do |val|
|
563
604
|
options[:group] = val
|
564
605
|
end
|
565
|
-
opts.on('--
|
566
|
-
|
606
|
+
opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
|
607
|
+
params['labels'] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
|
567
608
|
end
|
568
|
-
opts.on('--
|
569
|
-
options[:
|
609
|
+
opts.on('--tags LIST', String, "Tags in the format 'name:value, name:value'. This will add and remove tags.") do |val|
|
610
|
+
options[:tags] = val
|
570
611
|
end
|
571
|
-
opts.
|
572
|
-
|
573
|
-
|
574
|
-
|
612
|
+
opts.on('--add-tags TAGS', String, "Add Tags in the format 'name:value, name:value'. This will only add/update tags.") do |val|
|
613
|
+
options[:add_tags] = val
|
614
|
+
end
|
615
|
+
opts.on('--remove-tags TAGS', String, "Remove Tags in the format 'name, name:value'. This removes tags, the :value component is optional and must match if passed.") do |val|
|
616
|
+
options[:remove_tags] = val
|
575
617
|
end
|
576
618
|
opts.on('--power-schedule-type ID', String, "Power Schedule Type ID") do |val|
|
577
619
|
params['powerScheduleType'] = val == "null" ? nil : val
|
@@ -627,32 +669,17 @@ class Morpheus::Cli::Instances
|
|
627
669
|
payload['instance']['site'] = {'id' => group['id']}
|
628
670
|
end
|
629
671
|
# metadata tags
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
metadata = []
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
# merge IDs from current metadata
|
642
|
-
# todo: should allow quoted semicolons..
|
643
|
-
metadata_list = options[:metadata].split(",").select {|it| !it.to_s.empty? }
|
644
|
-
metadata_list = metadata_list.collect do |it|
|
645
|
-
metadata_pair = it.split(":")
|
646
|
-
if metadata_pair.size == 1 && it.include?("=")
|
647
|
-
metadata_pair = it.split("=")
|
648
|
-
end
|
649
|
-
row = {}
|
650
|
-
row['name'] = metadata_pair[0].to_s.strip
|
651
|
-
row['value'] = metadata_pair[1].to_s.strip
|
652
|
-
row
|
653
|
-
end
|
654
|
-
payload['instance']['metadata'] = metadata_list
|
655
|
-
end
|
672
|
+
if options[:tags]
|
673
|
+
# api version 4.2.5 and later supports tags, older versions expect metadata
|
674
|
+
# todo: use tags instead like everywhere else
|
675
|
+
# payload['instance']['tags'] = parse_metadata(options[:tags])
|
676
|
+
payload['instance']['metadata'] = parse_metadata(options[:tags])
|
677
|
+
end
|
678
|
+
if options[:add_tags]
|
679
|
+
payload['instance']['addTags'] = parse_metadata(options[:add_tags])
|
680
|
+
end
|
681
|
+
if options[:remove_tags]
|
682
|
+
payload['instance']['removeTags'] = parse_metadata(options[:remove_tags])
|
656
683
|
end
|
657
684
|
if payload['instance'].empty? && params.empty? && options[:owner].nil?
|
658
685
|
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
@@ -1256,11 +1283,14 @@ class Morpheus::Cli::Instances
|
|
1256
1283
|
# metadata tags used to be returned as metadata and are now returned as tags
|
1257
1284
|
# the problem is tags is what we used to call Labels (keywords)
|
1258
1285
|
# the api will change to tags and labels, so handle the old format as long as metadata is returned.
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1286
|
+
labels = nil
|
1287
|
+
tags = nil
|
1288
|
+
if instance.key?('labels')
|
1289
|
+
labels = instance['labels']
|
1290
|
+
tags = instance['tags']
|
1262
1291
|
else
|
1263
|
-
|
1292
|
+
labels = instance['tags']
|
1293
|
+
tags = instance['metadata']
|
1264
1294
|
end
|
1265
1295
|
# containers are fetched via separate api call
|
1266
1296
|
containers = nil
|
@@ -1302,7 +1332,7 @@ class Morpheus::Cli::Instances
|
|
1302
1332
|
# "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
|
1303
1333
|
# "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
|
1304
1334
|
"Environment" => 'instanceContext',
|
1305
|
-
"Labels" => lambda {|it|
|
1335
|
+
"Labels" => lambda {|it| labels ? labels.join(',') : '' },
|
1306
1336
|
"Tags" => lambda {|it| tags ? tags.collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' },
|
1307
1337
|
"Owner" => lambda {|it|
|
1308
1338
|
if it['owner']
|
@@ -3020,7 +3050,8 @@ EOT
|
|
3020
3050
|
snapshot_column_definitions = {
|
3021
3051
|
"ID" => lambda {|it| it['id'] },
|
3022
3052
|
"Name" => lambda {|it| it['name'] },
|
3023
|
-
"Description" => lambda {|it| it['
|
3053
|
+
"Description" => lambda {|it| it['description'] },
|
3054
|
+
# "Type" => lambda {|it| it['snapshotType'] },
|
3024
3055
|
"Date Created" => lambda {|it| format_local_dt(it['snapshotCreated']) },
|
3025
3056
|
"Status" => lambda {|it| format_snapshot_status(it) }
|
3026
3057
|
}
|
@@ -272,7 +272,7 @@ class Morpheus::Cli::InvoicesCommand
|
|
272
272
|
{"ESTIMATE" => lambda {|it| format_boolean(it['estimate']) } },
|
273
273
|
{"ACTIVE" => lambda {|it| format_boolean(it['active']) } },
|
274
274
|
{"ITEMS" => lambda {|it| it['lineItems'].size rescue '' } },
|
275
|
-
{"TAGS" => lambda {|it| it['metadata'] ? it['metadata'].collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' } },
|
275
|
+
{"TAGS" => lambda {|it| (it['metadata'] || it['tags']) ? (it['metadata'] || it['tags']).collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' } },
|
276
276
|
]
|
277
277
|
if show_projects
|
278
278
|
columns += [
|
@@ -438,7 +438,7 @@ EOT
|
|
438
438
|
"Ref Start" => lambda {|it| format_dt(it['refStart']) },
|
439
439
|
"Ref End" => lambda {|it| format_dt(it['refEnd']) },
|
440
440
|
"Items" => lambda {|it| it['lineItems'].size rescue '' },
|
441
|
-
"Tags" => lambda {|it| it['metadata'] ? it['metadata'].collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' },
|
441
|
+
"Tags" => lambda {|it| (it['metadata'] || it['tags']) ? (it['metadata'] || it['tags']).collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' },
|
442
442
|
"Project ID" => lambda {|it| it['project'] ? it['project']['id'] : '' },
|
443
443
|
"Project Name" => lambda {|it| it['project'] ? it['project']['name'] : '' },
|
444
444
|
"Project Tags" => lambda {|it| it['project'] ? format_metadata(it['project']['tags']) : '' },
|
@@ -454,7 +454,8 @@ EOT
|
|
454
454
|
description_cols.delete("Project Name")
|
455
455
|
description_cols.delete("Project Tags")
|
456
456
|
end
|
457
|
-
|
457
|
+
tags = (invoice['metadata'] || invoice['tags'])
|
458
|
+
if tags.nil? || tags.empty?
|
458
459
|
description_cols.delete("Tags")
|
459
460
|
end
|
460
461
|
if !['ComputeServer','Instance','Container'].include?(invoice['refType'])
|
@@ -596,14 +596,12 @@ module Morpheus::Cli::ProvisioningHelper
|
|
596
596
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'environment', 'fieldLabel' => 'Environment', 'type' => 'select', 'required' => false, 'selectOptions' => get_available_environments()}], options[:options])
|
597
597
|
payload['instance']['instanceContext'] = v_prompt['environment'] if !v_prompt['environment'].empty?
|
598
598
|
|
599
|
-
# Labels (
|
600
|
-
# and tags (metadata tags) is called metadata.
|
601
|
-
# todo: switch this from 'tags' to labels' when the api changes
|
599
|
+
# Labels (used to be called tags)
|
602
600
|
if options[:labels]
|
603
|
-
payload['instance']['
|
601
|
+
payload['instance']['labels'] = options[:labels].is_a?(Array) ? options[:labels] : options[:labels].to_s.split(',').collect {|it| it.to_s.strip }.compact.uniq
|
604
602
|
else
|
605
603
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'labels', 'fieldLabel' => 'Labels', 'type' => 'text', 'required' => false}], options[:options])
|
606
|
-
payload['instance']['
|
604
|
+
payload['instance']['labels'] = v_prompt['labels'].split(',').collect {|it| it.to_s.strip }.compact.uniq if !v_prompt['labels'].empty?
|
607
605
|
end
|
608
606
|
|
609
607
|
# Version and Layout
|
@@ -1693,6 +1691,15 @@ module Morpheus::Cli::ProvisioningHelper
|
|
1693
1691
|
row = {}
|
1694
1692
|
row['name'] = metadata_pair[0].to_s.strip
|
1695
1693
|
row['value'] = metadata_pair[1].to_s.strip
|
1694
|
+
# hacky way to set masked flag to true of false to (masked) in the value itself
|
1695
|
+
if(row['value'].include?("(masked)"))
|
1696
|
+
row['value'] = row['value'].gsub("(masked)", "").strip
|
1697
|
+
row['masked'] = true
|
1698
|
+
end
|
1699
|
+
if(row['value'].include?("(unmasked)"))
|
1700
|
+
row['value'] = row['value'].gsub("(unmasked)", "").strip
|
1701
|
+
row['masked'] = false
|
1702
|
+
end
|
1696
1703
|
row
|
1697
1704
|
end
|
1698
1705
|
metadata = metadata_list
|
data/lib/morpheus/cli/version.rb
CHANGED
@@ -299,7 +299,7 @@ EOT
|
|
299
299
|
opts.on('--tags LIST', String, "Tags in the format 'name:value, name:value'. This will add and remove tags.") do |val|
|
300
300
|
options[:tags] = val
|
301
301
|
end
|
302
|
-
opts.on('--add-tags TAGS', String, "Add Tags in the format 'name:value, name:value'. This will only add/update
|
302
|
+
opts.on('--add-tags TAGS', String, "Add Tags in the format 'name:value, name:value'. This will only add/update tags.") do |val|
|
303
303
|
options[:add_tags] = val
|
304
304
|
end
|
305
305
|
opts.on('--remove-tags TAGS', String, "Remove Tags in the format 'name, name:value'. This removes tags, the :value component is optional and must match if passed.") do |val|
|
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: 5.2.
|
4
|
+
version: 5.2.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: 2020-11-
|
14
|
+
date: 2020-11-18 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|