morpheus-cli 5.0.2 → 5.2.4
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/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +4 -0
- data/lib/morpheus/api/instances_interface.rb +30 -2
- data/lib/morpheus/api/invoices_interface.rb +12 -3
- data/lib/morpheus/api/servers_interface.rb +7 -0
- data/lib/morpheus/api/service_catalog_interface.rb +89 -0
- data/lib/morpheus/cli.rb +2 -1
- data/lib/morpheus/cli/apps.rb +3 -23
- data/lib/morpheus/cli/budgets_command.rb +389 -319
- data/lib/morpheus/cli/{catalog_command.rb → catalog_item_types_command.rb} +182 -67
- data/lib/morpheus/cli/cli_command.rb +25 -1
- data/lib/morpheus/cli/commands/standard/curl_command.rb +25 -10
- data/lib/morpheus/cli/commands/standard/history_command.rb +6 -2
- data/lib/morpheus/cli/containers_command.rb +0 -24
- data/lib/morpheus/cli/cypher_command.rb +6 -2
- data/lib/morpheus/cli/dashboard_command.rb +260 -20
- data/lib/morpheus/cli/health_command.rb +57 -0
- data/lib/morpheus/cli/hosts.rb +128 -11
- data/lib/morpheus/cli/instances.rb +270 -108
- data/lib/morpheus/cli/invoices_command.rb +67 -4
- data/lib/morpheus/cli/jobs_command.rb +94 -92
- data/lib/morpheus/cli/library_option_lists_command.rb +1 -1
- data/lib/morpheus/cli/library_option_types_command.rb +10 -5
- data/lib/morpheus/cli/mixins/accounts_helper.rb +5 -1
- data/lib/morpheus/cli/mixins/print_helper.rb +13 -6
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +88 -5
- data/lib/morpheus/cli/option_types.rb +10 -10
- data/lib/morpheus/cli/projects_command.rb +1 -1
- data/lib/morpheus/cli/roles.rb +193 -155
- data/lib/morpheus/cli/service_catalog_command.rb +1474 -0
- data/lib/morpheus/cli/tasks.rb +9 -11
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/virtual_images.rb +162 -68
- data/lib/morpheus/formatters.rb +55 -20
- metadata +5 -4
- data/lib/morpheus/cli/mixins/catalog_helper.rb +0 -66
@@ -30,6 +30,7 @@ class Morpheus::Cli::HealthCommand
|
|
30
30
|
opts.on('-a', '--all', "Display all details: CPU, Memory, Database, etc." ) do
|
31
31
|
options[:details] = true
|
32
32
|
options[:show_cpu] = true
|
33
|
+
options[:show_threads] = true
|
33
34
|
options[:show_memory] = true
|
34
35
|
options[:show_database] = true
|
35
36
|
options[:show_elastic] = true
|
@@ -47,6 +48,9 @@ class Morpheus::Cli::HealthCommand
|
|
47
48
|
opts.on('--cpu', "Display CPU details" ) do
|
48
49
|
options[:show_cpu] = true
|
49
50
|
end
|
51
|
+
opts.on('--threads', "Display Thread details" ) do
|
52
|
+
options[:show_threads] = true
|
53
|
+
end
|
50
54
|
opts.on('--memory', "Display Memory details" ) do
|
51
55
|
options[:show_memory] = true
|
52
56
|
end
|
@@ -184,6 +188,59 @@ class Morpheus::Cli::HealthCommand
|
|
184
188
|
end
|
185
189
|
end
|
186
190
|
|
191
|
+
# Threads ()
|
192
|
+
if options[:show_threads]
|
193
|
+
print_h2 "Threads", options
|
194
|
+
if health['threads'].nil?
|
195
|
+
print yellow,"No thread information returned.",reset,"\n\n"
|
196
|
+
else
|
197
|
+
print cyan
|
198
|
+
|
199
|
+
thread_summary_columns = {
|
200
|
+
"Thread Count" => lambda {|it| it['totalThreads'].size rescue '' },
|
201
|
+
"Busy Threads" => lambda {|it| it['busyThreads'].size rescue '' },
|
202
|
+
"Running Threads" => lambda {|it| it['runningThreads'].size rescue '' },
|
203
|
+
"Blocked Threads" => lambda {|it| it['blockedThreads'].size rescue '' },
|
204
|
+
}
|
205
|
+
print_description_list(thread_summary_columns, health['threads'], options)
|
206
|
+
|
207
|
+
|
208
|
+
thread_columns = [
|
209
|
+
{"Name".upcase => lambda {|it| it['name']} },
|
210
|
+
{"Status".upcase => lambda {|it|
|
211
|
+
# hrmm
|
212
|
+
status_string = (it['status'] || it['state']).to_s.downcase
|
213
|
+
status_color = cyan
|
214
|
+
# if status_string.include?('waiting')
|
215
|
+
# status_color = yellow
|
216
|
+
# end
|
217
|
+
"#{status_color}#{status_string.upcase}#{cyan}"
|
218
|
+
} },
|
219
|
+
# {"CPU Time" => lambda {|it| it['cpuTime'].to_s } },
|
220
|
+
# {"CPU Time" => lambda {|it| format_human_duration(it['cpuTime'].to_f / 1000) rescue '' } },
|
221
|
+
{"CPU Percent" => lambda {|it| it['cpuPercent'].to_i.to_s + '%' } }
|
222
|
+
]
|
223
|
+
|
224
|
+
if health['threads']['busyThreads'] && health['threads']['busyThreads'].size > 0
|
225
|
+
print_h2 "Busy Threads"
|
226
|
+
print cyan
|
227
|
+
print as_pretty_table(health['threads']['busyThreads'], thread_columns, options)
|
228
|
+
end
|
229
|
+
|
230
|
+
if health['threads']['runningThreads'] && health['threads']['runningThreads'].size > 0
|
231
|
+
print_h2 "Running Threads"
|
232
|
+
print cyan
|
233
|
+
print as_pretty_table(health['threads']['runningThreads'], thread_columns, options)
|
234
|
+
end
|
235
|
+
|
236
|
+
if health['threads']['blockedThreads'] && health['threads']['blockedThreads'].size > 0
|
237
|
+
print_h2 "Blocked Threads"
|
238
|
+
print cyan
|
239
|
+
print as_pretty_table(health['threads']['blockedThreads'], thread_columns, options)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
187
244
|
# Memory
|
188
245
|
if options[:show_memory]
|
189
246
|
if health['memory'].nil?
|
data/lib/morpheus/cli/hosts.rb
CHANGED
@@ -17,7 +17,7 @@ class Morpheus::Cli::Hosts
|
|
17
17
|
set_command_name :hosts
|
18
18
|
set_command_description "View and manage hosts (servers)."
|
19
19
|
register_subcommands :list, :count, :get, :view, :stats, :add, :update, :remove, :logs, :start, :stop, :resize,
|
20
|
-
:run_workflow, :make_managed, :upgrade_agent, :snapshots,
|
20
|
+
:run_workflow, :make_managed, :upgrade_agent, :snapshots, :software,
|
21
21
|
{:'types' => :list_types},
|
22
22
|
{:exec => :execution_request},
|
23
23
|
:wiki, :update_wiki
|
@@ -54,9 +54,6 @@ class Morpheus::Cli::Hosts
|
|
54
54
|
params = {}
|
55
55
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
56
56
|
opts.banner = subcommand_usage()
|
57
|
-
opts.on( '-a', '--account ACCOUNT', "Account Name or ID" ) do |val|
|
58
|
-
options[:account] = val
|
59
|
-
end
|
60
57
|
opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
|
61
58
|
options[:group] = val
|
62
59
|
end
|
@@ -82,6 +79,17 @@ class Morpheus::Cli::Hosts
|
|
82
79
|
# params[:clusterId] = val
|
83
80
|
options[:cluster] = val
|
84
81
|
end
|
82
|
+
opts.on( '--plan NAME', String, "Filter by Plan name(s)" ) do |val|
|
83
|
+
# commas used in names a lot so use --plan one --plan two
|
84
|
+
params['plan'] ||= []
|
85
|
+
params['plan'] << val
|
86
|
+
end
|
87
|
+
opts.on( '--plan-id ID', String, "Filter by Plan id(s)" ) do |val|
|
88
|
+
params['planId'] = parse_id_list(val)
|
89
|
+
end
|
90
|
+
opts.on( '--plan-code CODE', String, "Filter by Plan code(s)" ) do |val|
|
91
|
+
params['planCode'] = parse_id_list(val)
|
92
|
+
end
|
85
93
|
opts.on( '', '--vm', "Show only virtual machines" ) do |val|
|
86
94
|
params[:vm] = true
|
87
95
|
end
|
@@ -106,8 +114,14 @@ class Morpheus::Cli::Hosts
|
|
106
114
|
opts.on( '--created-by USER', "Created By User Username or ID" ) do |val|
|
107
115
|
options[:created_by] = val
|
108
116
|
end
|
109
|
-
opts.on('--
|
110
|
-
options[:
|
117
|
+
opts.on( '--tenant TENANT', "Tenant Name or ID" ) do |val|
|
118
|
+
options[:account] = val
|
119
|
+
end
|
120
|
+
opts.on('--labels label',String, "Filter by labels (keywords).") do |val|
|
121
|
+
val.split(",").each do |k|
|
122
|
+
options[:labels] ||= []
|
123
|
+
options[:labels] << k.strip
|
124
|
+
end
|
111
125
|
end
|
112
126
|
opts.on('--tags Name=Value',String, "Filter by tags.") do |val|
|
113
127
|
val.split(",").each do |value_pair|
|
@@ -123,6 +137,12 @@ class Morpheus::Cli::Hosts
|
|
123
137
|
opts.on('--non-tag-compliant', "Displays only servers with tag compliance warnings." ) do
|
124
138
|
params[:tagCompliant] = false
|
125
139
|
end
|
140
|
+
opts.on('--stats', "Display values for memory and storage usage used / max values." ) do
|
141
|
+
options[:stats] = true
|
142
|
+
end
|
143
|
+
opts.on('-a', '--details', "Display all details: hostname, private ip, plan, stats, etc." ) do
|
144
|
+
options[:details] = true
|
145
|
+
end
|
126
146
|
build_standard_list_options(opts, options)
|
127
147
|
opts.footer = "List hosts."
|
128
148
|
end
|
@@ -172,6 +192,7 @@ class Morpheus::Cli::Hosts
|
|
172
192
|
params['clusterId'] = cluster['id']
|
173
193
|
end
|
174
194
|
end
|
195
|
+
params['labels'] = options[:labels] if options[:labels]
|
175
196
|
if options[:tags] && !options[:tags].empty?
|
176
197
|
options[:tags].each do |k,v|
|
177
198
|
params['tags.' + k] = v
|
@@ -208,6 +229,9 @@ class Morpheus::Cli::Hosts
|
|
208
229
|
multi_tenant = json_response['multiTenant'] == true
|
209
230
|
title = "Morpheus Hosts"
|
210
231
|
subtitles = []
|
232
|
+
if account
|
233
|
+
subtitles << "Tenant: #{account['name']}".strip
|
234
|
+
end
|
211
235
|
if group
|
212
236
|
subtitles << "Group: #{group['name']}".strip
|
213
237
|
end
|
@@ -248,7 +272,7 @@ class Morpheus::Cli::Hosts
|
|
248
272
|
cpu_usage_str = !stats ? "" : generate_usage_bar((stats['usedCpu'] || stats['cpuUsage']).to_f, 100, {max_bars: 10})
|
249
273
|
memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
|
250
274
|
storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
|
251
|
-
if options[:details]
|
275
|
+
if options[:details] || options[:stats]
|
252
276
|
if stats['maxMemory'] && stats['maxMemory'].to_i != 0
|
253
277
|
memory_usage_str = memory_usage_str + cyan + format_bytes_short(stats['usedMemory']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxMemory']).strip
|
254
278
|
end
|
@@ -259,12 +283,14 @@ class Morpheus::Cli::Hosts
|
|
259
283
|
row = {
|
260
284
|
id: server['id'],
|
261
285
|
name: server['name'],
|
286
|
+
external_name: server['externalName'],
|
262
287
|
hostname: server['hostname'],
|
263
288
|
platform: server['serverOs'] ? server['serverOs']['name'].upcase : 'N/A',
|
264
289
|
type: server['computeServerType'] ? server['computeServerType']['name'] : 'unmanaged',
|
265
290
|
tenant: server['account'] ? server['account']['name'] : server['accountId'],
|
266
291
|
owner: server['owner'] ? server['owner']['username'] : server['owner'],
|
267
292
|
cloud: server['zone'] ? server['zone']['name'] : '',
|
293
|
+
plan: server['plan'] ? server['plan']['name'] : '',
|
268
294
|
ip: server['externalIp'],
|
269
295
|
internal_ip: server['internalIp'],
|
270
296
|
nodes: server['containers'] ? server['containers'].size : '',
|
@@ -283,11 +309,13 @@ class Morpheus::Cli::Hosts
|
|
283
309
|
columns = {
|
284
310
|
"ID" => :id,
|
285
311
|
"Name" => :name,
|
312
|
+
"External Name" => :external_name,
|
286
313
|
"Hostname" => :hostname,
|
287
314
|
"Type" => :type,
|
288
315
|
"Owner" => :owner,
|
289
316
|
"Tenant" => :tenant,
|
290
317
|
"Cloud" => :cloud,
|
318
|
+
"Plan" => :plan,
|
291
319
|
"IP" => :ip,
|
292
320
|
"Private IP" => :internal_ip,
|
293
321
|
"Nodes" => :nodes,
|
@@ -300,7 +328,9 @@ class Morpheus::Cli::Hosts
|
|
300
328
|
"Updated" => :updated,
|
301
329
|
}
|
302
330
|
if options[:details] != true
|
331
|
+
columns.delete("External Name")
|
303
332
|
columns.delete("Hostname")
|
333
|
+
columns.delete("Plan")
|
304
334
|
columns.delete("Private IP")
|
305
335
|
columns.delete("Owner")
|
306
336
|
columns.delete("Tenant")
|
@@ -308,6 +338,10 @@ class Morpheus::Cli::Hosts
|
|
308
338
|
columns.delete("Created")
|
309
339
|
columns.delete("Updated")
|
310
340
|
end
|
341
|
+
# hide External Name if there are none
|
342
|
+
if !servers.find {|it| it['externalName'] && it['externalName'] != it['name']}
|
343
|
+
columns.delete("External Name")
|
344
|
+
end
|
311
345
|
if !multi_tenant
|
312
346
|
columns.delete("Tenant")
|
313
347
|
end
|
@@ -334,7 +368,7 @@ class Morpheus::Cli::Hosts
|
|
334
368
|
options = {}
|
335
369
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
336
370
|
opts.banner = subcommand_usage("[options]")
|
337
|
-
opts.on( '
|
371
|
+
opts.on( '--tenant TENANT', "Tenant Name or ID" ) do |val|
|
338
372
|
options[:account] = val
|
339
373
|
end
|
340
374
|
opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
|
@@ -503,9 +537,10 @@ class Morpheus::Cli::Hosts
|
|
503
537
|
puts records_as_csv([json_response['server']], options)
|
504
538
|
return 0
|
505
539
|
end
|
506
|
-
server = json_response['server']
|
540
|
+
server = json_response['server'] || json_response['host'] || {}
|
507
541
|
#stats = server['stats'] || json_response['stats'] || {}
|
508
542
|
stats = json_response['stats'] || {}
|
543
|
+
tags = server['tags'] || server['metadata']
|
509
544
|
title = "Host Details"
|
510
545
|
print_h1 title, [], options
|
511
546
|
print cyan
|
@@ -514,6 +549,8 @@ class Morpheus::Cli::Hosts
|
|
514
549
|
"Name" => 'name',
|
515
550
|
"Hostname" => 'hostname',
|
516
551
|
"Description" => 'description',
|
552
|
+
"Labels" => lambda {|it| it['labels'] ? it['labels'].join(',') : '' },
|
553
|
+
"Tags" => lambda {|it| tags ? format_metadata(tags) : '' },
|
517
554
|
"Owner" => lambda {|it| it['owner'] ? it['owner']['username'] : '' },
|
518
555
|
"Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
519
556
|
#"Group" => lambda {|it| it['group'] ? it['group']['name'] : '' },
|
@@ -537,6 +574,8 @@ class Morpheus::Cli::Hosts
|
|
537
574
|
# server_columns.delete("Tenant") if multi_tenant != true
|
538
575
|
server_columns.delete("Cost") if server['hourlyCost'].to_f == 0
|
539
576
|
server_columns.delete("Price") if server['hourlyPrice'].to_f == 0 || server['hourlyPrice'] == server['hourlyCost']
|
577
|
+
server_columns.delete("Labels") if server['labels'].nil? || server['labels'].empty?
|
578
|
+
server_columns.delete("Tags") if tags.nil? || tags.empty?
|
540
579
|
|
541
580
|
print_description_list(server_columns, server)
|
542
581
|
|
@@ -963,6 +1002,22 @@ class Morpheus::Cli::Hosts
|
|
963
1002
|
opts.on('--power-schedule-type ID', String, "Power Schedule Type ID") do |val|
|
964
1003
|
params['powerScheduleType'] = val == "null" ? nil : val
|
965
1004
|
end
|
1005
|
+
opts.on('--labels [LIST]', String, "Labels (keywords) in the format 'foo, bar'") do |val|
|
1006
|
+
params['labels'] = val.to_s.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
|
1007
|
+
end
|
1008
|
+
opts.on('--tags LIST', String, "Tags in the format 'name:value, name:value'. This will add and remove tags.") do |val|
|
1009
|
+
options[:tags] = val
|
1010
|
+
end
|
1011
|
+
opts.on('--metadata LIST', String, "Alias for --tags.") do |val|
|
1012
|
+
options[:tags] = val
|
1013
|
+
end
|
1014
|
+
opts.add_hidden_option('--metadata')
|
1015
|
+
opts.on('--add-tags TAGS', String, "Add Tags in the format 'name:value, name:value'. This will only add/update tags.") do |val|
|
1016
|
+
options[:add_tags] = val
|
1017
|
+
end
|
1018
|
+
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|
|
1019
|
+
options[:remove_tags] = val
|
1020
|
+
end
|
966
1021
|
# opts.on('--created-by ID', String, "Created By User ID") do |val|
|
967
1022
|
# params['createdById'] = val
|
968
1023
|
# end
|
@@ -981,6 +1036,18 @@ class Morpheus::Cli::Hosts
|
|
981
1036
|
new_group = nil
|
982
1037
|
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
983
1038
|
params.deep_merge!(passed_options) unless passed_options.empty?
|
1039
|
+
# metadata tags
|
1040
|
+
if options[:tags]
|
1041
|
+
params['tags'] = parse_metadata(options[:tags])
|
1042
|
+
else
|
1043
|
+
# params['tags'] = prompt_metadata(options)
|
1044
|
+
end
|
1045
|
+
if options[:add_tags]
|
1046
|
+
params['addTags'] = parse_metadata(options[:add_tags])
|
1047
|
+
end
|
1048
|
+
if options[:remove_tags]
|
1049
|
+
params['removeTags'] = parse_metadata(options[:remove_tags])
|
1050
|
+
end
|
984
1051
|
payload = nil
|
985
1052
|
if options[:payload]
|
986
1053
|
payload = options[:payload]
|
@@ -1892,7 +1959,8 @@ class Morpheus::Cli::Hosts
|
|
1892
1959
|
snapshot_column_definitions = {
|
1893
1960
|
"ID" => lambda {|it| it['id'] },
|
1894
1961
|
"Name" => lambda {|it| it['name'] },
|
1895
|
-
"Description" => lambda {|it| it['
|
1962
|
+
"Description" => lambda {|it| it['description'] },
|
1963
|
+
# "Type" => lambda {|it| it['snapshotType'] },
|
1896
1964
|
"Date Created" => lambda {|it| format_local_dt(it['snapshotCreated']) },
|
1897
1965
|
"Status" => lambda {|it| format_snapshot_status(it) }
|
1898
1966
|
}
|
@@ -1909,6 +1977,55 @@ class Morpheus::Cli::Hosts
|
|
1909
1977
|
end
|
1910
1978
|
end
|
1911
1979
|
|
1980
|
+
def software(args)
|
1981
|
+
options = {}
|
1982
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1983
|
+
opts.banner = subcommand_usage("[host]")
|
1984
|
+
build_standard_list_options(opts, options)
|
1985
|
+
end
|
1986
|
+
optparse.parse!(args)
|
1987
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
1988
|
+
connect(options)
|
1989
|
+
begin
|
1990
|
+
server = find_host_by_name_or_id(args[0])
|
1991
|
+
return 1 if server.nil?
|
1992
|
+
params = {}
|
1993
|
+
params.merge!(parse_list_options(options))
|
1994
|
+
@servers_interface.setopts(options)
|
1995
|
+
if options[:dry_run]
|
1996
|
+
print_dry_run @servers_interface.dry.software(server['id'], params)
|
1997
|
+
return
|
1998
|
+
end
|
1999
|
+
json_response = @servers_interface.software(server['id'], params)
|
2000
|
+
software = json_response['software']
|
2001
|
+
render_response(json_response, options, 'software') do
|
2002
|
+
print_h1 "Software: #{server['name']}", [], options
|
2003
|
+
if software.empty?
|
2004
|
+
print cyan,"No software found",reset,"\n"
|
2005
|
+
else
|
2006
|
+
software_column_definitions = {
|
2007
|
+
# "ID" => lambda {|it| it['id'] },
|
2008
|
+
"Name" => lambda {|it| it['name'] },
|
2009
|
+
"Version" => lambda {|it| it['packageVersion'] },
|
2010
|
+
"Publisher" => lambda {|it| it['packagePublisher'] },
|
2011
|
+
# "Release" => lambda {|it| it['packageRelease'] },
|
2012
|
+
# "Type" => lambda {|it| it['packageType'] },
|
2013
|
+
# "Architecture" => lambda {|it| it['architecture'] },
|
2014
|
+
# "Install Date" => lambda {|it| format_local_dt(it['installDate']) },
|
2015
|
+
}
|
2016
|
+
print cyan
|
2017
|
+
print as_pretty_table(software, software_column_definitions.upcase_keys!, options)
|
2018
|
+
print_results_pagination({size: software.size, total: software.size})
|
2019
|
+
end
|
2020
|
+
print reset, "\n"
|
2021
|
+
end
|
2022
|
+
return 0
|
2023
|
+
rescue RestClient::Exception => e
|
2024
|
+
print_rest_exception(e, options)
|
2025
|
+
exit 1
|
2026
|
+
end
|
2027
|
+
end
|
2028
|
+
|
1912
2029
|
private
|
1913
2030
|
|
1914
2031
|
def find_host_by_id(id)
|
@@ -2061,7 +2178,7 @@ class Morpheus::Cli::Hosts
|
|
2061
2178
|
def make_managed_option_types(connected=true)
|
2062
2179
|
[
|
2063
2180
|
#{'fieldName' => 'account', 'fieldLabel' => 'Account', 'type' => 'select', 'optionSource' => 'accounts', 'required' => true},
|
2064
|
-
{'fieldName' => 'sshUsername', 'fieldLabel' => 'SSH Username', 'type' => 'text'
|
2181
|
+
{'fieldName' => 'sshUsername', 'fieldLabel' => 'SSH Username', 'type' => 'text'},
|
2065
2182
|
{'fieldName' => 'sshPassword', 'fieldLabel' => 'SSH Password', 'type' => 'password', 'required' => false},
|
2066
2183
|
{'fieldName' => 'serverOs', 'fieldLabel' => 'OS Type', 'type' => 'select', 'optionSource' => 'osTypes', 'required' => false},
|
2067
2184
|
]
|
@@ -19,7 +19,8 @@ class Morpheus::Cli::Instances
|
|
19
19
|
:history, {:'history-details' => :history_details}, {:'history-event' => :history_event_details},
|
20
20
|
:stats, :stop, :start, :restart, :actions, :action, :suspend, :eject, :stop_service, :start_service, :restart_service,
|
21
21
|
:backup, :backups, :resize, :clone, :envs, :setenv, :delenv,
|
22
|
-
:
|
22
|
+
:lock, :unlock, :clone_image,
|
23
|
+
:security_groups, :apply_security_groups, :run_workflow, :import_snapshot, :snapshot, :snapshots,
|
23
24
|
:console, :status_check, {:containers => :list_containers},
|
24
25
|
:scaling, {:'scaling-update' => :scaling_update},
|
25
26
|
:wiki, :update_wiki,
|
@@ -80,9 +81,6 @@ class Morpheus::Cli::Instances
|
|
80
81
|
options[:owner] = val
|
81
82
|
end
|
82
83
|
opts.add_hidden_option('--created-by')
|
83
|
-
opts.on('--details', "Display more details: memory and storage usage used / max values." ) do
|
84
|
-
options[:details] = true
|
85
|
-
end
|
86
84
|
opts.on('--status STATUS', "Filter by status i.e. provisioning,running,starting,stopping") do |val|
|
87
85
|
params['status'] = (params['status'] || []) + val.to_s.split(',').collect {|s| s.strip }.select {|s| s != "" }
|
88
86
|
end
|
@@ -92,6 +90,17 @@ class Morpheus::Cli::Instances
|
|
92
90
|
opts.on('--pending-removal-only', "Only instances pending removal.") do
|
93
91
|
options[:deleted] = true
|
94
92
|
end
|
93
|
+
opts.on( '--plan NAME', String, "Filter by Plan name(s)" ) do |val|
|
94
|
+
# commas used in names a lot so use --plan one --plan two
|
95
|
+
params['plan'] ||= []
|
96
|
+
params['plan'] << val
|
97
|
+
end
|
98
|
+
opts.on( '--plan-id ID', String, "Filter by Plan id(s)" ) do |val|
|
99
|
+
params['planId'] = parse_id_list(val)
|
100
|
+
end
|
101
|
+
opts.on( '--plan-code CODE', String, "Filter by Plan code(s)" ) do |val|
|
102
|
+
params['planCode'] = parse_id_list(val)
|
103
|
+
end
|
95
104
|
opts.on('--labels label',String, "Filter by labels (keywords).") do |val|
|
96
105
|
val.split(",").each do |k|
|
97
106
|
options[:labels] ||= []
|
@@ -106,6 +115,12 @@ class Morpheus::Cli::Instances
|
|
106
115
|
options[:tags][k] << (v || '')
|
107
116
|
end
|
108
117
|
end
|
118
|
+
opts.on('--stats', "Display values for memory and storage usage used / max values." ) do
|
119
|
+
options[:stats] = true
|
120
|
+
end
|
121
|
+
opts.on('-a', '--details', "Display all details: plan, stats, etc" ) do
|
122
|
+
options[:details] = true
|
123
|
+
end
|
109
124
|
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
110
125
|
opts.footer = "List instances."
|
111
126
|
end
|
@@ -216,7 +231,7 @@ class Morpheus::Cli::Instances
|
|
216
231
|
cpu_usage_str = !stats ? "" : generate_usage_bar((stats['usedCpu'] || stats['cpuUsage']).to_f, 100, {max_bars: 10})
|
217
232
|
memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
|
218
233
|
storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
|
219
|
-
if options[:details]
|
234
|
+
if options[:details] || options[:stats]
|
220
235
|
if stats['maxMemory'] && stats['maxMemory'].to_i != 0
|
221
236
|
memory_usage_str = memory_usage_str + cyan + format_bytes_short(stats['usedMemory']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxMemory']).strip
|
222
237
|
end
|
@@ -234,8 +249,9 @@ class Morpheus::Cli::Instances
|
|
234
249
|
nodes: instance['containers'].count,
|
235
250
|
status: format_instance_status(instance, cyan),
|
236
251
|
type: instance['instanceType']['name'],
|
237
|
-
group:
|
238
|
-
cloud:
|
252
|
+
group: instance['group'] ? instance['group']['name'] : nil,
|
253
|
+
cloud: instance['cloud'] ? instance['cloud']['name'] : nil,
|
254
|
+
plan: instance['plan'] ? instance['plan']['name'] : '',
|
239
255
|
version: instance['instanceVersion'] ? instance['instanceVersion'] : '',
|
240
256
|
created: format_local_dt(instance['dateCreated']),
|
241
257
|
cpu: cpu_usage_str + cyan,
|
@@ -249,12 +265,13 @@ class Morpheus::Cli::Instances
|
|
249
265
|
{:created => {:display_name => "CREATED"}},
|
250
266
|
# {:tenant => {:display_name => "TENANT"}},
|
251
267
|
{:user => {:display_name => "OWNER", :max_width => 20}},
|
268
|
+
:plan,
|
252
269
|
:nodes, {:connection => {:max_width => 30}}, :status, :cpu, :memory, :storage]
|
253
270
|
# custom pretty table columns ... this is handled in as_pretty_table now(),
|
254
271
|
# todo: remove all these.. and try to always pass rows as the json data itself..
|
255
|
-
|
256
|
-
|
257
|
-
|
272
|
+
if options[:details] != true
|
273
|
+
columns.delete(:plan)
|
274
|
+
end
|
258
275
|
print cyan
|
259
276
|
print as_pretty_table(rows, columns, options)
|
260
277
|
print reset
|
@@ -443,17 +460,58 @@ class Morpheus::Cli::Instances
|
|
443
460
|
options[:instance_name] = args[0]
|
444
461
|
end
|
445
462
|
|
446
|
-
# use active group by default
|
447
|
-
options[:group] ||= @active_group_id
|
448
|
-
options[:select_datastore] = true
|
449
|
-
options[:name_required] = true
|
450
463
|
begin
|
451
464
|
payload = nil
|
452
465
|
if options[:payload]
|
453
466
|
payload = options[:payload]
|
454
467
|
# support -O OPTION switch on top of --payload
|
455
468
|
payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
469
|
+
# obviously should support every option that prompt supports on top of -- payload as well
|
470
|
+
# group, cloud and type for now
|
471
|
+
# todo: also support :layout, service_plan, :resource_pool, etc.
|
472
|
+
group = nil
|
473
|
+
if options[:group]
|
474
|
+
group = find_group_by_name_or_id_for_provisioning(options[:group])
|
475
|
+
if group.nil?
|
476
|
+
return 1, "group not found by #{options[:group]}"
|
477
|
+
end
|
478
|
+
#payload["siteId"] = group["id"]
|
479
|
+
payload.deep_merge!({"instance" => {"site" => {"id" => group["id"]} } })
|
480
|
+
end
|
481
|
+
if options[:cloud]
|
482
|
+
group_id = group ? group["id"] : ((payload["instance"] && payload["instance"]["site"].is_a?(Hash)) ? payload["instance"]["site"]["id"] : nil)
|
483
|
+
cloud = find_cloud_by_name_or_id_for_provisioning(group_id, options[:cloud])
|
484
|
+
if cloud.nil?
|
485
|
+
return 1, "cloud not found by #{options[:cloud]}"
|
486
|
+
end
|
487
|
+
payload["zoneId"] = cloud["id"]
|
488
|
+
payload.deep_merge!({"instance" => {"cloud" => cloud["name"] } })
|
489
|
+
end
|
490
|
+
if options[:cloud]
|
491
|
+
group_id = group ? group["id"] : ((payload["instance"] && payload["instance"]["site"].is_a?(Hash)) ? payload["instance"]["site"]["id"] : nil)
|
492
|
+
cloud = find_cloud_by_name_or_id_for_provisioning(group_id, options[:cloud])
|
493
|
+
if cloud.nil?
|
494
|
+
return 1, "cloud not found by #{options[:cloud]}"
|
495
|
+
end
|
496
|
+
payload["zoneId"] = cloud["id"]
|
497
|
+
payload.deep_merge!({"instance" => {"cloud" => cloud["name"] } })
|
498
|
+
end
|
499
|
+
if options[:instance_type_code]
|
500
|
+
# should just use find_instance_type_by_name_or_id
|
501
|
+
# note that the api actually will match name name or code
|
502
|
+
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])
|
503
|
+
if instance_type.nil?
|
504
|
+
return 1, "instance type not found by #{options[:cloud]}"
|
505
|
+
end
|
506
|
+
payload.deep_merge!({"instance" => {"type" => instance_type["code"] } })
|
507
|
+
payload.deep_merge!({"instance" => {"instanceType" => {"code" => instance_type["code"]} } })
|
508
|
+
end
|
509
|
+
|
456
510
|
else
|
511
|
+
# use active group by default
|
512
|
+
options[:group] ||= @active_group_id
|
513
|
+
options[:select_datastore] = true
|
514
|
+
options[:name_required] = true
|
457
515
|
# prompt for all the instance configuration options
|
458
516
|
# this provisioning helper method handles all (most) of the parsing and prompting
|
459
517
|
# and it relies on the method to exit non-zero on error, like a bad CLOUD or TYPE value
|
@@ -546,16 +604,17 @@ class Morpheus::Cli::Instances
|
|
546
604
|
opts.on('--group GROUP', String, "Group Name or ID") do |val|
|
547
605
|
options[:group] = val
|
548
606
|
end
|
549
|
-
opts.on('--
|
550
|
-
|
607
|
+
opts.on('--labels [LIST]', String, "Labels (keywords) in the format 'foo, bar'") do |val|
|
608
|
+
params['labels'] = val.to_s.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
|
551
609
|
end
|
552
|
-
opts.on('--
|
553
|
-
options[:
|
610
|
+
opts.on('--tags LIST', String, "Tags in the format 'name:value, name:value'. This will add and remove tags.") do |val|
|
611
|
+
options[:tags] = val
|
554
612
|
end
|
555
|
-
opts.
|
556
|
-
|
557
|
-
|
558
|
-
|
613
|
+
opts.on('--add-tags TAGS', String, "Add Tags in the format 'name:value, name:value'. This will only add/update tags.") do |val|
|
614
|
+
options[:add_tags] = val
|
615
|
+
end
|
616
|
+
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|
|
617
|
+
options[:remove_tags] = val
|
559
618
|
end
|
560
619
|
opts.on('--power-schedule-type ID', String, "Power Schedule Type ID") do |val|
|
561
620
|
params['powerScheduleType'] = val == "null" ? nil : val
|
@@ -611,32 +670,17 @@ class Morpheus::Cli::Instances
|
|
611
670
|
payload['instance']['site'] = {'id' => group['id']}
|
612
671
|
end
|
613
672
|
# metadata tags
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
metadata = []
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
# merge IDs from current metadata
|
626
|
-
# todo: should allow quoted semicolons..
|
627
|
-
metadata_list = options[:metadata].split(",").select {|it| !it.to_s.empty? }
|
628
|
-
metadata_list = metadata_list.collect do |it|
|
629
|
-
metadata_pair = it.split(":")
|
630
|
-
if metadata_pair.size == 1 && it.include?("=")
|
631
|
-
metadata_pair = it.split("=")
|
632
|
-
end
|
633
|
-
row = {}
|
634
|
-
row['name'] = metadata_pair[0].to_s.strip
|
635
|
-
row['value'] = metadata_pair[1].to_s.strip
|
636
|
-
row
|
637
|
-
end
|
638
|
-
payload['instance']['metadata'] = metadata_list
|
639
|
-
end
|
673
|
+
if options[:tags]
|
674
|
+
# api version 4.2.5 and later supports tags, older versions expect metadata
|
675
|
+
# todo: use tags instead like everywhere else
|
676
|
+
# payload['instance']['tags'] = parse_metadata(options[:tags])
|
677
|
+
payload['instance']['metadata'] = parse_metadata(options[:tags])
|
678
|
+
end
|
679
|
+
if options[:add_tags]
|
680
|
+
payload['instance']['addTags'] = parse_metadata(options[:add_tags])
|
681
|
+
end
|
682
|
+
if options[:remove_tags]
|
683
|
+
payload['instance']['removeTags'] = parse_metadata(options[:remove_tags])
|
640
684
|
end
|
641
685
|
if payload['instance'].empty? && params.empty? && options[:owner].nil?
|
642
686
|
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
@@ -1237,7 +1281,18 @@ class Morpheus::Cli::Instances
|
|
1237
1281
|
instance = json_response['instance']
|
1238
1282
|
stats = instance['stats'] || json_response['stats'] || {}
|
1239
1283
|
# load_balancers = json_response['loadBalancers'] || {}
|
1240
|
-
|
1284
|
+
# metadata tags used to be returned as metadata and are now returned as tags
|
1285
|
+
# the problem is tags is what we used to call Labels (keywords)
|
1286
|
+
# the api will change to tags and labels, so handle the old format as long as metadata is returned.
|
1287
|
+
labels = nil
|
1288
|
+
tags = nil
|
1289
|
+
if instance.key?('labels')
|
1290
|
+
labels = instance['labels']
|
1291
|
+
tags = instance['tags']
|
1292
|
+
else
|
1293
|
+
labels = instance['tags']
|
1294
|
+
tags = instance['metadata']
|
1295
|
+
end
|
1241
1296
|
# containers are fetched via separate api call
|
1242
1297
|
containers = nil
|
1243
1298
|
if options[:include_containers]
|
@@ -1278,8 +1333,8 @@ class Morpheus::Cli::Instances
|
|
1278
1333
|
# "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
|
1279
1334
|
# "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
|
1280
1335
|
"Environment" => 'instanceContext',
|
1281
|
-
"Labels" => lambda {|it|
|
1282
|
-
"
|
1336
|
+
"Labels" => lambda {|it| labels ? labels.join(',') : '' },
|
1337
|
+
"Tags" => lambda {|it| tags ? tags.collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' },
|
1283
1338
|
"Owner" => lambda {|it|
|
1284
1339
|
if it['owner']
|
1285
1340
|
(it['owner']['username'] || it['owner']['id'])
|
@@ -1296,13 +1351,18 @@ class Morpheus::Cli::Instances
|
|
1296
1351
|
"Shutdown Date" => lambda {|it| it['shutdownDate'] ? format_local_dt(it['shutdownDate']) : '' },
|
1297
1352
|
"Nodes" => lambda {|it| it['containers'] ? it['containers'].count : 0 },
|
1298
1353
|
"Connection" => lambda {|it| format_instance_connection_string(it) },
|
1354
|
+
"Locked" => lambda {|it| format_boolean(it['locked']) },
|
1299
1355
|
"Status" => lambda {|it| format_instance_status(it) }
|
1300
1356
|
}
|
1357
|
+
description_cols.delete("Labels") if labels.nil? || labels.empty?
|
1358
|
+
description_cols.delete("Tags") if tags.nil? || tags.empty?
|
1301
1359
|
description_cols.delete("Power Schedule") if instance['powerSchedule'].nil?
|
1302
1360
|
description_cols.delete("Expire Date") if instance['expireDate'].nil?
|
1303
1361
|
description_cols.delete("Shutdown Date") if instance['shutdownDate'].nil?
|
1304
1362
|
description_cols["Removal Date"] = lambda {|it| format_local_dt(it['removalDate'])} if instance['status'] == 'pendingRemoval'
|
1305
1363
|
description_cols.delete("Last Deployment") if instance['lastDeploy'].nil?
|
1364
|
+
description_cols.delete("Locked") if instance['locked'] != true
|
1365
|
+
#description_cols.delete("Environment") if instance['instanceContext'].nil?
|
1306
1366
|
print_description_list(description_cols, instance)
|
1307
1367
|
|
1308
1368
|
if instance['statusMessage']
|
@@ -2616,6 +2676,48 @@ class Morpheus::Cli::Instances
|
|
2616
2676
|
end
|
2617
2677
|
end
|
2618
2678
|
|
2679
|
+
def snapshot(args)
|
2680
|
+
options = {}
|
2681
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2682
|
+
opts.banner = subcommand_usage("[instance]")
|
2683
|
+
opts.on( '--name VALUE', String, "Snapshot Name. Default is server name + timestamp" ) do |val|
|
2684
|
+
options[:options]['name'] = val
|
2685
|
+
end
|
2686
|
+
opts.on( '--description VALUE', String, "Snapshot Description." ) do |val|
|
2687
|
+
options[:options]['description'] = val
|
2688
|
+
end
|
2689
|
+
build_standard_add_options(opts, options, [:auto_confirm])
|
2690
|
+
opts.footer = <<-EOT
|
2691
|
+
Create a snapshot for an instance.
|
2692
|
+
[instance] is required. This is the name or id of an instance
|
2693
|
+
EOT
|
2694
|
+
end
|
2695
|
+
optparse.parse!(args)
|
2696
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
2697
|
+
connect(options)
|
2698
|
+
instance = find_instance_by_name_or_id(args[0])
|
2699
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to snapshot the instance '#{instance['name']}'?", options)
|
2700
|
+
exit 1
|
2701
|
+
end
|
2702
|
+
payload = {}
|
2703
|
+
if options[:payload]
|
2704
|
+
payload = options[:payload]
|
2705
|
+
payload.deep_merge!({'snapshot' => parse_passed_options(options)})
|
2706
|
+
else
|
2707
|
+
payload.deep_merge!({'snapshot' => parse_passed_options(options)})
|
2708
|
+
end
|
2709
|
+
@instances_interface.setopts(options)
|
2710
|
+
if options[:dry_run]
|
2711
|
+
print_dry_run @instances_interface.dry.snapshot(instance['id'], payload)
|
2712
|
+
return
|
2713
|
+
end
|
2714
|
+
json_response = @instances_interface.snapshot(instance['id'], payload)
|
2715
|
+
render_response(json_response, options, 'snapshots') do
|
2716
|
+
print_green_success "Snapshot initiated."
|
2717
|
+
end
|
2718
|
+
return 0, nil
|
2719
|
+
end
|
2720
|
+
|
2619
2721
|
def remove(args)
|
2620
2722
|
options = {}
|
2621
2723
|
query_params = {}
|
@@ -2925,6 +3027,10 @@ class Morpheus::Cli::Instances
|
|
2925
3027
|
# no pagination yet
|
2926
3028
|
# build_standard_list_options(opts, options)
|
2927
3029
|
build_standard_get_options(opts, options)
|
3030
|
+
opts.footer = <<-EOT
|
3031
|
+
List snapshots for an instance.
|
3032
|
+
[instance] is required. This is the name or id of an instance
|
3033
|
+
EOT
|
2928
3034
|
end
|
2929
3035
|
optparse.parse!(args)
|
2930
3036
|
verify_args!(args:args, optparse:optparse, count:1)
|
@@ -2947,7 +3053,8 @@ class Morpheus::Cli::Instances
|
|
2947
3053
|
snapshot_column_definitions = {
|
2948
3054
|
"ID" => lambda {|it| it['id'] },
|
2949
3055
|
"Name" => lambda {|it| it['name'] },
|
2950
|
-
"Description" => lambda {|it| it['
|
3056
|
+
"Description" => lambda {|it| it['description'] },
|
3057
|
+
# "Type" => lambda {|it| it['snapshotType'] },
|
2951
3058
|
"Date Created" => lambda {|it| format_local_dt(it['snapshotCreated']) },
|
2952
3059
|
"Status" => lambda {|it| format_snapshot_status(it) }
|
2953
3060
|
}
|
@@ -3784,6 +3891,117 @@ EOT
|
|
3784
3891
|
return 0
|
3785
3892
|
end
|
3786
3893
|
|
3894
|
+
def clone_image(args)
|
3895
|
+
options = {}
|
3896
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
3897
|
+
opts.banner = subcommand_usage("[instance]")
|
3898
|
+
opts.on( '--name VALUE', String, "Image Name (Template Name). Default is server name + timestamp" ) do |val|
|
3899
|
+
options[:options]['templateName'] = val
|
3900
|
+
end
|
3901
|
+
build_standard_update_options(opts, options)
|
3902
|
+
opts.footer = <<-EOT
|
3903
|
+
Clone to image (template) for an instance
|
3904
|
+
[instance] is required. This is the name or id of an instance
|
3905
|
+
EOT
|
3906
|
+
end
|
3907
|
+
optparse.parse!(args)
|
3908
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
3909
|
+
connect(options)
|
3910
|
+
instance = find_instance_by_name_or_id(args[0])
|
3911
|
+
return 1 if instance.nil?
|
3912
|
+
payload = {}
|
3913
|
+
if options[:payload]
|
3914
|
+
payload = options[:payload]
|
3915
|
+
payload.deep_merge!(parse_passed_options(options))
|
3916
|
+
else
|
3917
|
+
payload.deep_merge!(parse_passed_options(options))
|
3918
|
+
if payload['templateName'].nil?
|
3919
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'templateName', 'type' => 'text', 'fieldLabel' => 'Image Name', 'description' => 'Choose a name for the new image template. Default is the server name + timestamp'}], options[:options])
|
3920
|
+
if v_prompt['templateName'].to_s != ''
|
3921
|
+
payload['templateName'] = v_prompt['templateName']
|
3922
|
+
end
|
3923
|
+
end
|
3924
|
+
end
|
3925
|
+
@instances_interface.setopts(options)
|
3926
|
+
if options[:dry_run]
|
3927
|
+
print_dry_run @instances_interface.dry.clone_image(instance['id'], payload)
|
3928
|
+
return
|
3929
|
+
end
|
3930
|
+
json_response = @instances_interface.clone_image(instance['id'], payload)
|
3931
|
+
render_response(json_response, options) do
|
3932
|
+
print_green_success "Clone Image initiated."
|
3933
|
+
end
|
3934
|
+
return 0, nil
|
3935
|
+
end
|
3936
|
+
|
3937
|
+
def lock(args)
|
3938
|
+
options = {}
|
3939
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
3940
|
+
opts.banner = subcommand_usage("[instance]")
|
3941
|
+
build_standard_update_options(opts, options)
|
3942
|
+
opts.footer = <<-EOT
|
3943
|
+
Lock an instance
|
3944
|
+
[instance] is required. This is the name or id of an instance
|
3945
|
+
EOT
|
3946
|
+
end
|
3947
|
+
optparse.parse!(args)
|
3948
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
3949
|
+
connect(options)
|
3950
|
+
instance = find_instance_by_name_or_id(args[0])
|
3951
|
+
return 1 if instance.nil?
|
3952
|
+
payload = {}
|
3953
|
+
if options[:payload]
|
3954
|
+
payload = options[:payload]
|
3955
|
+
payload.deep_merge!(parse_passed_options(options))
|
3956
|
+
else
|
3957
|
+
payload.deep_merge!(parse_passed_options(options))
|
3958
|
+
end
|
3959
|
+
@instances_interface.setopts(options)
|
3960
|
+
if options[:dry_run]
|
3961
|
+
print_dry_run @instances_interface.dry.lock(instance['id'], payload)
|
3962
|
+
return
|
3963
|
+
end
|
3964
|
+
json_response = @instances_interface.lock(instance['id'], payload)
|
3965
|
+
render_response(json_response, options) do
|
3966
|
+
print_green_success "Locked instance #{instance['name']}"
|
3967
|
+
end
|
3968
|
+
return 0, nil
|
3969
|
+
end
|
3970
|
+
|
3971
|
+
def unlock(args)
|
3972
|
+
options = {}
|
3973
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
3974
|
+
opts.banner = subcommand_usage("[instance]")
|
3975
|
+
build_standard_update_options(opts, options)
|
3976
|
+
opts.footer = <<-EOT
|
3977
|
+
Unlock an instance
|
3978
|
+
[instance] is required. This is the name or id of an instance
|
3979
|
+
EOT
|
3980
|
+
end
|
3981
|
+
optparse.parse!(args)
|
3982
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
3983
|
+
connect(options)
|
3984
|
+
instance = find_instance_by_name_or_id(args[0])
|
3985
|
+
return 1 if instance.nil?
|
3986
|
+
payload = {}
|
3987
|
+
if options[:payload]
|
3988
|
+
payload = options[:payload]
|
3989
|
+
payload.deep_merge!(parse_passed_options(options))
|
3990
|
+
else
|
3991
|
+
payload.deep_merge!(parse_passed_options(options))
|
3992
|
+
end
|
3993
|
+
@instances_interface.setopts(options)
|
3994
|
+
if options[:dry_run]
|
3995
|
+
print_dry_run @instances_interface.dry.unlock(instance['id'], payload)
|
3996
|
+
return
|
3997
|
+
end
|
3998
|
+
json_response = @instances_interface.unlock(instance['id'], payload)
|
3999
|
+
render_response(json_response, options) do
|
4000
|
+
print_green_success "Unlocked instance #{instance['name']}"
|
4001
|
+
end
|
4002
|
+
return 0, nil
|
4003
|
+
end
|
4004
|
+
|
3787
4005
|
private
|
3788
4006
|
|
3789
4007
|
def find_zone_by_name_or_id(group_id, val)
|
@@ -3876,51 +4094,6 @@ private
|
|
3876
4094
|
end
|
3877
4095
|
end
|
3878
4096
|
|
3879
|
-
def format_instance_status(instance, return_color=cyan)
|
3880
|
-
out = ""
|
3881
|
-
status_string = instance['status'].to_s
|
3882
|
-
if status_string == 'running'
|
3883
|
-
out << "#{green}#{status_string.upcase}#{return_color}"
|
3884
|
-
elsif status_string == 'provisioning'
|
3885
|
-
out << "#{cyan}#{status_string.upcase}#{return_color}"
|
3886
|
-
elsif status_string == 'stopped' or status_string == 'failed'
|
3887
|
-
out << "#{red}#{status_string.upcase}#{return_color}"
|
3888
|
-
else
|
3889
|
-
out << "#{yellow}#{status_string.upcase}#{return_color}"
|
3890
|
-
end
|
3891
|
-
out
|
3892
|
-
end
|
3893
|
-
|
3894
|
-
def format_instance_connection_string(instance)
|
3895
|
-
if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
|
3896
|
-
connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
|
3897
|
-
end
|
3898
|
-
end
|
3899
|
-
|
3900
|
-
def format_container_status(container, return_color=cyan)
|
3901
|
-
out = ""
|
3902
|
-
status_string = container['status'].to_s
|
3903
|
-
if status_string == 'running'
|
3904
|
-
out << "#{green}#{status_string.upcase}#{return_color}"
|
3905
|
-
elsif status_string == 'provisioning'
|
3906
|
-
out << "#{cyan}#{status_string.upcase}#{return_color}"
|
3907
|
-
elsif status_string == 'stopped' or status_string == 'failed'
|
3908
|
-
out << "#{red}#{status_string.upcase}#{return_color}"
|
3909
|
-
else
|
3910
|
-
out << "#{yellow}#{status_string.upcase}#{return_color}"
|
3911
|
-
end
|
3912
|
-
out
|
3913
|
-
end
|
3914
|
-
|
3915
|
-
def format_container_connection_string(container)
|
3916
|
-
if !container['ports'].nil? && container['ports'].empty? == false
|
3917
|
-
connection_string = "#{container['ip']}:#{container['ports'][0]['external']}"
|
3918
|
-
else
|
3919
|
-
# eh? more logic needed here i think, see taglib morph:containerLocationMenu
|
3920
|
-
connection_string = "#{container['ip']}"
|
3921
|
-
end
|
3922
|
-
end
|
3923
|
-
|
3924
4097
|
def instance_scaling_option_types(instance=nil)
|
3925
4098
|
|
3926
4099
|
# Group
|
@@ -3974,17 +4147,6 @@ private
|
|
3974
4147
|
list
|
3975
4148
|
end
|
3976
4149
|
|
3977
|
-
def format_instance_container_display_name(instance, plural=false)
|
3978
|
-
#<span class="info-label">${[null,'docker'].contains(instance.layout?.provisionType?.code) ? 'Containers' : 'Virtual Machines'}:</span> <span class="info-value">${instance.containers?.size()}</span>
|
3979
|
-
v = plural ? "Containers" : "Container"
|
3980
|
-
if instance && instance['layout'] && instance['layout'].key?("provisionTypeCode")
|
3981
|
-
if [nil, 'docker'].include?(instance['layout']["provisionTypeCode"])
|
3982
|
-
v = plural ? "Virtual Machines" : "Virtual Machine"
|
3983
|
-
end
|
3984
|
-
end
|
3985
|
-
return v
|
3986
|
-
end
|
3987
|
-
|
3988
4150
|
def print_instance_threshold_description_list(instance_threshold)
|
3989
4151
|
description_cols = {
|
3990
4152
|
# "Instance" => lambda {|it| "#{instance['id']} - #{instance['name']}" },
|