morpheus-cli 5.5.1.4 → 5.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +25 -0
- data/lib/morpheus/api/archive_buckets_interface.rb +1 -1
- data/lib/morpheus/api/body_io.rb +22 -0
- data/lib/morpheus/api/catalog_item_types_interface.rb +5 -1
- data/lib/morpheus/api/clients_interface.rb +41 -0
- data/lib/morpheus/api/clouds_interface.rb +21 -0
- data/lib/morpheus/api/instances_interface.rb +8 -1
- data/lib/morpheus/api/integrations_interface.rb +30 -0
- data/lib/morpheus/api/library_instance_types_interface.rb +15 -3
- data/lib/morpheus/api/network_pool_server_types_interface.rb +9 -0
- data/lib/morpheus/api/plugins_interface.rb +22 -0
- data/lib/morpheus/api/roles_interface.rb +20 -1
- data/lib/morpheus/api/security_package_types_interface.rb +9 -0
- data/lib/morpheus/api/security_packages_interface.rb +9 -0
- data/lib/morpheus/api/security_scans_interface.rb +9 -0
- data/lib/morpheus/api/servers_interface.rb +17 -17
- data/lib/morpheus/api/storage_providers_interface.rb +1 -1
- data/lib/morpheus/api/virtual_images_interface.rb +1 -23
- data/lib/morpheus/cli/cli_command.rb +81 -7
- data/lib/morpheus/cli/commands/apps.rb +28 -2
- data/lib/morpheus/cli/commands/archives_command.rb +2 -2
- data/lib/morpheus/cli/commands/blueprints_command.rb +16 -0
- data/lib/morpheus/cli/commands/catalog_item_types_command.rb +34 -2
- data/lib/morpheus/cli/commands/clients_command.rb +338 -0
- data/lib/morpheus/cli/commands/clouds.rb +127 -1
- data/lib/morpheus/cli/commands/clusters.rb +42 -12
- data/lib/morpheus/cli/commands/curl_command.rb +114 -135
- data/lib/morpheus/cli/commands/hosts.rb +108 -11
- data/lib/morpheus/cli/commands/instances.rb +115 -14
- data/lib/morpheus/cli/commands/integrations_command.rb +215 -4
- data/lib/morpheus/cli/commands/invoices_command.rb +20 -11
- data/lib/morpheus/cli/commands/jobs_command.rb +299 -190
- data/lib/morpheus/cli/commands/library_cluster_layouts_command.rb +16 -2
- data/lib/morpheus/cli/commands/library_container_scripts_command.rb +14 -0
- data/lib/morpheus/cli/commands/library_container_templates_command.rb +131 -48
- data/lib/morpheus/cli/commands/library_container_types_command.rb +17 -4
- data/lib/morpheus/cli/commands/library_instance_types_command.rb +85 -7
- data/lib/morpheus/cli/commands/library_layouts_command.rb +32 -1
- data/lib/morpheus/cli/commands/library_option_lists_command.rb +30 -18
- data/lib/morpheus/cli/commands/library_option_types_command.rb +31 -14
- data/lib/morpheus/cli/commands/library_spec_templates_command.rb +14 -0
- data/lib/morpheus/cli/commands/library_upgrades_command.rb +2 -2
- data/lib/morpheus/cli/commands/network_pool_server_types.rb +20 -0
- data/lib/morpheus/cli/commands/network_pool_servers_command.rb +55 -158
- data/lib/morpheus/cli/commands/network_pools_command.rb +49 -23
- data/lib/morpheus/cli/commands/networks_command.rb +262 -45
- data/lib/morpheus/cli/commands/plugins.rb +213 -0
- data/lib/morpheus/cli/commands/price_sets_command.rb +40 -10
- data/lib/morpheus/cli/commands/prices_command.rb +17 -5
- data/lib/morpheus/cli/commands/processes_command.rb +2 -1
- data/lib/morpheus/cli/commands/remote.rb +7 -10
- data/lib/morpheus/cli/commands/roles.rb +924 -335
- data/lib/morpheus/cli/commands/search_command.rb +2 -0
- data/lib/morpheus/cli/commands/security_groups.rb +72 -84
- data/lib/morpheus/cli/commands/security_package_types.rb +32 -0
- data/lib/morpheus/cli/commands/security_packages.rb +84 -0
- data/lib/morpheus/cli/commands/security_scans.rb +107 -0
- data/lib/morpheus/cli/commands/service_plans_command.rb +16 -14
- data/lib/morpheus/cli/commands/subnets_command.rb +15 -1
- data/lib/morpheus/cli/commands/tasks.rb +34 -1
- data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
- data/lib/morpheus/cli/commands/user_settings_command.rb +11 -2
- data/lib/morpheus/cli/commands/users.rb +50 -9
- data/lib/morpheus/cli/commands/virtual_images.rb +14 -0
- data/lib/morpheus/cli/commands/workflows.rb +14 -0
- data/lib/morpheus/cli/mixins/accounts_helper.rb +6 -5
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +79 -0
- data/lib/morpheus/cli/mixins/jobs_helper.rb +4 -5
- data/lib/morpheus/cli/mixins/library_helper.rb +2 -0
- data/lib/morpheus/cli/mixins/logs_helper.rb +3 -0
- data/lib/morpheus/cli/mixins/monitoring_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/print_helper.rb +29 -4
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +38 -9
- data/lib/morpheus/cli/mixins/rest_command.rb +106 -8
- data/lib/morpheus/cli/mixins/secondary_rest_command.rb +6 -2
- data/lib/morpheus/cli/option_types.rb +94 -25
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/formatters.rb +10 -1
- metadata +15 -2
@@ -8,7 +8,7 @@ class Morpheus::Cli::Hosts
|
|
8
8
|
set_command_name :hosts
|
9
9
|
set_command_description "View and manage hosts (servers)."
|
10
10
|
register_subcommands :list, :count, :get, :view, :stats, :add, :update, :remove, :logs, :start, :stop, :resize,
|
11
|
-
:run_workflow, :make_managed, :upgrade_agent, :snapshots, :software, :software_sync,
|
11
|
+
:run_workflow, :make_managed, :upgrade_agent, :snapshots, :software, :software_sync, :update_network_label,
|
12
12
|
{:'types' => :list_types},
|
13
13
|
{:exec => :execution_request},
|
14
14
|
:wiki, :update_wiki
|
@@ -109,11 +109,11 @@ class Morpheus::Cli::Hosts
|
|
109
109
|
opts.on( '--tenant TENANT', "Tenant Name or ID" ) do |val|
|
110
110
|
options[:account] = val
|
111
111
|
end
|
112
|
-
opts.on('--labels
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
112
|
+
opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val|
|
113
|
+
add_query_parameter(params, 'labels', parse_labels(val))
|
114
|
+
end
|
115
|
+
opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
|
116
|
+
add_query_parameter(params, 'allLabels', parse_labels(val))
|
117
117
|
end
|
118
118
|
opts.on('--tags Name=Value',String, "Filter by tags.") do |val|
|
119
119
|
val.split(",").each do |value_pair|
|
@@ -519,7 +519,7 @@ class Morpheus::Cli::Hosts
|
|
519
519
|
"Name" => 'name',
|
520
520
|
"Hostname" => 'hostname',
|
521
521
|
"Description" => 'description',
|
522
|
-
"Labels" => lambda {|it| it['labels']
|
522
|
+
"Labels" => lambda {|it| format_list(it['labels']) rescue '' },
|
523
523
|
"Tags" => lambda {|it| tags ? format_metadata(tags) : '' },
|
524
524
|
"Owner" => lambda {|it| it['owner'] ? it['owner']['username'] : '' },
|
525
525
|
"Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
@@ -751,6 +751,16 @@ class Morpheus::Cli::Hosts
|
|
751
751
|
opts.on("--security-groups LIST", Integer, "Security Groups, comma separated list of security group IDs") do |val|
|
752
752
|
options[:security_groups] = val.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
|
753
753
|
end
|
754
|
+
opts.on('--tags LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
|
755
|
+
options[:metadata] = val
|
756
|
+
end
|
757
|
+
opts.on('--metadata [LIST]', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
|
758
|
+
options[:metadata] = val
|
759
|
+
end
|
760
|
+
opts.add_hidden_option('--metadata')
|
761
|
+
opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
|
762
|
+
options[:options]['labels'] = parse_labels(val)
|
763
|
+
end
|
754
764
|
opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
|
755
765
|
options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
|
756
766
|
end
|
@@ -933,7 +943,18 @@ class Morpheus::Cli::Hosts
|
|
933
943
|
end
|
934
944
|
end
|
935
945
|
end
|
936
|
-
|
946
|
+
|
947
|
+
# Metadata Tags
|
948
|
+
if metadata_option_type
|
949
|
+
if options[:metadata]
|
950
|
+
metadata = parse_metadata(options[:metadata])
|
951
|
+
payload['tags'] = metadata if !metadata.empty?
|
952
|
+
else
|
953
|
+
metadata = prompt_metadata(options)
|
954
|
+
payload['tags'] = metadata if !metadata.empty?
|
955
|
+
end
|
956
|
+
end
|
957
|
+
|
937
958
|
api_params = {}
|
938
959
|
api_params['zoneId'] = cloud['id']
|
939
960
|
api_params['poolId'] = payload['config']['resourcePool'] if (payload['config'] && payload['config']['resourcePool'])
|
@@ -988,8 +1009,8 @@ class Morpheus::Cli::Hosts
|
|
988
1009
|
opts.on('--power-schedule-type ID', String, "Power Schedule Type ID") do |val|
|
989
1010
|
params['powerScheduleType'] = val == "null" ? nil : val
|
990
1011
|
end
|
991
|
-
opts.on('--labels [LIST]', String, "Labels
|
992
|
-
params['labels'] = val
|
1012
|
+
opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
|
1013
|
+
params['labels'] = parse_labels(val)
|
993
1014
|
end
|
994
1015
|
opts.on('--tags LIST', String, "Tags in the format 'name:value, name:value'. This will add and remove tags.") do |val|
|
995
1016
|
options[:tags] = val
|
@@ -1276,7 +1297,10 @@ class Morpheus::Cli::Hosts
|
|
1276
1297
|
current_volumes = volumes_response['volumes'].sort {|x,y| x['displayOrder'] <=> y['displayOrder'] }
|
1277
1298
|
|
1278
1299
|
# prompt for volumes
|
1279
|
-
|
1300
|
+
vol_options = options
|
1301
|
+
vol_options['siteId'] = group_id
|
1302
|
+
vol_options['zoneId'] = cloud_id
|
1303
|
+
volumes = prompt_resize_volumes(current_volumes, service_plan, provision_type, vol_options)
|
1280
1304
|
if !volumes.empty?
|
1281
1305
|
payload[:volumes] = volumes
|
1282
1306
|
end
|
@@ -2219,6 +2243,79 @@ EOT
|
|
2219
2243
|
end
|
2220
2244
|
out
|
2221
2245
|
end
|
2246
|
+
|
2247
|
+
def update_network_label(args)
|
2248
|
+
options = {}
|
2249
|
+
params = {}
|
2250
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
2251
|
+
opts.banner = subcommand_usage("[server] [options]")
|
2252
|
+
opts.on('--network NETWORK', "Network Interface ID" ) do |val|
|
2253
|
+
options[:network] = val
|
2254
|
+
end
|
2255
|
+
opts.on('--label LABEL', "label") do |val|
|
2256
|
+
options[:label] = val
|
2257
|
+
end
|
2258
|
+
opts.footer = "Change the label of a Network Interface.\n" +
|
2259
|
+
"Editing an Interface will not apply changes to the physical hardware. The purpose is for a manual override or data correction (mostly for self managed or baremetal servers where cloud sync is not available)\n" +
|
2260
|
+
"[name or id] is required. The name or the id of the server.\n" +
|
2261
|
+
"[network] ID of the Network Interface. (optional).\n" +
|
2262
|
+
"[label] New Label name for the Network Interface (optional)"
|
2263
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
2264
|
+
end
|
2265
|
+
optparse.parse!(args)
|
2266
|
+
if args.count != 1
|
2267
|
+
puts_error "#{Morpheus::Terminal.angry_prompt}wrong number of arguments. Expected 1 and received #{args.count} #{args.inspect}\n#{optparse}"
|
2268
|
+
return 1
|
2269
|
+
end
|
2270
|
+
connect(options)
|
2271
|
+
|
2272
|
+
begin
|
2273
|
+
host = find_host_by_name_or_id(args[0])
|
2274
|
+
return 1 if host.nil?
|
2275
|
+
|
2276
|
+
network_id = options[:network]
|
2277
|
+
if network_id != nil && network_id.to_i == 0
|
2278
|
+
print_red_alert "network must be an ID/integer above 0, not a name/string value."
|
2279
|
+
network_id = nil
|
2280
|
+
end
|
2281
|
+
|
2282
|
+
|
2283
|
+
if !network_id
|
2284
|
+
available_networks = get_available_networks(host)
|
2285
|
+
network_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'network', 'fieldLabel' => 'Network', 'type' => 'select', 'selectOptions' => available_networks, 'required' => true, 'defaultValue' => available_networks[0], 'description' => "The networks available for relabeling"}], options[:options])
|
2286
|
+
network_id = network_prompt['network']
|
2287
|
+
end
|
2288
|
+
|
2289
|
+
label = options[:label]
|
2290
|
+
while label.nil? do
|
2291
|
+
label_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'label', 'fieldLabel' => 'Label', 'type' => 'text', 'required' => true}], options[:options])
|
2292
|
+
label = label_prompt['label']
|
2293
|
+
end
|
2294
|
+
payload = { "name" => label }
|
2295
|
+
if options[:dry_run]
|
2296
|
+
print_dry_run @servers_interface.dry.update_network_label(network_id, host["id"], payload)
|
2297
|
+
return
|
2298
|
+
end
|
2299
|
+
json_response = @servers_interface.update_network_label(network_id, host["id"], payload)
|
2300
|
+
if options[:json]
|
2301
|
+
puts as_json(json_response, options)
|
2302
|
+
else
|
2303
|
+
print_green_success "Updated label for host #{host['name']} network #{network_id} to #{label}"
|
2304
|
+
end
|
2305
|
+
return 0
|
2306
|
+
rescue RestClient::Exception => e
|
2307
|
+
print_rest_exception(e, options)
|
2308
|
+
exit 1
|
2309
|
+
end
|
2310
|
+
end
|
2311
|
+
|
2312
|
+
def get_available_networks(host)
|
2313
|
+
results = @options_interface.options_for_source('availableNetworksForHost',{serverId: host['id'].to_i})
|
2314
|
+
available_networks = results['data'].collect {|it|
|
2315
|
+
{"id" => it["value"], "name" => it["name"], "value" => it["value"]}
|
2316
|
+
}
|
2317
|
+
return available_networks
|
2318
|
+
end
|
2222
2319
|
|
2223
2320
|
|
2224
2321
|
def make_managed_option_types(connected=true)
|
@@ -23,6 +23,7 @@ class Morpheus::Cli::Instances
|
|
23
23
|
:console, :status_check, {:containers => :list_containers},
|
24
24
|
:scaling, {:'scaling-update' => :scaling_update},
|
25
25
|
:wiki, :update_wiki,
|
26
|
+
:update_network_label,
|
26
27
|
{:exec => :execution_request},
|
27
28
|
:deploys,
|
28
29
|
:refresh, :prepare_apply, :apply, :state
|
@@ -121,11 +122,11 @@ class Morpheus::Cli::Instances
|
|
121
122
|
opts.on( '--plan-code CODE', String, "Filter by Plan code(s)" ) do |val|
|
122
123
|
params['planCode'] = parse_id_list(val)
|
123
124
|
end
|
124
|
-
opts.on('--labels
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
125
|
+
opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val|
|
126
|
+
add_query_parameter(params, 'labels', parse_labels(val))
|
127
|
+
end
|
128
|
+
opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
|
129
|
+
add_query_parameter(params, 'allLabels', parse_labels(val))
|
129
130
|
end
|
130
131
|
opts.on('--tags Name=Value',String, "Filter by tags (metadata name value pairs).") do |val|
|
131
132
|
val.split(",").each do |value_pair|
|
@@ -182,7 +183,6 @@ class Morpheus::Cli::Instances
|
|
182
183
|
|
183
184
|
params['showDeleted'] = true if options[:showDeleted]
|
184
185
|
params['deleted'] = true if options[:deleted]
|
185
|
-
params['labels'] = options[:labels] if options[:labels]
|
186
186
|
if options[:tags]
|
187
187
|
options[:tags].each do |k,v|
|
188
188
|
params['tags.' + k] = v
|
@@ -257,6 +257,7 @@ class Morpheus::Cli::Instances
|
|
257
257
|
row = {
|
258
258
|
id: instance['id'],
|
259
259
|
name: instance['name'],
|
260
|
+
labels: format_list(instance['labels'], '', 3),
|
260
261
|
connection: format_instance_connection_string(instance),
|
261
262
|
environment: instance['instanceContext'],
|
262
263
|
user: (instance['owner'] ? (instance['owner']['username'] || instance['owner']['id']) : (instance['createdBy'].is_a?(Hash) ? instance['createdBy']['username'] : instance['createdBy'])),
|
@@ -275,16 +276,16 @@ class Morpheus::Cli::Instances
|
|
275
276
|
}
|
276
277
|
row
|
277
278
|
}
|
278
|
-
columns = [:id, {:name => {:max_width => 50}}, :group, :cloud,
|
279
|
-
:type, :version, :environment,
|
279
|
+
columns = [:id, {:name => {:max_width => 50}}, :labels, :group, :cloud,
|
280
|
+
:type, :version, :environment, :plan,
|
280
281
|
{:created => {:display_name => "CREATED"}},
|
281
282
|
# {:tenant => {:display_name => "TENANT"}},
|
282
283
|
{:user => {:display_name => "OWNER", :max_width => 20}},
|
283
|
-
:plan,
|
284
284
|
:nodes, {:connection => {:max_width => 30}}, :status, :cpu, :memory, :storage]
|
285
285
|
# custom pretty table columns ... this is handled in as_pretty_table now(),
|
286
286
|
# todo: remove all these.. and try to always pass rows as the json data itself..
|
287
287
|
if options[:details] != true
|
288
|
+
columns.delete(:labels)
|
288
289
|
columns.delete(:plan)
|
289
290
|
end
|
290
291
|
print cyan
|
@@ -404,7 +405,7 @@ class Morpheus::Cli::Instances
|
|
404
405
|
end
|
405
406
|
opts.add_hidden_option('--metadata')
|
406
407
|
opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
|
407
|
-
options[:labels] = val
|
408
|
+
options[:labels] = parse_labels(val)
|
408
409
|
end
|
409
410
|
opts.on("--copies NUMBER", Integer, "Number of copies to provision") do |val|
|
410
411
|
options[:copies] = val.to_i
|
@@ -608,7 +609,7 @@ class Morpheus::Cli::Instances
|
|
608
609
|
options[:group] = val
|
609
610
|
end
|
610
611
|
opts.on('--labels [LIST]', String, "Labels (keywords) in the format 'foo, bar'") do |val|
|
611
|
-
params['labels'] = val
|
612
|
+
params['labels'] = parse_labels(val)
|
612
613
|
end
|
613
614
|
opts.on('--tags LIST', String, "Tags in the format 'name:value, name:value'. This will add and remove tags.") do |val|
|
614
615
|
options[:tags] = val
|
@@ -902,6 +903,74 @@ class Morpheus::Cli::Instances
|
|
902
903
|
end
|
903
904
|
end
|
904
905
|
|
906
|
+
def update_network_label(args)
|
907
|
+
options = {}
|
908
|
+
params = {}
|
909
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
910
|
+
opts.banner = subcommand_usage("[instance] [options]")
|
911
|
+
opts.on('--network NETWORK', "Network Interface ID" ) do |val|
|
912
|
+
options[:network] = val
|
913
|
+
end
|
914
|
+
opts.on('--label LABEL', "label") do |val|
|
915
|
+
options[:label] = val
|
916
|
+
end
|
917
|
+
opts.footer = "Change the label of a Network Interface.\n" +
|
918
|
+
"Editing an Interface will not apply changes to the physical hardware. The purpose is for a manual override or data correction (mostly for self managed or baremetal servers where cloud sync is not available)\n" +
|
919
|
+
"[name or id] is required. The name or the id of the instance.\n" +
|
920
|
+
"[network] ID of the Network Interface. (optional).\n" +
|
921
|
+
"[label] New Label name for the Network Interface (optional)"
|
922
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
923
|
+
end
|
924
|
+
optparse.parse!(args)
|
925
|
+
if args.count != 1
|
926
|
+
puts_error "#{Morpheus::Terminal.angry_prompt}wrong number of arguments. Expected 1 and received #{args.count} #{args.inspect}\n#{optparse}"
|
927
|
+
return 1
|
928
|
+
end
|
929
|
+
connect(options)
|
930
|
+
|
931
|
+
begin
|
932
|
+
instance = find_instance_by_name_or_id(args[0])
|
933
|
+
return 1 if instance.nil?
|
934
|
+
|
935
|
+
network_id = options[:network]
|
936
|
+
if network_id != nil && network_id.to_i == 0
|
937
|
+
print_red_alert "network must be an ID/integer above 0, not a name/string value."
|
938
|
+
network_id = nil
|
939
|
+
end
|
940
|
+
|
941
|
+
if !network_id
|
942
|
+
available_networks = get_available_networks(instance)
|
943
|
+
network_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'network', 'fieldLabel' => 'Network', 'type' => 'select', 'selectOptions' => available_networks, 'required' => true, 'defaultValue' => available_networks[0], 'description' => "The networks available for relabeling"}], options[:options])
|
944
|
+
network_id = network_prompt['network']
|
945
|
+
end
|
946
|
+
|
947
|
+
|
948
|
+
|
949
|
+
label = options[:label]
|
950
|
+
while label.nil? do
|
951
|
+
label_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'label', 'fieldLabel' => 'Label', 'type' => 'text', 'required' => true}], options[:options])
|
952
|
+
label = label_prompt['label']
|
953
|
+
end
|
954
|
+
payload = { "name" => label }
|
955
|
+
if options[:dry_run]
|
956
|
+
print_dry_run @instances_interface.dry.update_network_label(network_id, instance["id"], payload)
|
957
|
+
return
|
958
|
+
end
|
959
|
+
json_response = @instances_interface.update_network_label(network_id, instance["id"], payload)
|
960
|
+
if options[:json]
|
961
|
+
puts as_json(json_response, options)
|
962
|
+
else
|
963
|
+
print_green_success "Updated label for instance #{instance['name']} network #{network_id} to #{label}"
|
964
|
+
end
|
965
|
+
return 0
|
966
|
+
rescue RestClient::Exception => e
|
967
|
+
print_rest_exception(e, options)
|
968
|
+
exit 1
|
969
|
+
end
|
970
|
+
end
|
971
|
+
|
972
|
+
|
973
|
+
|
905
974
|
def status_check(args)
|
906
975
|
out = ""
|
907
976
|
options = {}
|
@@ -1725,7 +1794,7 @@ class Morpheus::Cli::Instances
|
|
1725
1794
|
end
|
1726
1795
|
opts.add_hidden_option('--metadata')
|
1727
1796
|
opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
|
1728
|
-
options[:labels] = val
|
1797
|
+
options[:labels] = parse_labels(val)
|
1729
1798
|
end
|
1730
1799
|
# opts.on("--copies NUMBER", Integer, "Number of copies to provision") do |val|
|
1731
1800
|
# options[:copies] = val.to_i
|
@@ -2697,13 +2766,19 @@ class Morpheus::Cli::Instances
|
|
2697
2766
|
cloud_id = instance['cloud']['id']
|
2698
2767
|
layout_id = instance['layout']['id']
|
2699
2768
|
plan_id = instance['plan']['id']
|
2769
|
+
resource_pool_id = instance['config']['resourcePoolId'] if instance['config']
|
2700
2770
|
current_plan_name = instance['plan']['name']
|
2771
|
+
current_interfaces = get_instance_interfaces(instance)
|
2772
|
+
if current_interfaces != false
|
2773
|
+
payload['networkInterfaces'] = current_interfaces
|
2774
|
+
end
|
2775
|
+
|
2701
2776
|
|
2702
2777
|
# need to GET provision type for some settings...
|
2703
2778
|
provision_type = @provision_types_interface.get(instance['layout']['provisionTypeId'])['provisionType']
|
2704
2779
|
|
2705
2780
|
# prompt for service plan
|
2706
|
-
service_plans_json = @instances_interface.service_plans({zoneId: cloud_id, siteId: group_id, layoutId: layout_id})
|
2781
|
+
service_plans_json = @instances_interface.service_plans({zoneId: cloud_id, siteId: group_id, layoutId: layout_id, resourcePoolId: resource_pool_id})
|
2707
2782
|
service_plans = service_plans_json["plans"]
|
2708
2783
|
service_plans_dropdown = service_plans.collect {|sp| {'name' => sp["name"], 'value' => sp["id"]} } # already sorted
|
2709
2784
|
service_plans_dropdown.each do |plan|
|
@@ -2723,7 +2798,10 @@ class Morpheus::Cli::Instances
|
|
2723
2798
|
current_volumes = volumes_response['volumes'].sort {|x,y| x['displayOrder'] <=> y['displayOrder'] }
|
2724
2799
|
|
2725
2800
|
# prompt for volumes
|
2726
|
-
|
2801
|
+
vol_options = options
|
2802
|
+
vol_options['siteId'] = group_id
|
2803
|
+
vol_options['zoneId'] = cloud_id
|
2804
|
+
volumes = prompt_resize_volumes(current_volumes, service_plan, provision_type, vol_options)
|
2727
2805
|
if !volumes.empty?
|
2728
2806
|
payload["volumes"] = volumes
|
2729
2807
|
end
|
@@ -5184,4 +5262,27 @@ private
|
|
5184
5262
|
}
|
5185
5263
|
end
|
5186
5264
|
|
5265
|
+
def get_available_networks(instance)
|
5266
|
+
results = @options_interface.options_for_source('availableNetworksForInstance',{instanceId: instance['id'].to_i})
|
5267
|
+
available_networks = results['data'].collect {|it|
|
5268
|
+
{"id" => it["value"], "name" => it["name"], "value" => it["value"]}
|
5269
|
+
}
|
5270
|
+
return available_networks
|
5271
|
+
end
|
5272
|
+
|
5273
|
+
def get_instance_interfaces(instance)
|
5274
|
+
begin
|
5275
|
+
servers = instance['servers']
|
5276
|
+
interfaces = []
|
5277
|
+
servers.each do |server|
|
5278
|
+
details = @servers_interface.get(server.to_i)['server']
|
5279
|
+
details['interfaces'].each do |inter|
|
5280
|
+
interfaces.push(inter)
|
5281
|
+
end
|
5282
|
+
end
|
5283
|
+
return interfaces
|
5284
|
+
rescue
|
5285
|
+
return false
|
5286
|
+
end
|
5287
|
+
end
|
5187
5288
|
end
|
@@ -8,6 +8,7 @@ class Morpheus::Cli::IntegrationsCommand
|
|
8
8
|
|
9
9
|
register_subcommands :list, :get, :add, :update, :remove, :refresh
|
10
10
|
register_subcommands :list_objects, :get_object, :add_object, :remove_object
|
11
|
+
register_subcommands :list_inventory, :get_inventory, :update_inventory
|
11
12
|
register_subcommands :list_types, :get_type
|
12
13
|
|
13
14
|
def connect(opts)
|
@@ -936,6 +937,170 @@ EOT
|
|
936
937
|
return 0, nil
|
937
938
|
end
|
938
939
|
|
940
|
+
def list_inventory(args)
|
941
|
+
options = {}
|
942
|
+
params = {}
|
943
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
944
|
+
opts.banner = subcommand_usage("[integration] [search]")
|
945
|
+
build_standard_list_options(opts, options)
|
946
|
+
opts.footer = <<-EOT
|
947
|
+
List integration inventory.
|
948
|
+
[integration] is required. This is the name or id of an integration.
|
949
|
+
Only certain types of integrations support this operation, such as Ansible Tower.
|
950
|
+
EOT
|
951
|
+
end
|
952
|
+
optparse.parse!(args)
|
953
|
+
verify_args!(args:args, optparse:optparse, min:1)
|
954
|
+
connect(options)
|
955
|
+
|
956
|
+
integration = find_integration_by_name_or_id(args[0])
|
957
|
+
return 1, "integration not found for #{args[0]}" if integration.nil?
|
958
|
+
|
959
|
+
if args.count > 1
|
960
|
+
options[:phrase] = args[1..-1].join(" ")
|
961
|
+
end
|
962
|
+
params.merge!(parse_list_options(options))
|
963
|
+
@integrations_interface.setopts(options)
|
964
|
+
if options[:dry_run]
|
965
|
+
print_dry_run @integrations_interface.dry.list_inventory(integration['id'], params)
|
966
|
+
return 0, nil
|
967
|
+
end
|
968
|
+
json_response = @integrations_interface.list_inventory(integration['id'], params)
|
969
|
+
render_response(json_response, options, integration_inventory_list_key) do
|
970
|
+
integration_inventory = json_response[integration_inventory_list_key]
|
971
|
+
print_h1 "Integration Inventory [#{integration['name']}]", parse_list_subtitles(options), options
|
972
|
+
if integration_inventory.empty?
|
973
|
+
print cyan,"No inventory found.",reset,"\n"
|
974
|
+
else
|
975
|
+
list_columns = {
|
976
|
+
"ID" => 'id',
|
977
|
+
"Name" => 'name',
|
978
|
+
"Description" => 'description',
|
979
|
+
"Tenant Default" => lambda {|it| (format_list(it['tenants'].collect {|t| t['name'] }) rescue "") },
|
980
|
+
}
|
981
|
+
print as_pretty_table(integration_inventory, list_columns.upcase_keys!, options)
|
982
|
+
print_results_pagination(json_response)
|
983
|
+
end
|
984
|
+
print reset,"\n"
|
985
|
+
end
|
986
|
+
return 0, nil
|
987
|
+
end
|
988
|
+
|
989
|
+
def get_inventory(args)
|
990
|
+
params = {}
|
991
|
+
options = {}
|
992
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
993
|
+
opts.banner = subcommand_usage("[integration] [inventory]")
|
994
|
+
build_standard_get_options(opts, options)
|
995
|
+
opts.footer = <<-EOT
|
996
|
+
Get details about a specific integration inventory item.
|
997
|
+
[integration] is required. This is the name or id of an integration.
|
998
|
+
[inventory] is required. This is the name or id of an integration inventory item.
|
999
|
+
Only certain types of integrations support this operation, such as Ansible Tower.
|
1000
|
+
EOT
|
1001
|
+
end
|
1002
|
+
optparse.parse!(args)
|
1003
|
+
verify_args!(args:args, optparse:optparse, min:2)
|
1004
|
+
connect(options)
|
1005
|
+
integration = find_integration_by_name_or_id(args[0])
|
1006
|
+
return 1, "integration not found for #{args[0]}" if integration.nil?
|
1007
|
+
params.merge!(parse_query_options(options))
|
1008
|
+
id_list = parse_id_list(args[1..-1])
|
1009
|
+
return run_command_for_each_arg(id_list) do |arg|
|
1010
|
+
_get_inventory(integration, arg, params, options)
|
1011
|
+
end
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
def _get_inventory(integration, id, params, options)
|
1015
|
+
integration_object = nil
|
1016
|
+
if id.to_s !~ /\A\d{1,}\Z/
|
1017
|
+
integration_inventory = find_integration_inventory_by_name_or_id(integration['id'], id)
|
1018
|
+
return 1, "integration inventory not found for #{id}" if integration_inventory.nil?
|
1019
|
+
id = integration_inventory['id']
|
1020
|
+
end
|
1021
|
+
@integrations_interface.setopts(options)
|
1022
|
+
if options[:dry_run]
|
1023
|
+
print_dry_run @integrations_interface.dry.get_inventory(integration['id'], id, params)
|
1024
|
+
return
|
1025
|
+
end
|
1026
|
+
json_response = @integrations_interface.get_inventory(integration['id'], id, params)
|
1027
|
+
integration_inventory = json_response[integration_inventory_object_key]
|
1028
|
+
render_response(json_response, options, integration_inventory_object_key) do
|
1029
|
+
print_h1 "Integration Inventory Details", [], options
|
1030
|
+
print cyan
|
1031
|
+
show_columns = {
|
1032
|
+
"ID" => 'id',
|
1033
|
+
"Name" => 'name',
|
1034
|
+
"Description" => 'description',
|
1035
|
+
"Tenant Default" => lambda {|it| (format_list(it['tenants'].collect {|t| t['name'] }) rescue "") },
|
1036
|
+
}
|
1037
|
+
print_description_list(show_columns, integration_inventory, options)
|
1038
|
+
print reset,"\n"
|
1039
|
+
end
|
1040
|
+
return 0, nil
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
def update_inventory(args)
|
1044
|
+
options = {}
|
1045
|
+
params = {}
|
1046
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1047
|
+
opts.banner = subcommand_usage("[integration] [inventory] [options]")
|
1048
|
+
opts.on('--tenants [LIST]', String, "Tenant Default, comma separated list of account IDs") do |val|
|
1049
|
+
options[:tenants] = parse_array(val)
|
1050
|
+
end
|
1051
|
+
build_standard_update_options(opts, options)
|
1052
|
+
opts.footer = <<-EOT
|
1053
|
+
Update an integration inventory item.
|
1054
|
+
[integration] is required. This is the name or id of an integration.
|
1055
|
+
[inventory] is required. This is the name or id of an integration inventory item.
|
1056
|
+
Only certain types of integrations support this operation, such as Ansible Tower.
|
1057
|
+
EOT
|
1058
|
+
end
|
1059
|
+
optparse.parse!(args)
|
1060
|
+
verify_args!(args:args, optparse:optparse, count:2)
|
1061
|
+
connect(options)
|
1062
|
+
integration = find_integration_by_name_or_id(args[0])
|
1063
|
+
return 1, "integration not found for #{args[0]}" if integration.nil?
|
1064
|
+
integration_inventory = find_integration_inventory_by_name_or_id(integration['id'], args[1])
|
1065
|
+
return 1, "integration inventory not found for #{args[1]}" if integration_inventory.nil?
|
1066
|
+
# construct payload
|
1067
|
+
object_key = integration_inventory_object_key
|
1068
|
+
payload = build_payload(options, object_key)
|
1069
|
+
if options[:tenants]
|
1070
|
+
#params['tenants'] = options[:tenants]
|
1071
|
+
params['tenants'] = options[:tenants].collect do |val|
|
1072
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
1073
|
+
val.to_i
|
1074
|
+
else
|
1075
|
+
# todo: use /api/options/allTenants to avoid permission errors here..
|
1076
|
+
record = find_by_name_or_id(:account, val)
|
1077
|
+
if record.nil?
|
1078
|
+
exit 1 #return 1, "Tenant not found by '#{val}'"
|
1079
|
+
else
|
1080
|
+
record['id']
|
1081
|
+
end
|
1082
|
+
end
|
1083
|
+
end
|
1084
|
+
end
|
1085
|
+
payload.deep_merge!({object_key => params})
|
1086
|
+
if payload.empty? || payload[object_key].empty?
|
1087
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
1088
|
+
end
|
1089
|
+
# make request
|
1090
|
+
@integrations_interface.setopts(options)
|
1091
|
+
if options[:dry_run]
|
1092
|
+
print_dry_run @integrations_interface.dry.update_inventory(integration['id'], integration_inventory['id'], payload)
|
1093
|
+
return
|
1094
|
+
end
|
1095
|
+
json_response = @integrations_interface.update_inventory(integration['id'], integration_inventory['id'], payload)
|
1096
|
+
integration_inventory = json_response[object_key]
|
1097
|
+
render_response(json_response, options, object_key) do
|
1098
|
+
print_green_success "Updated integration inventory #{integration_inventory['name']}"
|
1099
|
+
# return _get_inventory(integration, integration_inventory["id"], {}, options)
|
1100
|
+
end
|
1101
|
+
return 0, nil
|
1102
|
+
end
|
1103
|
+
|
939
1104
|
private
|
940
1105
|
|
941
1106
|
def format_integration_type(integration)
|
@@ -999,7 +1164,7 @@ EOT
|
|
999
1164
|
return json_response[integration_object_key]
|
1000
1165
|
rescue RestClient::Exception => e
|
1001
1166
|
if e.response && e.response.code == 404
|
1002
|
-
print_red_alert "
|
1167
|
+
print_red_alert "Integration not found by id '#{id}'"
|
1003
1168
|
else
|
1004
1169
|
raise e
|
1005
1170
|
end
|
@@ -1010,7 +1175,7 @@ EOT
|
|
1010
1175
|
json_response = @integrations_interface.list({name: name.to_s})
|
1011
1176
|
integrations = json_response[integration_list_key]
|
1012
1177
|
if integrations.empty?
|
1013
|
-
print_red_alert "
|
1178
|
+
print_red_alert "Integration not found by name '#{name}'"
|
1014
1179
|
return nil
|
1015
1180
|
elsif integrations.size > 1
|
1016
1181
|
print_red_alert "#{integrations.size} integrations found by name '#{name}'"
|
@@ -1075,7 +1240,7 @@ EOT
|
|
1075
1240
|
return json_response[integration_object_key]
|
1076
1241
|
rescue RestClient::Exception => e
|
1077
1242
|
if e.response && e.response.code == 404
|
1078
|
-
print_red_alert "
|
1243
|
+
print_red_alert "Integration not found by id '#{id}'"
|
1079
1244
|
else
|
1080
1245
|
raise e
|
1081
1246
|
end
|
@@ -1086,7 +1251,7 @@ EOT
|
|
1086
1251
|
json_response = @integration_types_interface.list(params.merge({name: name.to_s}))
|
1087
1252
|
integration_types = json_response[integration_type_list_key]
|
1088
1253
|
if integration_types.empty?
|
1089
|
-
print_red_alert "
|
1254
|
+
print_red_alert "Integration type not found by name '#{name}'"
|
1090
1255
|
return nil
|
1091
1256
|
elsif integration_types.size > 1
|
1092
1257
|
print_red_alert "#{integration_types.size} integration types found by name '#{name}'"
|
@@ -1196,4 +1361,50 @@ EOT
|
|
1196
1361
|
end
|
1197
1362
|
end
|
1198
1363
|
|
1364
|
+
def integration_inventory_object_key
|
1365
|
+
'inventory'
|
1366
|
+
end
|
1367
|
+
|
1368
|
+
def integration_inventory_list_key
|
1369
|
+
'inventory'
|
1370
|
+
end
|
1371
|
+
|
1372
|
+
def find_integration_inventory_by_name_or_id(integration_id, val)
|
1373
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
1374
|
+
return find_integration_inventory_by_id(integration_id, val)
|
1375
|
+
else
|
1376
|
+
return find_integration_inventory_by_name(integration_id, val)
|
1377
|
+
end
|
1378
|
+
end
|
1379
|
+
|
1380
|
+
def find_integration_inventory_by_id(integration_id, id)
|
1381
|
+
begin
|
1382
|
+
json_response = @integrations_interface.get_inventory(integration_id, id.to_i)
|
1383
|
+
return json_response[integration_inventory_object_key]
|
1384
|
+
rescue RestClient::Exception => e
|
1385
|
+
if e.response && e.response.code == 404
|
1386
|
+
print_red_alert "Inventory not found by id '#{id}'"
|
1387
|
+
else
|
1388
|
+
raise e
|
1389
|
+
end
|
1390
|
+
end
|
1391
|
+
end
|
1392
|
+
|
1393
|
+
def find_integration_inventory_by_name(integration_id, name)
|
1394
|
+
json_response = @integrations_interface.list_inventory(integration_id, {name: name.to_s})
|
1395
|
+
integration_inventory = json_response[integration_inventory_list_key]
|
1396
|
+
if integration_inventory.empty?
|
1397
|
+
print_red_alert "Inventory not found by name '#{name}'"
|
1398
|
+
return nil
|
1399
|
+
elsif integration_inventory.size > 1
|
1400
|
+
print_red_alert "#{integration_inventory.size} inventory found by name '#{name}'"
|
1401
|
+
puts_error as_pretty_table(integration_inventory, [:id, :name], {color:red})
|
1402
|
+
print_red_alert "Try using ID instead"
|
1403
|
+
print reset,"\n"
|
1404
|
+
return nil
|
1405
|
+
else
|
1406
|
+
return integration_inventory[0]
|
1407
|
+
end
|
1408
|
+
end
|
1409
|
+
|
1199
1410
|
end
|