morpheus-cli 8.1.2 → 9.0.0
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/Dockerfile +2 -2
- data/lib/morpheus/api/api_client.rb +8 -0
- data/lib/morpheus/api/groups_interface.rb +2 -1
- data/lib/morpheus/api/servers_interface.rb +0 -1
- data/lib/morpheus/api/support_bundles_interface.rb +46 -0
- data/lib/morpheus/api/systems_interface.rb +32 -0
- data/lib/morpheus/api/tokens_interface.rb +39 -0
- data/lib/morpheus/cli/cli_command.rb +6 -1
- data/lib/morpheus/cli/commands/clients_command.rb +60 -74
- data/lib/morpheus/cli/commands/clouds.rb +30 -2
- data/lib/morpheus/cli/commands/clusters.rb +5 -0
- data/lib/morpheus/cli/commands/execution_request_command.rb +6 -2
- data/lib/morpheus/cli/commands/groups.rb +46 -23
- data/lib/morpheus/cli/commands/hosts.rb +21 -14
- data/lib/morpheus/cli/commands/instances.rb +32 -6
- data/lib/morpheus/cli/commands/storage_volumes.rb +1 -1
- data/lib/morpheus/cli/commands/support_bundles_command.rb +606 -0
- data/lib/morpheus/cli/commands/systems.rb +606 -2
- data/lib/morpheus/cli/commands/tokens_command.rb +391 -0
- data/lib/morpheus/cli/commands/workflows.rb +16 -3
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +30 -14
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +21 -7
- data/lib/morpheus/cli/version.rb +1 -1
- data/test/api/systems_interface_test.rb +26 -0
- data/test/cli/systems_test.rb +206 -0
- metadata +10 -2
|
@@ -2,6 +2,7 @@ require 'morpheus/cli/cli_command'
|
|
|
2
2
|
|
|
3
3
|
class Morpheus::Cli::Groups
|
|
4
4
|
include Morpheus::Cli::CliCommand
|
|
5
|
+
include Morpheus::Cli::AccountsHelper
|
|
5
6
|
include Morpheus::Cli::InfrastructureHelper
|
|
6
7
|
include Morpheus::Cli::OptionSourceHelper
|
|
7
8
|
|
|
@@ -19,6 +20,8 @@ class Morpheus::Cli::Groups
|
|
|
19
20
|
@groups_interface = @api_client.groups
|
|
20
21
|
@clouds_interface = @api_client.clouds
|
|
21
22
|
@options_interface = @api_client.options
|
|
23
|
+
@accounts_interface = @api_client.accounts
|
|
24
|
+
@account_users_interface = @api_client.account_users
|
|
22
25
|
@active_group_id = Morpheus::Cli::Groups.active_groups[@appliance_name]
|
|
23
26
|
end
|
|
24
27
|
|
|
@@ -37,12 +40,31 @@ class Morpheus::Cli::Groups
|
|
|
37
40
|
opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
|
|
38
41
|
add_query_parameter(params, 'allLabels', parse_labels(val))
|
|
39
42
|
end
|
|
43
|
+
opts.on('--include-tenants','--include-tenants', "Include sub tenant groups") do
|
|
44
|
+
options[:include_tenants] = true
|
|
45
|
+
params['includeTenants'] = true
|
|
46
|
+
end
|
|
47
|
+
opts.on('--tenant TENANT', String, "Tenant Name or ID" ) do |val|
|
|
48
|
+
options[:tenant] = val
|
|
49
|
+
end
|
|
40
50
|
build_standard_list_options(opts, options)
|
|
41
51
|
opts.footer = "List groups."
|
|
42
52
|
end
|
|
43
53
|
optparse.parse!(args)
|
|
44
54
|
verify_args!(args:args, optparse:optparse, count:0)
|
|
45
55
|
connect(options)
|
|
56
|
+
if options[:tenant]
|
|
57
|
+
if options[:tenant].to_s !~ /\A\d{1,}\Z/
|
|
58
|
+
account = find_account_by_name_or_id(options[:tenant])
|
|
59
|
+
if account.nil?
|
|
60
|
+
return 1, "Tenant not found by name: #{options[:tenant]}"
|
|
61
|
+
else
|
|
62
|
+
params['tenantId'] = account['id']
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
params['tenantId'] = options[:tenant]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
46
68
|
params.merge!(parse_list_options(options))
|
|
47
69
|
@groups_interface.setopts(options)
|
|
48
70
|
if options[:dry_run]
|
|
@@ -71,6 +93,9 @@ class Morpheus::Cli::Groups
|
|
|
71
93
|
options = {}
|
|
72
94
|
optparse = Morpheus::Cli::OptionParser.new do|opts|
|
|
73
95
|
opts.banner = subcommand_usage("[name]")
|
|
96
|
+
opts.on('--include-tenants','--include-tenants', "Include sub tenant groups when finding group by name") do
|
|
97
|
+
options[:include_tenants] = true
|
|
98
|
+
end
|
|
74
99
|
build_standard_get_options(opts, options)
|
|
75
100
|
opts.footer = <<-EOT
|
|
76
101
|
Get details about a group.
|
|
@@ -86,25 +111,20 @@ EOT
|
|
|
86
111
|
end
|
|
87
112
|
end
|
|
88
113
|
|
|
89
|
-
def _get(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
json_response = {'group' => group}
|
|
104
|
-
|
|
105
|
-
render_result = render_with_format(json_response, options)
|
|
106
|
-
return 0 if render_result
|
|
107
|
-
|
|
114
|
+
def _get(id, options={})
|
|
115
|
+
params = {}
|
|
116
|
+
group = nil
|
|
117
|
+
if id.to_s !~ /\A\d{1,}\Z/
|
|
118
|
+
group = find_group_by_name_or_id(id, options[:include_tenants])
|
|
119
|
+
id = group['id']
|
|
120
|
+
end
|
|
121
|
+
@groups_interface.setopts(options)
|
|
122
|
+
if options[:dry_run]
|
|
123
|
+
print_dry_run @groups_interface.dry.get(id.to_i, params)
|
|
124
|
+
return
|
|
125
|
+
end
|
|
126
|
+
json_response = @groups_interface.get(id.to_i, params)
|
|
127
|
+
render_response(json_response, options, 'group') do
|
|
108
128
|
group = json_response['group']
|
|
109
129
|
group_stats = group['stats']
|
|
110
130
|
# serverCounts moved to zone.stats.serverCounts
|
|
@@ -124,6 +144,7 @@ EOT
|
|
|
124
144
|
"Location" => 'location',
|
|
125
145
|
"Labels" => lambda {|it| format_list(it['labels'], '') rescue '' },
|
|
126
146
|
"Clouds" => lambda {|it| it['zones'].collect {|z| z['name'] }.join(', ') },
|
|
147
|
+
"Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
|
127
148
|
#"Instances" => lambda {|it| it['stats']['instanceCounts']['all'] rescue '' },
|
|
128
149
|
# "Hosts" => lambda {|it| it['stats']['serverCounts']['host'] rescue it['serverCount'] },
|
|
129
150
|
# "VMs" => lambda {|it| it['stats']['serverCounts']['vm'] rescue '' },
|
|
@@ -665,13 +686,14 @@ EOT
|
|
|
665
686
|
|
|
666
687
|
protected
|
|
667
688
|
|
|
668
|
-
def print_groups_table(groups,
|
|
669
|
-
table_color =
|
|
689
|
+
def print_groups_table(groups, options={})
|
|
690
|
+
table_color = options[:color] || cyan
|
|
670
691
|
active_group = @active_group_id ? groups.find {|group| group['id'] == @active_group_id } : nil
|
|
671
692
|
rows = groups.collect do |group|
|
|
672
693
|
{
|
|
673
694
|
id: active_group ? (((@active_group_id && (@active_group_id == group['id'])) ? "=> " : " ") + group['id'].to_s) : group['id'],
|
|
674
695
|
name: group['name'],
|
|
696
|
+
tenant: group['account'] ? group['account']['name'] : '',
|
|
675
697
|
labels: group['labels'],
|
|
676
698
|
location: group['location'],
|
|
677
699
|
cloud_count: group['zones'] ? group['zones'].size : 0,
|
|
@@ -685,6 +707,7 @@ EOT
|
|
|
685
707
|
#{:active => {:display_name => ""}},
|
|
686
708
|
{:id => {:display_name => (active_group ? " ID" : "ID")}},
|
|
687
709
|
{:name => {:width => 64}},
|
|
710
|
+
options[:include_tenants] || options[:tenant] ? {:tenant => {:width => 32}} : nil,
|
|
688
711
|
{:location => {:width => 32}},
|
|
689
712
|
{:labels => {:display_method => lambda {|it| format_list(it[:labels], '', 3) rescue '' }}},
|
|
690
713
|
{:cloud_count => {:display_name => "CLOUDS"}},
|
|
@@ -692,8 +715,8 @@ EOT
|
|
|
692
715
|
{:host_count => {:display_name => "HOSTS"}},
|
|
693
716
|
{:vm_count => {:display_name => "VMS"}},
|
|
694
717
|
{:baremetal_count => {:display_name => "BARE METAL"}},
|
|
695
|
-
|
|
696
|
-
print as_pretty_table(rows, columns,
|
|
718
|
+
].compact
|
|
719
|
+
print as_pretty_table(rows, columns, options)
|
|
697
720
|
end
|
|
698
721
|
|
|
699
722
|
def add_group_option_types()
|
|
@@ -113,8 +113,12 @@ class Morpheus::Cli::Hosts
|
|
|
113
113
|
opts.on( '--created-by USER', "Created By User Username or ID" ) do |val|
|
|
114
114
|
options[:created_by] = val
|
|
115
115
|
end
|
|
116
|
+
opts.on('--include-tenants','--include-tenants', "Include sub tenant servers. The default is true for this endpoint.") do
|
|
117
|
+
options[:include_tenants] = true
|
|
118
|
+
params['includeTenants'] = true
|
|
119
|
+
end
|
|
116
120
|
opts.on( '--tenant TENANT', "Tenant Name or ID" ) do |val|
|
|
117
|
-
options[:
|
|
121
|
+
options[:tenant] = val
|
|
118
122
|
end
|
|
119
123
|
opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val|
|
|
120
124
|
add_query_parameter(params, 'labels', parse_labels(val))
|
|
@@ -154,8 +158,8 @@ class Morpheus::Cli::Hosts
|
|
|
154
158
|
|
|
155
159
|
params.merge!(parse_list_options(options))
|
|
156
160
|
account = nil
|
|
157
|
-
if options[:
|
|
158
|
-
account = find_account_by_name_or_id(options[:
|
|
161
|
+
if options[:tenant]
|
|
162
|
+
account = find_account_by_name_or_id(options[:tenant])
|
|
159
163
|
if account.nil?
|
|
160
164
|
return 1
|
|
161
165
|
else
|
|
@@ -315,9 +319,9 @@ class Morpheus::Cli::Hosts
|
|
|
315
319
|
"External Name" => :external_name,
|
|
316
320
|
"Hostname" => :hostname,
|
|
317
321
|
"Type" => :type,
|
|
322
|
+
"Cloud" => :cloud,
|
|
318
323
|
"Owner" => :owner,
|
|
319
324
|
"Tenant" => :tenant,
|
|
320
|
-
"Cloud" => :cloud,
|
|
321
325
|
"Plan" => :plan,
|
|
322
326
|
"IP" => :ip,
|
|
323
327
|
"Private IP" => :internal_ip,
|
|
@@ -335,8 +339,6 @@ class Morpheus::Cli::Hosts
|
|
|
335
339
|
columns.delete("Hostname")
|
|
336
340
|
columns.delete("Plan")
|
|
337
341
|
columns.delete("Private IP")
|
|
338
|
-
columns.delete("Owner")
|
|
339
|
-
columns.delete("Tenant")
|
|
340
342
|
columns.delete("Power")
|
|
341
343
|
columns.delete("Created")
|
|
342
344
|
columns.delete("Updated")
|
|
@@ -369,7 +371,7 @@ class Morpheus::Cli::Hosts
|
|
|
369
371
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
370
372
|
opts.banner = subcommand_usage("[options]")
|
|
371
373
|
opts.on( '--tenant TENANT', "Tenant Name or ID" ) do |val|
|
|
372
|
-
options[:
|
|
374
|
+
options[:tenant] = val
|
|
373
375
|
end
|
|
374
376
|
opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
|
|
375
377
|
options[:group] = val
|
|
@@ -434,8 +436,8 @@ class Morpheus::Cli::Hosts
|
|
|
434
436
|
begin
|
|
435
437
|
params.merge!(parse_list_options(options))
|
|
436
438
|
account = nil
|
|
437
|
-
if options[:
|
|
438
|
-
account = find_account_by_name_or_id(options[:
|
|
439
|
+
if options[:tenant]
|
|
440
|
+
account = find_account_by_name_or_id(options[:tenant])
|
|
439
441
|
if account.nil?
|
|
440
442
|
return 1
|
|
441
443
|
else
|
|
@@ -512,6 +514,9 @@ class Morpheus::Cli::Hosts
|
|
|
512
514
|
opts.on('--refresh-until STATUS', String, "Refresh until a specified status is reached.") do |val|
|
|
513
515
|
options[:refresh_until_status] = val.to_s.downcase
|
|
514
516
|
end
|
|
517
|
+
opts.on('--include-tenants','--include-tenants', "Include sub tenant servers when finding server by name") do
|
|
518
|
+
options[:include_tenants] = true
|
|
519
|
+
end
|
|
515
520
|
build_standard_get_options(opts, options)
|
|
516
521
|
end
|
|
517
522
|
optparse.parse!(args)
|
|
@@ -540,7 +545,7 @@ class Morpheus::Cli::Hosts
|
|
|
540
545
|
if arg.to_s =~ /\A\d{1,}\Z/
|
|
541
546
|
json_response = @servers_interface.get(arg.to_i)
|
|
542
547
|
else
|
|
543
|
-
server = find_host_by_name_or_id(arg)
|
|
548
|
+
server = find_host_by_name_or_id(arg, options[:include_tenants])
|
|
544
549
|
json_response = @servers_interface.get(server['id'])
|
|
545
550
|
# json_response = {"server" => server} need stats
|
|
546
551
|
end
|
|
@@ -2745,8 +2750,10 @@ EOT
|
|
|
2745
2750
|
end
|
|
2746
2751
|
end
|
|
2747
2752
|
|
|
2748
|
-
def find_host_by_name(name)
|
|
2749
|
-
|
|
2753
|
+
def find_host_by_name(name, include_tenants=false)
|
|
2754
|
+
params = {name: name.to_s}
|
|
2755
|
+
params['includeTenants'] = true if include_tenants
|
|
2756
|
+
results = @servers_interface.list(params)
|
|
2750
2757
|
if results['servers'].empty?
|
|
2751
2758
|
print_red_alert "Server not found by name #{name}"
|
|
2752
2759
|
exit 1
|
|
@@ -2759,11 +2766,11 @@ EOT
|
|
|
2759
2766
|
return results['servers'][0]
|
|
2760
2767
|
end
|
|
2761
2768
|
|
|
2762
|
-
def find_host_by_name_or_id(val)
|
|
2769
|
+
def find_host_by_name_or_id(val, include_tenants=false)
|
|
2763
2770
|
if val.to_s =~ /\A\d{1,}\Z/
|
|
2764
2771
|
return find_host_by_id(val)
|
|
2765
2772
|
else
|
|
2766
|
-
return find_host_by_name(val)
|
|
2773
|
+
return find_host_by_name(val, include_tenants)
|
|
2767
2774
|
end
|
|
2768
2775
|
end
|
|
2769
2776
|
|
|
@@ -150,6 +150,13 @@ class Morpheus::Cli::Instances
|
|
|
150
150
|
options[:details] = true
|
|
151
151
|
params['details'] = true # get more data from server this way
|
|
152
152
|
end
|
|
153
|
+
opts.on('--include-tenants','--include-tenants', "Include sub tenant instances") do
|
|
154
|
+
options[:include_tenants] = true
|
|
155
|
+
params['includeTenants'] = true
|
|
156
|
+
end
|
|
157
|
+
opts.on('--tenant TENANT', String, "Tenant Name or ID" ) do |val|
|
|
158
|
+
options[:tenant] = val
|
|
159
|
+
end
|
|
153
160
|
build_standard_list_options(opts, options)
|
|
154
161
|
opts.footer = "List instances."
|
|
155
162
|
end
|
|
@@ -204,6 +211,19 @@ class Morpheus::Cli::Instances
|
|
|
204
211
|
end
|
|
205
212
|
end
|
|
206
213
|
|
|
214
|
+
if options[:tenant]
|
|
215
|
+
if options[:tenant].to_s !~ /\A\d{1,}\Z/
|
|
216
|
+
account = find_account_by_name_or_id(options[:tenant])
|
|
217
|
+
if account.nil?
|
|
218
|
+
return 1, "Tenant not found by name: #{options[:tenant]}"
|
|
219
|
+
else
|
|
220
|
+
params['tenantId'] = account['id']
|
|
221
|
+
end
|
|
222
|
+
else
|
|
223
|
+
params['tenantId'] = options[:tenant]
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
207
227
|
@instances_interface.setopts(options)
|
|
208
228
|
if options[:dry_run]
|
|
209
229
|
print_dry_run @instances_interface.dry.list(params)
|
|
@@ -241,6 +261,7 @@ class Morpheus::Cli::Instances
|
|
|
241
261
|
if options[:owner]
|
|
242
262
|
subtitles << "Created By: #{options[:owner]}"
|
|
243
263
|
end
|
|
264
|
+
|
|
244
265
|
subtitles += parse_list_subtitles(options)
|
|
245
266
|
print_h1 title, subtitles, options
|
|
246
267
|
if instances.empty?
|
|
@@ -279,7 +300,7 @@ class Morpheus::Cli::Instances
|
|
|
279
300
|
connection: format_instance_connection_string(instance),
|
|
280
301
|
environment: instance['instanceContext'],
|
|
281
302
|
user: (instance['owner'] ? (instance['owner']['username'] || instance['owner']['id']) : (instance['createdBy'].is_a?(Hash) ? instance['createdBy']['username'] : instance['createdBy'])),
|
|
282
|
-
tenant: (instance['
|
|
303
|
+
tenant: (instance['tenant'] ? instance['tenant']['name'] : ''),
|
|
283
304
|
nodes: instance['containers'].count,
|
|
284
305
|
status: format_instance_status(instance, cyan),
|
|
285
306
|
type: instance['instanceType']['name'],
|
|
@@ -294,18 +315,20 @@ class Morpheus::Cli::Instances
|
|
|
294
315
|
}
|
|
295
316
|
row
|
|
296
317
|
}
|
|
297
|
-
columns = [:id, {:name => {:max_width => 50}}, :
|
|
318
|
+
columns = [:id, {:name => {:max_width => 50}}, :tenant, :group, :cloud,
|
|
298
319
|
:type, :version, :environment, :plan,
|
|
299
320
|
{:created => {:display_name => "CREATED"}},
|
|
300
|
-
# {:tenant => {:display_name => "TENANT"}},
|
|
301
321
|
{:user => {:display_name => "OWNER", :max_width => 20}},
|
|
302
|
-
:nodes, {:connection => {:max_width => 30}}, :status, :cpu, :memory, :storage]
|
|
322
|
+
:nodes, {:connection => {:max_width => 30}}, :status, :cpu, :memory, :storage].compact
|
|
303
323
|
# custom pretty table columns ... this is handled in as_pretty_table now(),
|
|
304
324
|
# todo: remove all these.. and try to always pass rows as the json data itself..
|
|
305
325
|
if options[:details] != true
|
|
306
326
|
columns.delete(:labels)
|
|
307
327
|
columns.delete(:plan)
|
|
308
328
|
end
|
|
329
|
+
if !options[:include_tenants] && !options[:tenant]
|
|
330
|
+
columns.delete(:tenant)
|
|
331
|
+
end
|
|
309
332
|
print cyan
|
|
310
333
|
print as_pretty_table(rows, columns, options)
|
|
311
334
|
print reset
|
|
@@ -1337,6 +1360,9 @@ class Morpheus::Cli::Instances
|
|
|
1337
1360
|
opts.on('--refresh-until STATUS', String, "Refresh until a specified status is reached.") do |val|
|
|
1338
1361
|
options[:refresh_until_status] = val.to_s.downcase
|
|
1339
1362
|
end
|
|
1363
|
+
opts.on('--include-tenants','--include-tenants', "Include sub tenant instances when finding instance by name") do
|
|
1364
|
+
options[:include_tenants] = true
|
|
1365
|
+
end
|
|
1340
1366
|
# opts.on( nil, '--threshold', "Alias for --scaling" ) do
|
|
1341
1367
|
# options[:include_scaling] = true
|
|
1342
1368
|
# end
|
|
@@ -1369,7 +1395,7 @@ class Morpheus::Cli::Instances
|
|
|
1369
1395
|
end
|
|
1370
1396
|
instance = nil
|
|
1371
1397
|
if id.to_s !~ /\A\d{1,}\Z/
|
|
1372
|
-
instance = find_instance_by_name_or_id(id)
|
|
1398
|
+
instance = find_instance_by_name_or_id(id, options[:include_tenants])
|
|
1373
1399
|
return 1, "Instance not found by name #{id}" if instance.nil?
|
|
1374
1400
|
id = instance['id']
|
|
1375
1401
|
end
|
|
@@ -1463,7 +1489,7 @@ class Morpheus::Cli::Instances
|
|
|
1463
1489
|
it['createdBy'] ? (it['createdBy']['username'] || it['createdBy']['id']) : ''
|
|
1464
1490
|
end
|
|
1465
1491
|
},
|
|
1466
|
-
|
|
1492
|
+
"Tenant" => lambda {|it| it['tenant'] ? it['tenant']['name'] : '' },
|
|
1467
1493
|
"Apps" => lambda {|it| anded_list(it['apps'] ? it['apps'].collect {|app| app['name'] } : [])},
|
|
1468
1494
|
"Date Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
|
1469
1495
|
# "Last Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
|
|
@@ -68,7 +68,7 @@ class Morpheus::Cli::StorageVolumes
|
|
|
68
68
|
def add_storage_volume_option_types()
|
|
69
69
|
[
|
|
70
70
|
{'fieldContext' => 'storageServer', 'fieldName' => 'id', 'fieldLabel' => 'Storage Server', 'type' => 'select', 'optionSource' => 'storageServers', 'optionParams' => {'createType' => 'block'}, 'required' => true},
|
|
71
|
-
{'fieldContext' => 'storageGroup', 'fieldName' => 'id', 'fieldLabel' => 'Storage Group', 'type' => 'select', 'optionSource' => 'storageGroups', 'required' =>
|
|
71
|
+
{'fieldContext' => 'storageGroup', 'fieldName' => 'id', 'fieldLabel' => 'Storage Group', 'type' => 'select', 'optionSource' => 'storageGroups', 'required' => false},
|
|
72
72
|
{'shorthand' => '-t', 'fieldName' => 'type', 'fieldLabel' => 'Storage Volume Type', 'type' => 'select', 'optionSource' => 'storageVolumeTypes', 'required' => true},
|
|
73
73
|
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true},
|
|
74
74
|
]
|