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