morpheus-cli 2.11.1 → 2.11.3
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/lib/morpheus/api/api_client.rb +18 -1
- data/lib/morpheus/api/dashboard_interface.rb +1 -1
- data/lib/morpheus/cli/cli_command.rb +14 -0
- data/lib/morpheus/cli/groups.rb +1 -1
- data/lib/morpheus/cli/instance_types.rb +10 -5
- data/lib/morpheus/cli/instances.rb +71 -13
- data/lib/morpheus/cli/load_balancers.rb +80 -16
- data/lib/morpheus/cli/mixins/print_helper.rb +3 -1
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +82 -3
- data/lib/morpheus/cli/recent_activity_command.rb +77 -10
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/rest_client.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d561294737f605bb14714d84f21984797f012b2
|
4
|
+
data.tar.gz: 857aa6f016e8f4db4cfaea280703a7f71adac83a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 941e2fd2a2478c7af97e604f3bbea977874a585b5a4dcd0d526bce39434b4461b9859556f509054239b0fd957919f36b5606304e314f2befda86667d888aec15
|
7
|
+
data.tar.gz: 1fe0c7f8d07df32dd4ea2b771ef2b21106831115d3d3c0a51358e8b9c640be215a3057b12f8f3608524fb46aee02b8f6fa5eef2f60599b6653e1553df6e3cb16
|
@@ -3,13 +3,14 @@ require 'uri'
|
|
3
3
|
require 'rest-client'
|
4
4
|
|
5
5
|
class Morpheus::APIClient
|
6
|
-
def initialize(access_token, refresh_token=nil,expires_in = nil, base_url=nil)
|
6
|
+
def initialize(access_token, refresh_token=nil,expires_in = nil, base_url=nil, verify_ssl=true)
|
7
7
|
@access_token = access_token
|
8
8
|
@refresh_token = refresh_token
|
9
9
|
@base_url = base_url
|
10
10
|
if expires_in != nil
|
11
11
|
@expires_at = DateTime.now + expires_in.seconds
|
12
12
|
end
|
13
|
+
set_ssl_verification_enabled(verify_ssl)
|
13
14
|
end
|
14
15
|
|
15
16
|
def dry_run(val=true)
|
@@ -21,7 +22,23 @@ class Morpheus::APIClient
|
|
21
22
|
dry_run(true)
|
22
23
|
end
|
23
24
|
|
25
|
+
def ssl_verification_enabled?
|
26
|
+
@verify_ssl
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_ssl_verification_enabled(val)
|
30
|
+
@verify_ssl = !!val
|
31
|
+
end
|
32
|
+
|
24
33
|
def execute(opts, parse_json=true)
|
34
|
+
# @verify_ssl is not used atm
|
35
|
+
# todo: finish this and use it instead of the global variable RestClient.ssl_verification_enabled?
|
36
|
+
# gotta clean up all APIClient subclasses new() methods to support this
|
37
|
+
# the CliCommand subclasses should be changed to @users_interface = @api_client.users
|
38
|
+
# also.. Credentials.new()
|
39
|
+
if @verify_ssl == false
|
40
|
+
opts[:verify_ssl] = OpenSSL::SSL::VERIFY_NONE
|
41
|
+
end
|
25
42
|
if @dry_run
|
26
43
|
# JD: could return a Request object instead...
|
27
44
|
return opts
|
@@ -20,7 +20,7 @@ class Morpheus::DashboardInterface < Morpheus::APIClient
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def recent_activity(account_id=nil, options={})
|
23
|
-
url = "#{@base_url}/api/dashboard/
|
23
|
+
url = "#{@base_url}/api/dashboard/recent-activity"
|
24
24
|
headers = { params: {}, authorization: "Bearer #{@access_token}" }
|
25
25
|
headers[:params].merge!(options)
|
26
26
|
headers[:params]['accountId'] = account_id if account_id
|
@@ -547,6 +547,20 @@ module Morpheus
|
|
547
547
|
@appliance_name = appliance[:name]
|
548
548
|
@appliance_url = appliance[:host] || appliance[:url] # it's :host in the YAML..heh
|
549
549
|
|
550
|
+
# instead of toggling this global value
|
551
|
+
# this should just be an attribute of the api client
|
552
|
+
# for now, this fixes the issue where passing --insecure or --remote
|
553
|
+
# would then apply to all subsequent commands...
|
554
|
+
if options[:insecure]
|
555
|
+
Morpheus::RestClient.enable_ssl_verification = false
|
556
|
+
else
|
557
|
+
if appliance[:insecure] && Morpheus::RestClient.ssl_verification_enabled?
|
558
|
+
Morpheus::RestClient.enable_ssl_verification = false
|
559
|
+
elsif !appliance[:insecure] && !Morpheus::RestClient.ssl_verification_enabled?
|
560
|
+
Morpheus::RestClient.enable_ssl_verification = true
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
550
564
|
# todo: support old way of accepting --username and --password on the command line
|
551
565
|
# it's probably better not to do that tho, just so it stays out of history files
|
552
566
|
|
data/lib/morpheus/cli/groups.rb
CHANGED
@@ -277,7 +277,7 @@ class Morpheus::Cli::Groups
|
|
277
277
|
print JSON.pretty_generate(json_response)
|
278
278
|
print "\n"
|
279
279
|
else
|
280
|
-
print_green_success "Added cloud #{cloud["
|
280
|
+
print_green_success "Added cloud #{cloud["name"]} to group #{group['name']}"
|
281
281
|
#list([])
|
282
282
|
get([group["id"]])
|
283
283
|
end
|
@@ -93,13 +93,18 @@ class Morpheus::Cli::InstanceTypes
|
|
93
93
|
|
94
94
|
if options[:json]
|
95
95
|
print JSON.pretty_generate(json_response), "\n"
|
96
|
-
return
|
96
|
+
return 0
|
97
97
|
end
|
98
98
|
|
99
99
|
instance_types = json_response['instanceTypes']
|
100
|
-
|
100
|
+
title = "Morpheus Instance Types"
|
101
|
+
subtitles = []
|
102
|
+
if params[:phrase]
|
103
|
+
subtitles << "Search: #{params[:phrase]}".strip
|
104
|
+
end
|
105
|
+
print_h1 title, subtitles
|
101
106
|
if instance_types.empty?
|
102
|
-
|
107
|
+
print yellow,"No instance types found.",reset,"\n"
|
103
108
|
else
|
104
109
|
instance_types.each do |instance_type|
|
105
110
|
versions = instance_type['versions'].join(', ')
|
@@ -113,12 +118,12 @@ class Morpheus::Cli::InstanceTypes
|
|
113
118
|
# end
|
114
119
|
#print JSON.pretty_generate(instance_type['instanceTypeLayouts'].first), "\n"
|
115
120
|
end
|
116
|
-
|
117
121
|
end
|
118
122
|
print reset,"\n"
|
123
|
+
return 0
|
119
124
|
rescue RestClient::Exception => e
|
120
125
|
print_rest_exception(e, options)
|
121
|
-
|
126
|
+
return 1
|
122
127
|
end
|
123
128
|
end
|
124
129
|
end
|
@@ -171,7 +171,8 @@ class Morpheus::Cli::Instances
|
|
171
171
|
def add(args)
|
172
172
|
options = {}
|
173
173
|
optparse = OptionParser.new do|opts|
|
174
|
-
opts.banner = subcommand_usage("[type] [name]")
|
174
|
+
# opts.banner = subcommand_usage("[type] [name]")
|
175
|
+
opts.banner = subcommand_usage("[name] -c CLOUD -t TYPE")
|
175
176
|
opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
|
176
177
|
options[:group] = val
|
177
178
|
end
|
@@ -187,32 +188,73 @@ class Morpheus::Cli::Instances
|
|
187
188
|
opts.on("--layout-size NUMBER", Integer, "Apply a multiply factor of containers/vms within the instance") do |val|
|
188
189
|
options[:layout_size] = val.to_i
|
189
190
|
end
|
191
|
+
opts.on("--workflow ID", String, "Automation: Workflow ID") do |val|
|
192
|
+
options[:workflow_id] = val.to_i
|
193
|
+
end
|
194
|
+
# opts.on('-L', "--lb", "Enable Load Balancer") do
|
195
|
+
# options[:enable_load_balancer] = true
|
196
|
+
# end
|
197
|
+
opts.on("--shutdown-days NUMBER", Integer, "Automation: Shutdown Days") do |val|
|
198
|
+
options[:expire_days] = val.to_i
|
199
|
+
end
|
200
|
+
opts.on("--expire-days NUMBER", Integer, "Automation: Expiration Days") do |val|
|
201
|
+
options[:expire_days] = val.to_i
|
202
|
+
end
|
203
|
+
opts.on("--create-backup on|off", String, "Automation: Create Backups. Default is off") do |val|
|
204
|
+
options[:create_backup] = ['on','true','1'].include?(val.to_s.downcase) ? 'on' : 'off'
|
205
|
+
end
|
190
206
|
build_common_options(opts, options, [:options, :json, :dry_run, :remote])
|
191
207
|
end
|
192
208
|
|
193
209
|
optparse.parse!(args)
|
194
210
|
connect(options)
|
195
211
|
|
196
|
-
#
|
212
|
+
# this is the old format of `instance add TYPE NAME`
|
213
|
+
# JD: it seems confusing, let's deprecate and go with `instances add [NAME] -t TYPE`
|
197
214
|
if args[0]
|
198
215
|
options[:instance_type_code] = args[0]
|
199
216
|
end
|
200
217
|
if args[1]
|
201
218
|
options[:instance_name] = args[1]
|
202
219
|
end
|
220
|
+
|
221
|
+
# if args.count > 1
|
222
|
+
# print_error Morpheus::Terminal.angry_prompt
|
223
|
+
# puts_error "#{command_name} add has just 1 (optional) argument: NAME. Got #{args.count} arguments: #{args.join(', ')}\n#{optparse}"
|
224
|
+
# return 1
|
225
|
+
# end
|
226
|
+
# if args[0]
|
227
|
+
# options[:instance_name] = args[0]
|
228
|
+
# end
|
229
|
+
|
203
230
|
# use active group by default
|
204
231
|
options[:group] ||= @active_group_id
|
205
232
|
|
206
233
|
options[:name_required] = true
|
207
234
|
begin
|
208
|
-
|
235
|
+
# this provisioning helper method handles all (most) of the parsing and prompting
|
236
|
+
# and it relies on the method to exit non-zero on error, like a bad CLOUD or TYPE value
|
209
237
|
payload = prompt_new_instance(options)
|
238
|
+
|
239
|
+
# other stuff
|
210
240
|
payload[:copies] = options[:copies] if options[:copies] && options[:copies] > 0
|
211
241
|
payload[:layoutSize] = options[:layout_size] if options[:layout_size] && options[:layout_size] > 0 # aka Scale Factor
|
242
|
+
payload[:createBackup] = options[:create_backup] ? 'on' : 'off' if options[:create_backup] == true
|
243
|
+
payload['instance']['expireDays'] = options[:expire_days] if options[:expire_days]
|
244
|
+
payload['instance']['shutdownDays'] = options[:shutdown_days] if options[:shutdown_days]
|
245
|
+
if options[:workflow_id]
|
246
|
+
payload['taskSetId'] = options[:workflow_id]
|
247
|
+
end
|
248
|
+
if options[:enable_load_balancer]
|
249
|
+
lb_payload = prompt_instance_load_balancer(payload['instance'], nil, options)
|
250
|
+
payload.deep_merge!(lb_payload)
|
251
|
+
end
|
252
|
+
|
212
253
|
if options[:dry_run]
|
213
254
|
print_dry_run @instances_interface.dry.create(payload)
|
214
|
-
return
|
255
|
+
return 0
|
215
256
|
end
|
257
|
+
|
216
258
|
json_response = @instances_interface.create(payload)
|
217
259
|
if options[:json]
|
218
260
|
puts as_json(json_response, options)
|
@@ -221,9 +263,10 @@ class Morpheus::Cli::Instances
|
|
221
263
|
print_green_success "Provisioning instance #{instance_name}"
|
222
264
|
#list([])
|
223
265
|
end
|
266
|
+
return 0
|
224
267
|
rescue RestClient::Exception => e
|
225
268
|
print_rest_exception(e, options)
|
226
|
-
|
269
|
+
return 1
|
227
270
|
end
|
228
271
|
end
|
229
272
|
|
@@ -598,6 +641,20 @@ class Morpheus::Cli::Instances
|
|
598
641
|
}
|
599
642
|
print_description_list(description_cols, instance)
|
600
643
|
|
644
|
+
if instance['statusMessage']
|
645
|
+
print_h2 "Status Message"
|
646
|
+
if instance['status'] == 'failed'
|
647
|
+
print red, instance['statusMessage'], reset
|
648
|
+
else
|
649
|
+
print instance['statusMessage']
|
650
|
+
end
|
651
|
+
print "\n"
|
652
|
+
end
|
653
|
+
if instance['errorMessage']
|
654
|
+
print_h2 "Error Message"
|
655
|
+
print red, instance['errorMessage'], reset
|
656
|
+
print "\n"
|
657
|
+
end
|
601
658
|
if stats
|
602
659
|
print_h2 "Instance Usage"
|
603
660
|
print_stats_usage(stats)
|
@@ -650,15 +707,16 @@ class Morpheus::Cli::Instances
|
|
650
707
|
if current_instance_lb
|
651
708
|
print_h2 "Load Balancer"
|
652
709
|
print cyan
|
710
|
+
# this api response is going to change again.. port is no longer returned atm.
|
653
711
|
description_cols = {
|
654
712
|
"LB ID" => lambda {|it| it['loadBalancer']['id'] },
|
655
713
|
"Name" => lambda {|it| it['loadBalancer']['name'] },
|
656
714
|
"Type" => lambda {|it| it['loadBalancer']['type'] ? it['loadBalancer']['type']['name'] : '' },
|
657
715
|
"Host Name" => lambda {|it| it['loadBalancer']['host'] }, # instance.hostName ?
|
658
|
-
"Port" => lambda {|it| it['port']['port'] },
|
659
|
-
"Protocol" => lambda {|it| it['port']['proxyProtocol'] },
|
660
|
-
"SSL Enabled" => lambda {|it| format_boolean
|
661
|
-
"Cert" => lambda {|it| it['port']['sslCert'] ? it['port']['sslCert']['name'] : '
|
716
|
+
"Port" => lambda {|it| it['port'] ? it['port']['port'] : '' },
|
717
|
+
"Protocol" => lambda {|it| it['port'] ? it['port']['proxyProtocol'] : '' },
|
718
|
+
"SSL Enabled" => lambda {|it| it['port'] ? format_boolean(it['port']['sslEnabled']) : '' },
|
719
|
+
"Cert" => lambda {|it| (it['port'] && it['port']['sslCert']) ? it['port']['sslCert']['name'] : '' }
|
662
720
|
}
|
663
721
|
print_description_list(description_cols, current_instance_lb)
|
664
722
|
print "\n", reset
|
@@ -2054,20 +2112,20 @@ class Morpheus::Cli::Instances
|
|
2054
2112
|
}
|
2055
2113
|
|
2056
2114
|
cur_host_name = instance['hostName']
|
2057
|
-
#host_name = params = Morpheus::Cli::OptionTypes.prompt([{fieldName
|
2115
|
+
#host_name = params = Morpheus::Cli::OptionTypes.prompt([{'fieldName'=>'hostName', 'label'=>'Host Name', 'defaultValue'=>cur_host_name}], options[:options], @api_client, {})
|
2058
2116
|
payload['instance']['hostName'] = instance['hostName']
|
2059
2117
|
|
2060
|
-
payload['loadBalancerId'] =
|
2118
|
+
#payload['loadBalancerId'] = params['loadBalancerId']
|
2061
2119
|
|
2062
2120
|
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to update the load balancer for instance '#{instance['name']}'?", options)
|
2063
2121
|
return 9, "aborted command"
|
2064
2122
|
end
|
2065
2123
|
|
2066
2124
|
if options[:dry_run]
|
2067
|
-
print_dry_run @instances_interface.dry.
|
2125
|
+
print_dry_run @instances_interface.dry.update_load_balancer(instance['id'], payload)
|
2068
2126
|
return
|
2069
2127
|
end
|
2070
|
-
json_response = @instances_interface.
|
2128
|
+
json_response = @instances_interface.update_load_balancer(instance['id'], payload)
|
2071
2129
|
if options[:json]
|
2072
2130
|
puts as_json(json_response, options)
|
2073
2131
|
else
|
@@ -29,7 +29,7 @@ class Morpheus::Cli::LoadBalancers
|
|
29
29
|
options = {}
|
30
30
|
optparse = OptionParser.new do|opts|
|
31
31
|
opts.banner = subcommand_usage()
|
32
|
-
build_common_options(opts, options, [:list, :json, :dry_run, :remote])
|
32
|
+
build_common_options(opts, options, [:list, :json, :csv, :yaml, :fields, :dry_run, :remote])
|
33
33
|
end
|
34
34
|
optparse.parse!(args)
|
35
35
|
connect(options)
|
@@ -44,24 +44,41 @@ class Morpheus::Cli::LoadBalancers
|
|
44
44
|
end
|
45
45
|
json_response = @load_balancers_interface.get(params)
|
46
46
|
if options[:json]
|
47
|
-
|
47
|
+
if options[:include_fields]
|
48
|
+
json_response = {"loadBalancers" => filter_data(json_response["loadBalancers"], options[:include_fields]) }
|
49
|
+
end
|
50
|
+
puts as_json(json_response, options)
|
51
|
+
return 0
|
52
|
+
elsif options[:csv]
|
53
|
+
puts records_as_csv(json_response["loadBalancers"], options)
|
54
|
+
return 0
|
55
|
+
elsif options[:yaml]
|
56
|
+
if options[:include_fields]
|
57
|
+
json_response = {"loadBalancers" => filter_data(json_response["loadBalancers"], options[:include_fields]) }
|
58
|
+
end
|
59
|
+
puts as_yaml(json_response, options)
|
60
|
+
return 0
|
48
61
|
else
|
49
62
|
lbs = json_response['loadBalancers']
|
50
63
|
print_h1 "Morpheus Load Balancers"
|
51
64
|
if lbs.empty?
|
52
|
-
print
|
65
|
+
print yellow,"No load balancers found.",reset,"\n"
|
53
66
|
else
|
54
|
-
|
55
|
-
|
56
|
-
{
|
57
|
-
|
58
|
-
|
67
|
+
columns = [
|
68
|
+
{"ID" => 'id'},
|
69
|
+
{"Name" => 'name'},
|
70
|
+
{"Type" => lambda {|it| it['type'] ? it['type']['name'] : '' } },
|
71
|
+
{"Cloud" => lambda {|it| it['cloud'] ? it['cloud']['name'] : '' } },
|
72
|
+
{"Host" => lambda {|it| it['host'] } },
|
73
|
+
]
|
74
|
+
print as_pretty_table(lbs, columns, options)
|
59
75
|
end
|
60
76
|
print reset,"\n"
|
77
|
+
return 0
|
61
78
|
end
|
62
|
-
|
79
|
+
rescue RestClient::Exception => e
|
63
80
|
print_rest_exception(e, options)
|
64
|
-
|
81
|
+
return 1
|
65
82
|
end
|
66
83
|
end
|
67
84
|
|
@@ -69,7 +86,7 @@ class Morpheus::Cli::LoadBalancers
|
|
69
86
|
options = {}
|
70
87
|
optparse = OptionParser.new do|opts|
|
71
88
|
opts.banner = subcommand_usage("[name]")
|
72
|
-
build_common_options(opts, options, [:json, :dry_run, :remote])
|
89
|
+
build_common_options(opts, options, [:json, :csv, :yaml, :fields, :dry_run, :remote])
|
73
90
|
end
|
74
91
|
optparse.parse!(args)
|
75
92
|
if args.count < 1
|
@@ -89,15 +106,62 @@ class Morpheus::Cli::LoadBalancers
|
|
89
106
|
end
|
90
107
|
lb = find_lb_by_name_or_id(lb_name)
|
91
108
|
exit 1 if lb.nil?
|
109
|
+
# refetch
|
110
|
+
json_response = @load_balancers_interface.get(lb['id'])
|
92
111
|
lb_type = load_balancer_type_for_name_or_id(lb['type']['code'])
|
112
|
+
#puts "LB TYPE: #{lb_type}"
|
93
113
|
if options[:json]
|
94
114
|
puts JSON.pretty_generate({loadBalancer: lb})
|
115
|
+
if options[:include_fields]
|
116
|
+
json_response = {"loadBalancer" => filter_data(json_response["loadBalancer"], options[:include_fields]) }
|
117
|
+
end
|
118
|
+
puts as_json(json_response, options)
|
119
|
+
return 0
|
120
|
+
elsif options[:csv]
|
121
|
+
puts records_as_csv(json_response["loadBalancer"], options)
|
122
|
+
return 0
|
123
|
+
elsif options[:yaml]
|
124
|
+
if options[:include_fields]
|
125
|
+
json_response = {"loadBalancer" => filter_data(json_response["loadBalancer"], options[:include_fields]) }
|
126
|
+
end
|
127
|
+
puts as_yaml(json_response, options)
|
128
|
+
return 0
|
95
129
|
else
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
130
|
+
print_h1 "Load Balancer Details"
|
131
|
+
description_cols = {
|
132
|
+
"ID" => 'id',
|
133
|
+
"Name" => 'name',
|
134
|
+
"Description" => 'description',
|
135
|
+
"Type" => lambda {|it| it['type'] ? it['type']['name'] : '' },
|
136
|
+
"Cloud" => lambda {|it| it['cloud'] ? it['cloud']['name'] : '' },
|
137
|
+
"Visibility" => 'visibility',
|
138
|
+
"IP" => 'ip',
|
139
|
+
"Host" => 'host',
|
140
|
+
"Port" => 'port',
|
141
|
+
"Username" => 'username',
|
142
|
+
# "SSL Enabled" => lambda {|it| format_boolean it['sslEnabled'] },
|
143
|
+
# "SSL Cert" => lambda {|it| it['sslCert'] ? it['sslCert']['name'] : '' },
|
144
|
+
# "SSL" => lambda {|it| it['sslEnabled'] ? "Yes (#{it['sslCert'] ? it['sslCert']['name'] : 'none'})" : "No" },
|
145
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
146
|
+
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
147
|
+
}
|
148
|
+
print_description_list(description_cols, lb)
|
149
|
+
|
150
|
+
|
151
|
+
if lb['ports'] && lb['ports'].size > 0
|
152
|
+
print_h2 "LB Ports"
|
153
|
+
columns = [
|
154
|
+
{"ID" => 'id'},
|
155
|
+
{"Name" => 'name'},
|
156
|
+
#{"Description" => 'description'},
|
157
|
+
{"Port" => lambda {|it| it['port'] } },
|
158
|
+
{"Protocol" => lambda {|it| it['proxyProtocol'] } },
|
159
|
+
{"SSL" => lambda {|it| it['sslEnabled'] ? "Yes (#{it['sslCert'] ? it['sslCert']['name'] : 'none'})" : "No" } },
|
160
|
+
]
|
161
|
+
print as_pretty_table(lb['ports'], columns, options)
|
162
|
+
end
|
163
|
+
print reset,"\n"
|
164
|
+
return 0
|
101
165
|
end
|
102
166
|
rescue RestClient::Exception => e
|
103
167
|
print_rest_exception(e, options)
|
@@ -482,7 +482,7 @@ module Morpheus::Cli::PrintHelper
|
|
482
482
|
max_label_width = 0
|
483
483
|
justify = opts.key?(:justify) ? opts[:justify] : "right"
|
484
484
|
do_wrap = opts.key?(:wrap) ? !!opts[:wrap] : true
|
485
|
-
|
485
|
+
color = opts.key?(:color) ? opts[:color] : cyan
|
486
486
|
rows = []
|
487
487
|
|
488
488
|
columns.flatten.each do |column_def|
|
@@ -501,6 +501,7 @@ module Morpheus::Cli::PrintHelper
|
|
501
501
|
end
|
502
502
|
|
503
503
|
out = ""
|
504
|
+
out << color if color
|
504
505
|
rows.each do |row|
|
505
506
|
value = row[:value].to_s
|
506
507
|
if do_wrap
|
@@ -511,6 +512,7 @@ module Morpheus::Cli::PrintHelper
|
|
511
512
|
end
|
512
513
|
out << format_dt_dd(row[:label], value, label_width, justify) + "\n"
|
513
514
|
end
|
515
|
+
out << reset if color
|
514
516
|
return out
|
515
517
|
end
|
516
518
|
|
@@ -324,6 +324,12 @@ module Morpheus::Cli::ProvisioningHelper
|
|
324
324
|
payload['evars'] = evars
|
325
325
|
end
|
326
326
|
|
327
|
+
# prompt for metadata variables
|
328
|
+
metadata = prompt_metadata(options)
|
329
|
+
if !metadata.empty?
|
330
|
+
payload['metadata'] = metadata
|
331
|
+
end
|
332
|
+
|
327
333
|
return payload
|
328
334
|
end
|
329
335
|
|
@@ -806,6 +812,10 @@ module Morpheus::Cli::ProvisioningHelper
|
|
806
812
|
# puts JSON.pretty_generate(zone_network_options_json)
|
807
813
|
zone_network_data = zone_network_options_json['data'] || {}
|
808
814
|
networks = zone_network_data['networks']
|
815
|
+
network_groups = zone_network_data['networkGroups']
|
816
|
+
if network_groups
|
817
|
+
networks = network_groups + networks
|
818
|
+
end
|
809
819
|
network_interface_types = (zone_network_data['networkTypes'] || []).sort { |x,y| x['displayOrder'] <=> y['displayOrder'] }
|
810
820
|
enable_network_type_selection = (zone_network_data['enableNetworkTypeSelection'] == 'on' || zone_network_data['enableNetworkTypeSelection'] == true)
|
811
821
|
has_networks = zone_network_data["hasNetworks"] == true
|
@@ -853,8 +863,8 @@ module Morpheus::Cli::ProvisioningHelper
|
|
853
863
|
# choose network
|
854
864
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'networkId', 'type' => 'select', 'fieldLabel' => "Network", 'selectOptions' => network_options, 'required' => true, 'skipSingleOption' => false, 'description' => 'Choose a network for this interface.', 'defaultValue' => network_interface['networkId']}], options[:options])
|
855
865
|
network_interface['network'] = {}
|
856
|
-
network_interface['network']['id'] = v_prompt[field_context]['networkId'].
|
857
|
-
selected_network = networks.find {|it| it["id"] == network_interface['network']['id'] }
|
866
|
+
network_interface['network']['id'] = v_prompt[field_context]['networkId'].to_s
|
867
|
+
selected_network = networks.find {|it| it["id"].to_s == network_interface['network']['id'] }
|
858
868
|
|
859
869
|
if !selected_network
|
860
870
|
print_red_alert "Network not found by id #{network_interface['network']['id']}!"
|
@@ -868,7 +878,9 @@ module Morpheus::Cli::ProvisioningHelper
|
|
868
878
|
end
|
869
879
|
|
870
880
|
# choose IP unless network has a pool configured
|
871
|
-
if selected_network['
|
881
|
+
if selected_network['id'].to_s.include?('networkGroup')
|
882
|
+
puts "IP Address: Using network group." if !no_prompt
|
883
|
+
elsif selected_network['pool']
|
872
884
|
puts "IP Address: Using pool '#{selected_network['pool']['name']}'" if !no_prompt
|
873
885
|
elsif selected_network['dhcpServer']
|
874
886
|
puts "IP Address: Using DHCP" if !no_prompt
|
@@ -917,6 +929,73 @@ module Morpheus::Cli::ProvisioningHelper
|
|
917
929
|
return evars
|
918
930
|
end
|
919
931
|
|
932
|
+
# Prompts user for environment variables for new instance
|
933
|
+
# returns array of metadata objects {id: null, name: "MYTAG", value: "myvalue"}
|
934
|
+
def prompt_metadata(options={})
|
935
|
+
#puts "Configure Environment Variables:"
|
936
|
+
no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
|
937
|
+
metadata_array = []
|
938
|
+
metadata_index = 0
|
939
|
+
has_another_metadata = options[:options] && options[:options]["metadata#{metadata_index}"]
|
940
|
+
add_another_metadata = has_another_metadata || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add a metadata tag?", {default: false}))
|
941
|
+
while add_another_metadata do
|
942
|
+
field_context = "metadata#{metadata_index}"
|
943
|
+
metadata = {}
|
944
|
+
metadata['id'] = nil
|
945
|
+
metadata_label = metadata_index == 0 ? "Metadata Tag" : "Metadata Tag [#{metadata_index+1}]"
|
946
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "#{metadata_label} Name", 'required' => true, 'description' => 'Metadata Tag Name.', 'defaultValue' => metadata['name']}], options[:options])
|
947
|
+
# todo: metadata.type ?
|
948
|
+
metadata['name'] = v_prompt[field_context]['name']
|
949
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'value', 'type' => 'text', 'fieldLabel' => "#{metadata_label} Value", 'required' => true, 'description' => 'Metadata Tag Value', 'defaultValue' => metadata['value']}], options[:options])
|
950
|
+
metadata['value'] = v_prompt[field_context]['value']
|
951
|
+
metadata_array << metadata
|
952
|
+
metadata_index += 1
|
953
|
+
has_another_metadata = options[:options] && options[:options]["metadata#{metadata_index}"]
|
954
|
+
add_another_metadata = has_another_metadata || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add another metadata tag?", {default: false}))
|
955
|
+
end
|
956
|
+
|
957
|
+
return metadata_array
|
958
|
+
end
|
959
|
+
|
960
|
+
# Prompts user for load balancer settings
|
961
|
+
# returns Hash of parameters like {loadBalancerId: "-1", etc}
|
962
|
+
def prompt_instance_load_balancer(instance, default_lb_id, options)
|
963
|
+
#puts "Configure Environment Variables:"
|
964
|
+
no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
|
965
|
+
payload = {}
|
966
|
+
api_params = {}
|
967
|
+
if instance['id']
|
968
|
+
api_params['instanceId'] = instance['id']
|
969
|
+
end
|
970
|
+
if instance['zone']
|
971
|
+
api_params['zoneId'] = instance['zone']['id']
|
972
|
+
elsif instance['cloud']
|
973
|
+
api_params['zoneId'] = instance['cloud']['id']
|
974
|
+
end
|
975
|
+
if instance['group']
|
976
|
+
api_params['siteId'] = instance['group']['id']
|
977
|
+
elsif instance['site']
|
978
|
+
api_params['siteId'] = instance['site']['id']
|
979
|
+
end
|
980
|
+
if instance['plan']
|
981
|
+
api_params['planId'] = instance['plan']['id']
|
982
|
+
end
|
983
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'loadBalancerId', 'type' => 'select', 'fieldLabel' => "Load Balancer", 'optionSource' => 'loadBalancers', 'required' => true, 'description' => 'Select Load Balancer for instance', 'defaultValue' => default_lb_id || ''}], options[:options], api_client, api_params)
|
984
|
+
lb_id = v_prompt['loadBalancerId']
|
985
|
+
payload['loadBalancerId'] = lb_id
|
986
|
+
|
987
|
+
# todo: finish implmenting this
|
988
|
+
|
989
|
+
# loadBalancerId
|
990
|
+
# loadBalancerProxyProtocol
|
991
|
+
# loadBalancerName
|
992
|
+
# loadBalancerDescription
|
993
|
+
# loadBalancerSslCert
|
994
|
+
# loadBalancerScheme
|
995
|
+
|
996
|
+
return payload
|
997
|
+
end
|
998
|
+
|
920
999
|
# reject old volume option types
|
921
1000
|
# these will eventually get removed from the associated optionTypes
|
922
1001
|
def reject_volume_option_types(option_types)
|
@@ -8,7 +8,6 @@ class Morpheus::Cli::RecentActivityCommand
|
|
8
8
|
include Morpheus::Cli::AccountsHelper
|
9
9
|
|
10
10
|
set_command_name :'recent-activity'
|
11
|
-
set_command_hidden # remove once this is done
|
12
11
|
|
13
12
|
def initialize()
|
14
13
|
# @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
@@ -37,7 +36,7 @@ class Morpheus::Cli::RecentActivityCommand
|
|
37
36
|
opts.on('--end TIMESTAMP','--end TIMESTAMP', "End timestamp. Default is now.") do |val|
|
38
37
|
options[:end] = parse_time(val).iso8601
|
39
38
|
end
|
40
|
-
build_common_options(opts, options, [:account, :list, :json, :dry_run])
|
39
|
+
build_common_options(opts, options, [:account, :list, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
41
40
|
end
|
42
41
|
optparse.parse!(args)
|
43
42
|
connect(options)
|
@@ -54,22 +53,90 @@ class Morpheus::Cli::RecentActivityCommand
|
|
54
53
|
end
|
55
54
|
json_response = @dashboard_interface.recent_activity(account_id, params)
|
56
55
|
if options[:json]
|
56
|
+
if options[:include_fields]
|
57
|
+
json_response = {"activity" => filter_data(json_response["activity"], options[:include_fields]) }
|
58
|
+
end
|
57
59
|
print JSON.pretty_generate(json_response)
|
58
60
|
print "\n"
|
59
|
-
|
60
|
-
|
61
|
-
|
61
|
+
return 0
|
62
|
+
end
|
63
|
+
if options[:csv]
|
64
|
+
puts records_as_csv(json_response["activity"], options)
|
65
|
+
return 0
|
66
|
+
end
|
67
|
+
if options[:yaml]
|
68
|
+
if options[:include_fields]
|
69
|
+
json_response = {"activity" => filter_data(json_response["activity"], options[:include_fields]) }
|
70
|
+
end
|
71
|
+
puts as_yaml(json_response, options)
|
72
|
+
return 0
|
73
|
+
end
|
62
74
|
|
63
|
-
|
64
|
-
|
65
|
-
|
75
|
+
print_h1 "Recent Activity"
|
76
|
+
print cyan
|
77
|
+
items = json_response["activity"]
|
78
|
+
if items.empty?
|
79
|
+
puts "No activity found."
|
66
80
|
print reset,"\n"
|
67
|
-
|
81
|
+
return 0
|
68
82
|
end
|
83
|
+
# JD: this api response is funky, no meta and it includes date objects
|
84
|
+
items = items.select { |item| item['_id'] || item['name'] }
|
85
|
+
print_recent_activity_table(items, options)
|
86
|
+
print reset,"\n"
|
87
|
+
return 0
|
88
|
+
|
69
89
|
rescue RestClient::Exception => e
|
70
90
|
print_rest_exception(e, options)
|
71
|
-
|
91
|
+
return 1
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def print_recent_activity_table(items, opts={})
|
96
|
+
columns = [
|
97
|
+
# {"ID" => lambda {|item| item['id'] } },
|
98
|
+
# {"SEVERITY" => lambda {|item| format_activity_severity(item['severity']) } },
|
99
|
+
{"TYPE" => lambda {|item| item['activityType'] } },
|
100
|
+
{"AUTHOR" => lambda {|item| item['userName'] || '' } },
|
101
|
+
{"MESSAGE" => lambda {|item| item['message'] || '' } },
|
102
|
+
# {"NAME" => lambda {|item| item['name'] } },
|
103
|
+
{"OBJECT" => lambda {|item| format_activity_display_object(item) } },
|
104
|
+
{"WHEN" => lambda {|item| format_local_dt(item['ts']) } }
|
105
|
+
# {"WHEN" => lambda {|item| "#{format_duration(item['ts'])} ago" } }
|
106
|
+
]
|
107
|
+
if opts[:include_fields]
|
108
|
+
columns = opts[:include_fields]
|
109
|
+
end
|
110
|
+
print as_pretty_table(items, columns, opts)
|
111
|
+
end
|
112
|
+
|
113
|
+
def format_activity_severity(severity, return_color=cyan)
|
114
|
+
out = ""
|
115
|
+
status_string = severity
|
116
|
+
if status_string == 'critical'
|
117
|
+
out << "#{red}#{status_string.capitalize}#{return_color}"
|
118
|
+
elsif status_string == 'warning'
|
119
|
+
out << "#{yellow}#{status_string.capitalize}#{return_color}"
|
120
|
+
elsif status_string == 'info'
|
121
|
+
out << "#{cyan}#{status_string.capitalize}#{return_color}"
|
122
|
+
else
|
123
|
+
out << "#{cyan}#{status_string}#{return_color}"
|
124
|
+
end
|
125
|
+
out
|
126
|
+
end
|
127
|
+
|
128
|
+
def format_activity_display_object(item)
|
129
|
+
out = ""
|
130
|
+
if item['name']
|
131
|
+
out << item['name']
|
132
|
+
end
|
133
|
+
if item['objectType']
|
134
|
+
out << " (#{item['objectType']} #{item['objectId']})"
|
135
|
+
end
|
136
|
+
if item['deleted']
|
137
|
+
out << " [deleted]"
|
72
138
|
end
|
139
|
+
out
|
73
140
|
end
|
74
141
|
|
75
142
|
end
|
data/lib/morpheus/cli/version.rb
CHANGED
data/lib/morpheus/rest_client.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: morpheus-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.11.
|
4
|
+
version: 2.11.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Estes
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2017-
|
14
|
+
date: 2017-06-26 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|