morpheus-cli 3.2.0.1 → 3.3.1
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 +4 -0
- data/lib/morpheus/api/instances_interface.rb +10 -10
- data/lib/morpheus/api/power_scheduling_interface.rb +78 -0
- data/lib/morpheus/cli.rb +1 -0
- data/lib/morpheus/cli/app_templates.rb +55 -41
- data/lib/morpheus/cli/clouds.rb +8 -1
- data/lib/morpheus/cli/instances.rb +55 -18
- data/lib/morpheus/cli/key_pairs.rb +36 -12
- data/lib/morpheus/cli/power_scheduling_command.rb +815 -0
- data/lib/morpheus/cli/version.rb +1 -1
- metadata +5 -3
@@ -108,15 +108,29 @@ class Morpheus::Cli::KeyPairs
|
|
108
108
|
print_description_list({
|
109
109
|
"ID" => 'id',
|
110
110
|
"Name" => 'name',
|
111
|
+
"Fingerprint" => 'fingerprint',
|
111
112
|
"MD5" => 'md5',
|
113
|
+
# "Has Private Key" => lambda {|it| format_boolean(it['hasPrivateKey']) },
|
112
114
|
# "Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
113
115
|
"Created" => lambda {|it| format_local_dt(it['dateCreated']) }
|
114
116
|
#"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
115
117
|
}, key_pair)
|
116
|
-
print reset,"\n"
|
117
118
|
|
118
|
-
|
119
|
+
print_h2 "Public Key"
|
120
|
+
print cyan
|
121
|
+
puts "#{key_pair['publicKey']}"
|
122
|
+
|
123
|
+
if key_pair['hasPrivateKey']
|
124
|
+
# print_h2 "Private Key"
|
125
|
+
# print cyan
|
126
|
+
# puts "(hidden)"
|
127
|
+
else
|
128
|
+
# print_h2 "Private Key"
|
129
|
+
print cyan, "\n"
|
130
|
+
puts "This is only a public key. It does not include a private key."
|
131
|
+
end
|
119
132
|
|
133
|
+
print reset,"\n"
|
120
134
|
end
|
121
135
|
rescue RestClient::Exception => e
|
122
136
|
print_rest_exception(e, options)
|
@@ -180,16 +194,20 @@ class Morpheus::Cli::KeyPairs
|
|
180
194
|
|
181
195
|
params = Morpheus::Cli::OptionTypes.prompt(add_key_pair_option_types, options[:options], @api_client, options[:params])
|
182
196
|
|
183
|
-
if
|
184
|
-
print_red_alert "publicKey is required"
|
185
|
-
|
186
|
-
elsif !params['privateKey']
|
187
|
-
print_red_alert "privateKey is required"
|
188
|
-
exit 1
|
197
|
+
if params['publicKey'].to_s.empty? && params['privateKey'].to_s.empty?
|
198
|
+
print_red_alert "publicKey or privateKey is required"
|
199
|
+
return 1
|
189
200
|
end
|
201
|
+
# if !params['publicKey']
|
202
|
+
# print_red_alert "publicKey is required"
|
203
|
+
# exit 1
|
204
|
+
# elsif !params['privateKey']
|
205
|
+
# print_red_alert "privateKey is required"
|
206
|
+
# exit 1
|
207
|
+
# end
|
190
208
|
#params['name'] = args[0]
|
191
209
|
|
192
|
-
key_pair_payload = params.select {|k,v| ['name','publicKey', 'privateKey'].include?(k) }
|
210
|
+
key_pair_payload = params.select {|k,v| ['name','publicKey', 'privateKey', 'passphrase'].include?(k) }
|
193
211
|
payload = {keyPair: key_pair_payload}
|
194
212
|
if options[:dry_run]
|
195
213
|
print_dry_run @key_pairs_interface.dry.create(account_id, payload)
|
@@ -201,6 +219,7 @@ class Morpheus::Cli::KeyPairs
|
|
201
219
|
print "\n"
|
202
220
|
else
|
203
221
|
print_green_success "Key Pair #{key_pair_payload['name']} added"
|
222
|
+
get([json_response['keyPair']['id']])
|
204
223
|
end
|
205
224
|
rescue RestClient::Exception => e
|
206
225
|
print_rest_exception(e, options)
|
@@ -212,6 +231,7 @@ class Morpheus::Cli::KeyPairs
|
|
212
231
|
options = {}
|
213
232
|
optparse = OptionParser.new do|opts|
|
214
233
|
opts.banner = subcommand_usage("[name] [options]")
|
234
|
+
build_option_type_options(opts, options, update_key_pair_option_types)
|
215
235
|
build_common_options(opts, options, [:account, :options, :json, :dry_run])
|
216
236
|
end
|
217
237
|
optparse.parse!(args)
|
@@ -252,6 +272,7 @@ class Morpheus::Cli::KeyPairs
|
|
252
272
|
print "\n"
|
253
273
|
else
|
254
274
|
print_green_success "Key Pair #{key_pair_payload['name'] || key_pair['name']} updated"
|
275
|
+
get([json_response['keyPair']['id']])
|
255
276
|
end
|
256
277
|
rescue RestClient::Exception => e
|
257
278
|
print_rest_exception(e, options)
|
@@ -347,6 +368,7 @@ class Morpheus::Cli::KeyPairs
|
|
347
368
|
{
|
348
369
|
id: key_pair['id'],
|
349
370
|
name: key_pair['name'],
|
371
|
+
fingerprint: key_pair['fingerprint'],
|
350
372
|
md5: key_pair['md5'],
|
351
373
|
dateCreated: format_local_dt(key_pair['dateCreated'])
|
352
374
|
}
|
@@ -355,7 +377,8 @@ class Morpheus::Cli::KeyPairs
|
|
355
377
|
tp rows, [
|
356
378
|
:id,
|
357
379
|
:name,
|
358
|
-
:
|
380
|
+
{:fingerprint => {:width => 47} },
|
381
|
+
# {:md5 => {:width => 32} },
|
359
382
|
{:dateCreated => {:display_name => "Date Created"} }
|
360
383
|
]
|
361
384
|
print reset
|
@@ -365,8 +388,9 @@ class Morpheus::Cli::KeyPairs
|
|
365
388
|
def add_key_pair_option_types
|
366
389
|
[
|
367
390
|
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
|
368
|
-
{'fieldName' => 'publicKey', 'fieldLabel' => 'Public Key', 'type' => 'text', 'required' =>
|
369
|
-
{'fieldName' => 'privateKey', 'fieldLabel' => 'Private Key', 'type' => 'text', 'required' =>
|
391
|
+
{'fieldName' => 'publicKey', 'fieldLabel' => 'Public Key', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
|
392
|
+
{'fieldName' => 'privateKey', 'fieldLabel' => 'Private Key', 'type' => 'text', 'required' => false, 'displayOrder' => 3},
|
393
|
+
{'fieldName' => 'passphrase', 'fieldLabel' => 'Passphrase', 'type' => 'password', 'required' => false, 'displayOrder' => 4},
|
370
394
|
]
|
371
395
|
end
|
372
396
|
|
@@ -0,0 +1,815 @@
|
|
1
|
+
require 'morpheus/cli/cli_command'
|
2
|
+
|
3
|
+
class Morpheus::Cli::PowerSchedulingCommand
|
4
|
+
include Morpheus::Cli::CliCommand
|
5
|
+
# include Morpheus::Cli::ProvisioningHelper
|
6
|
+
|
7
|
+
# this is the only type of schedule right now
|
8
|
+
#set_command_name :'schedules'
|
9
|
+
#set_command_name :'scheduling'
|
10
|
+
set_command_name :'power-scheduling'
|
11
|
+
|
12
|
+
register_subcommands :list, :get, :add, :update, :remove
|
13
|
+
register_subcommands :'add-instances' => :add_instances
|
14
|
+
register_subcommands :'remove-instances' => :remove_instances
|
15
|
+
register_subcommands :'add-hosts' => :add_hosts
|
16
|
+
register_subcommands :'remove-hosts' => :remove_hosts
|
17
|
+
|
18
|
+
def connect(opts)
|
19
|
+
@api_client = establish_remote_appliance_connection(opts)
|
20
|
+
@power_scheduling_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).power_scheduling
|
21
|
+
@instances_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instances
|
22
|
+
@servers_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).servers
|
23
|
+
end
|
24
|
+
|
25
|
+
def handle(args)
|
26
|
+
handle_subcommand(args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def list(args)
|
30
|
+
options = {}
|
31
|
+
params = {}
|
32
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
33
|
+
opts.banner = subcommand_usage()
|
34
|
+
build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
35
|
+
end
|
36
|
+
optparse.parse!(args)
|
37
|
+
connect(options)
|
38
|
+
begin
|
39
|
+
[:phrase, :offset, :max, :sort, :direction, :lastUpdated].each do |k|
|
40
|
+
params[k] = options[k] unless options[k].nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
if options[:dry_run]
|
44
|
+
print_dry_run @power_scheduling_interface.dry.list(params)
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
json_response = @power_scheduling_interface.list(params)
|
49
|
+
if options[:include_fields]
|
50
|
+
json_response = {"powerScheduleTypes" => filter_data(json_response["powerScheduleTypes"], options[:include_fields]) }
|
51
|
+
end
|
52
|
+
if options[:json]
|
53
|
+
puts as_json(json_response, options)
|
54
|
+
return 0
|
55
|
+
elsif options[:csv]
|
56
|
+
puts records_as_csv(json_response['powerScheduleTypes'], options)
|
57
|
+
return 0
|
58
|
+
elsif options[:yaml]
|
59
|
+
puts as_yaml(json_response, options)
|
60
|
+
return 0
|
61
|
+
end
|
62
|
+
schedules = json_response['powerScheduleTypes']
|
63
|
+
title = "Morpheus Power Schedules"
|
64
|
+
subtitles = []
|
65
|
+
# if group
|
66
|
+
# subtitles << "Group: #{group['name']}".strip
|
67
|
+
# end
|
68
|
+
# if cloud
|
69
|
+
# subtitles << "Cloud: #{cloud['name']}".strip
|
70
|
+
# end
|
71
|
+
if params[:phrase]
|
72
|
+
subtitles << "Search: #{params[:phrase]}".strip
|
73
|
+
end
|
74
|
+
print_h1 title, subtitles
|
75
|
+
if schedules.empty?
|
76
|
+
print cyan,"No power schedules found.",reset,"\n"
|
77
|
+
else
|
78
|
+
print_schedules_table(schedules, options)
|
79
|
+
print_results_pagination(json_response, {:label => "power schedule", :n_label => "power schedules"})
|
80
|
+
# print_results_pagination(json_response)
|
81
|
+
end
|
82
|
+
print reset,"\n"
|
83
|
+
rescue RestClient::Exception => e
|
84
|
+
print_rest_exception(e, options)
|
85
|
+
return 1
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def get(args)
|
90
|
+
options = {}
|
91
|
+
options[:max_instances] = 10
|
92
|
+
options[:max_servers] = 10
|
93
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
94
|
+
opts.banner = subcommand_usage("[name]")
|
95
|
+
opts.on('--max-instances VALUE', String, "Display a limited number of instances in schedule. Default is 25") do |val|
|
96
|
+
options[:max_instances] = val.to_i
|
97
|
+
end
|
98
|
+
opts.on('--max-hosts VALUE', String, "Display a limited number of hosts in schedule. Default is 25") do |val|
|
99
|
+
options[:max_servers] = val.to_i
|
100
|
+
end
|
101
|
+
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
|
102
|
+
end
|
103
|
+
optparse.parse!(args)
|
104
|
+
if args.count < 1
|
105
|
+
puts optparse
|
106
|
+
return 1
|
107
|
+
end
|
108
|
+
connect(options)
|
109
|
+
id_list = parse_id_list(args)
|
110
|
+
return run_command_for_each_arg(id_list) do |arg|
|
111
|
+
_get(arg, options)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def _get(id, options)
|
116
|
+
|
117
|
+
begin
|
118
|
+
schedule = find_schedule_by_name_or_id(id)
|
119
|
+
if schedule.nil?
|
120
|
+
return 1
|
121
|
+
end
|
122
|
+
if options[:dry_run]
|
123
|
+
print_dry_run @power_scheduling_interface.dry.get(schedule['id'])
|
124
|
+
return
|
125
|
+
end
|
126
|
+
json_response = @power_scheduling_interface.get(schedule['id'])
|
127
|
+
schedule = json_response['powerScheduleType']
|
128
|
+
instances = json_response['instances'] || []
|
129
|
+
servers = json_response['servers'] || []
|
130
|
+
if options[:include_fields]
|
131
|
+
json_response = {"powerScheduleType" => filter_data(json_response["powerScheduleType"], options[:include_fields]) }
|
132
|
+
end
|
133
|
+
if options[:json]
|
134
|
+
puts as_json(json_response, options)
|
135
|
+
return 0
|
136
|
+
elsif options[:yaml]
|
137
|
+
puts as_yaml(json_response, options)
|
138
|
+
return 0
|
139
|
+
elsif options[:csv]
|
140
|
+
puts records_as_csv([json_response['schedule']], options)
|
141
|
+
return 0
|
142
|
+
end
|
143
|
+
|
144
|
+
print_h1 "Power Schedule Details"
|
145
|
+
print cyan
|
146
|
+
description_cols = {
|
147
|
+
"ID" => lambda {|it| it['id'] },
|
148
|
+
#"Account" => lambda {|it| it['owner'] ? it['owner']['name'] : '' },
|
149
|
+
"Name" => lambda {|it| it['name'] },
|
150
|
+
"Description" => lambda {|it| it['description'] },
|
151
|
+
"Type" => lambda {|it| format_schedule_type(it['scheduleType']) },
|
152
|
+
"Enabled" => lambda {|it| format_boolean it['enabled'] },
|
153
|
+
"Time Zone" => lambda {|it| it['scheduleTimezone'] || 'UTC (default)' },
|
154
|
+
"Sunday" => lambda {|it| schedule_hour_to_time(it['sundayOn']) + ' - ' + schedule_hour_to_time(it['sundayOff']) },
|
155
|
+
"Monday" => lambda {|it| schedule_hour_to_time(it['mondayOn']) + ' - ' + schedule_hour_to_time(it['mondayOff']) },
|
156
|
+
"Tuesday" => lambda {|it| schedule_hour_to_time(it['tuesdayOn']) + ' - ' + schedule_hour_to_time(it['tuesdayOff']) },
|
157
|
+
"Wednesday" => lambda {|it| schedule_hour_to_time(it['wednesdayOn']) + ' - ' + schedule_hour_to_time(it['wednesdayOff']) },
|
158
|
+
"Thursday" => lambda {|it| schedule_hour_to_time(it['thursdayOn']) + ' - ' + schedule_hour_to_time(it['thursdayOff']) },
|
159
|
+
"Friday" => lambda {|it| schedule_hour_to_time(it['fridayOn']) + ' - ' + schedule_hour_to_time(it['fridayOff']) },
|
160
|
+
"Saturday" => lambda {|it| schedule_hour_to_time(it['saturdayOn']) + ' - ' + schedule_hour_to_time(it['saturdayOff']) },
|
161
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
162
|
+
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
163
|
+
}
|
164
|
+
print_description_list(description_cols, schedule)
|
165
|
+
|
166
|
+
## Instances
|
167
|
+
if instances.size == 0
|
168
|
+
# print cyan,"No instances",reset,"\n"
|
169
|
+
else
|
170
|
+
print_h2 "Instances (#{instances.size})"
|
171
|
+
print as_pretty_table(instances.first(options[:max_instances]), [:id, :name])
|
172
|
+
if instances.size > options[:max_instances]
|
173
|
+
print_results_pagination({'meta'=>{'total'=>instances.size,'size'=>options[:max_instances]}}, {:label => "instance in schedule", :n_label => "instances in schedule"})
|
174
|
+
#print cyan, "(and #{instances.size - options[:max_instances]} more...)\n"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
## Hosts
|
179
|
+
if servers.size == 0
|
180
|
+
# print cyan,"No hosts",reset,"\n"
|
181
|
+
else
|
182
|
+
print_h2 "Hosts (#{servers.size})"
|
183
|
+
print as_pretty_table(servers.first(options[:max_servers]), [:id, :name])
|
184
|
+
if servers.size > options[:max_servers]
|
185
|
+
print_results_pagination({'meta'=>{'total'=>servers.size,'size'=>options[:max_servers]}}, {:label => "host in schedule", :n_label => "hosts in schedule"})
|
186
|
+
#print cyan, "(and #{servers.size - options[:max_servers]} more...)\n"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
print reset,"\n"
|
191
|
+
|
192
|
+
rescue RestClient::Exception => e
|
193
|
+
print_rest_exception(e, options)
|
194
|
+
return 1
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def add(args)
|
199
|
+
options = {}
|
200
|
+
params = {'scheduleType' => 'power'}
|
201
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
202
|
+
opts.banner = subcommand_usage("[name]")
|
203
|
+
opts.on('--name VALUE', String, "Name") do |val|
|
204
|
+
params['name'] = val
|
205
|
+
end
|
206
|
+
# opts.on('--code VALUE', String, "Code") do |val|
|
207
|
+
# params['code'] = val
|
208
|
+
# end
|
209
|
+
opts.on('--description VALUE', String, "Description") do |val|
|
210
|
+
params['description'] = val
|
211
|
+
end
|
212
|
+
opts.on('--type [power|power off]', String, "Type of Schedule. Default is 'power'") do |val|
|
213
|
+
params['scheduleType'] = val
|
214
|
+
end
|
215
|
+
opts.on('--timezone CODE', String, "The timezone. Default is UTC.") do |val|
|
216
|
+
params['scheduleTimezone'] = val
|
217
|
+
end
|
218
|
+
[
|
219
|
+
'sunday','monday','tuesday','wednesday','thursday','friday','saturday'
|
220
|
+
].each do |day|
|
221
|
+
opts.on("--#{day}On [0-24]", String, "#{day.capitalize} start hour. Default is 0.") do |val|
|
222
|
+
params["#{day}On"] = val.to_f
|
223
|
+
end
|
224
|
+
opts.on("--#{day}Off [0-24]", String, "#{day.capitalize} end hour. Default is 24.") do |val|
|
225
|
+
params["#{day}Off"] = val.to_f
|
226
|
+
end
|
227
|
+
end
|
228
|
+
opts.on('--enabled [on|off]', String, "Can be used to disable it") do |val|
|
229
|
+
options['enabled'] = !(val.to_s == 'off' || val.to_s == 'false')
|
230
|
+
end
|
231
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
|
232
|
+
opts.footer = "Create a new power schedule." + "\n" +
|
233
|
+
"[name] is required and can be passed as --name instead."
|
234
|
+
end
|
235
|
+
optparse.parse!(args)
|
236
|
+
if args.count > 1
|
237
|
+
print_error Morpheus::Terminal.angry_prompt
|
238
|
+
puts_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
|
239
|
+
return 1
|
240
|
+
end
|
241
|
+
# support [name] as first argument
|
242
|
+
if args[0]
|
243
|
+
params['name'] = args[0]
|
244
|
+
end
|
245
|
+
connect(options)
|
246
|
+
begin
|
247
|
+
# construct payload
|
248
|
+
payload = nil
|
249
|
+
if options[:payload]
|
250
|
+
payload = options[:payload]
|
251
|
+
else
|
252
|
+
# merge -O options into normally parsed options
|
253
|
+
params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
254
|
+
# todo: prompt?
|
255
|
+
payload = {'schedule' => params}
|
256
|
+
end
|
257
|
+
if options[:dry_run]
|
258
|
+
print_dry_run @power_scheduling_interface.dry.create(payload)
|
259
|
+
return
|
260
|
+
end
|
261
|
+
json_response = @power_scheduling_interface.create(payload)
|
262
|
+
if options[:json]
|
263
|
+
puts as_json(json_response, options)
|
264
|
+
elsif !options[:quiet]
|
265
|
+
schedule = json_response['schedule']
|
266
|
+
print_green_success "Added power schedule #{schedule['name']}"
|
267
|
+
_get(schedule['id'], {})
|
268
|
+
end
|
269
|
+
return 0
|
270
|
+
rescue RestClient::Exception => e
|
271
|
+
print_rest_exception(e, options)
|
272
|
+
return 1
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
|
277
|
+
def update(args)
|
278
|
+
options = {}
|
279
|
+
params = {}
|
280
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
281
|
+
opts.banner = subcommand_usage("[name]")
|
282
|
+
opts.on('--name VALUE', String, "Name") do |val|
|
283
|
+
params['name'] = val
|
284
|
+
end
|
285
|
+
# opts.on('--code VALUE', String, "Code") do |val|
|
286
|
+
# params['code'] = val
|
287
|
+
# end
|
288
|
+
opts.on('--description VALUE', String, "Description") do |val|
|
289
|
+
params['description'] = val
|
290
|
+
end
|
291
|
+
opts.on('--type [power|power off]', String, "Type of Schedule. Default is 'power'") do |val|
|
292
|
+
params['scheduleType'] = val
|
293
|
+
end
|
294
|
+
opts.on('--timezone CODE', String, "The timezone. Default is UTC.") do |val|
|
295
|
+
params['scheduleTimezone'] = val
|
296
|
+
end
|
297
|
+
[
|
298
|
+
'sunday','monday','tuesday','wednesday','thursday','friday','saturday'
|
299
|
+
].each do |day|
|
300
|
+
opts.on("--#{day}On [0-24]", String, "#{day.capitalize} on hour. Default is 0.") do |val|
|
301
|
+
params["#{day}On"] = val.to_f
|
302
|
+
end
|
303
|
+
opts.on("--#{day}Off [0-24]", String, "#{day.capitalize} off hour. Default is 24.") do |val|
|
304
|
+
params["#{day}Off"] = val.to_f
|
305
|
+
end
|
306
|
+
end
|
307
|
+
opts.on('--enabled [on|off]', String, "Can be used to disable it") do |val|
|
308
|
+
options['enabled'] = !(val.to_s == 'off' || val.to_s == 'false')
|
309
|
+
end
|
310
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
|
311
|
+
opts.footer = "Update a power schedule." + "\n" +
|
312
|
+
"[name] is required. This is the name or id of a power schedule."
|
313
|
+
end
|
314
|
+
optparse.parse!(args)
|
315
|
+
if args.count != 1
|
316
|
+
print_error Morpheus::Terminal.angry_prompt
|
317
|
+
puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
|
318
|
+
return 1
|
319
|
+
end
|
320
|
+
connect(options)
|
321
|
+
begin
|
322
|
+
schedule = find_schedule_by_name_or_id(args[0])
|
323
|
+
if schedule.nil?
|
324
|
+
return 1
|
325
|
+
end
|
326
|
+
# construct payload
|
327
|
+
payload = nil
|
328
|
+
if options[:payload]
|
329
|
+
payload = options[:payload]
|
330
|
+
else
|
331
|
+
# merge -O options into normally parsed options
|
332
|
+
params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
333
|
+
payload = {'powerScheduleType' => params}
|
334
|
+
end
|
335
|
+
if options[:dry_run]
|
336
|
+
print_dry_run @power_scheduling_interface.dry.update(schedule["id"], payload)
|
337
|
+
return
|
338
|
+
end
|
339
|
+
json_response = @power_scheduling_interface.update(schedule["id"], payload)
|
340
|
+
if options[:json]
|
341
|
+
puts as_json(json_response, options)
|
342
|
+
elsif !options[:quiet]
|
343
|
+
print_green_success "Updated power schedule #{schedule['name']}"
|
344
|
+
_get(schedule['id'], {})
|
345
|
+
end
|
346
|
+
return 0
|
347
|
+
rescue RestClient::Exception => e
|
348
|
+
print_rest_exception(e, options)
|
349
|
+
return 1
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def remove(args)
|
354
|
+
options = {}
|
355
|
+
params = {}
|
356
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
357
|
+
opts.banner = subcommand_usage("[name]")
|
358
|
+
build_common_options(opts, options, [:json, :dry_run, :quiet, :auto_confirm])
|
359
|
+
end
|
360
|
+
optparse.parse!(args)
|
361
|
+
if args.count < 1
|
362
|
+
puts optparse
|
363
|
+
return 127
|
364
|
+
end
|
365
|
+
connect(options)
|
366
|
+
|
367
|
+
begin
|
368
|
+
schedule = find_schedule_by_name_or_id(args[0])
|
369
|
+
if schedule.nil?
|
370
|
+
return 1
|
371
|
+
end
|
372
|
+
|
373
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to delete power schedule '#{schedule['name']}'?", options)
|
374
|
+
return false
|
375
|
+
end
|
376
|
+
|
377
|
+
# payload = {
|
378
|
+
# 'powerScheduleType' => {id: schedule["id"]}
|
379
|
+
# }
|
380
|
+
# payload['powerScheduleType'].merge!(schedule)
|
381
|
+
payload = params
|
382
|
+
|
383
|
+
if options[:dry_run]
|
384
|
+
print_dry_run @power_scheduling_interface.dry.destroy(schedule["id"])
|
385
|
+
return
|
386
|
+
end
|
387
|
+
|
388
|
+
json_response = @power_scheduling_interface.destroy(schedule["id"])
|
389
|
+
if options[:json]
|
390
|
+
puts as_json(json_response, options)
|
391
|
+
elsif !options[:quiet]
|
392
|
+
print_green_success "Deleted power schedule #{schedule['name']}"
|
393
|
+
end
|
394
|
+
return 0, nil
|
395
|
+
rescue RestClient::Exception => e
|
396
|
+
print_rest_exception(e, options)
|
397
|
+
return 1
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def add_instances(args)
|
402
|
+
options = {}
|
403
|
+
params = {}
|
404
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
405
|
+
opts.banner = subcommand_usage("[name] [instance]")
|
406
|
+
build_common_options(opts, options, [:payload, :json, :dry_run, :remote, :quiet])
|
407
|
+
opts.footer = "Assign instances to a power schedule.\n" +
|
408
|
+
"[name] is required. This is the name or id of a power schedule.\n" +
|
409
|
+
"[instance] is required. This is the name or id of an instance. More than one can be passed."
|
410
|
+
end
|
411
|
+
optparse.parse!(args)
|
412
|
+
if args.count < 2
|
413
|
+
puts optparse
|
414
|
+
return 1
|
415
|
+
end
|
416
|
+
connect(options)
|
417
|
+
begin
|
418
|
+
schedule = find_schedule_by_name_or_id(args[0])
|
419
|
+
if schedule.nil?
|
420
|
+
return 1
|
421
|
+
end
|
422
|
+
|
423
|
+
# construct payload
|
424
|
+
payload = nil
|
425
|
+
if options[:payload]
|
426
|
+
payload = options[:payload]
|
427
|
+
else
|
428
|
+
instance_ids = args[1..-1]
|
429
|
+
instances = []
|
430
|
+
instance_ids.each do |instance_id|
|
431
|
+
instance = find_instance_by_name_or_id(instance_id)
|
432
|
+
return 1 if instance.nil?
|
433
|
+
instances << instance
|
434
|
+
end
|
435
|
+
payload = {'instances' => instances.collect {|it| it['id'] } }
|
436
|
+
end
|
437
|
+
if options[:dry_run]
|
438
|
+
print_dry_run @power_scheduling_interface.dry.add_instances(schedule["id"], payload)
|
439
|
+
return 0
|
440
|
+
end
|
441
|
+
json_response = @power_scheduling_interface.add_instances(schedule["id"], payload)
|
442
|
+
if options[:json]
|
443
|
+
puts as_json(json_response, options)
|
444
|
+
elsif !options[:quiet]
|
445
|
+
if instances.size == 1
|
446
|
+
print_green_success "Added #{instances[0]['name']} to power schedule #{schedule['name']}"
|
447
|
+
else
|
448
|
+
print_green_success "Added #{instances.size} instances to power schedule #{schedule['name']}"
|
449
|
+
end
|
450
|
+
_get(schedule['id'], {})
|
451
|
+
end
|
452
|
+
return 0
|
453
|
+
rescue RestClient::Exception => e
|
454
|
+
print_rest_exception(e, options)
|
455
|
+
return 1
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
def remove_instances(args)
|
460
|
+
options = {}
|
461
|
+
params = {}
|
462
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
463
|
+
opts.banner = subcommand_usage("[name] [instance]")
|
464
|
+
build_common_options(opts, options, [:payload, :json, :dry_run, :remote, :quiet])
|
465
|
+
opts.footer = "Remove instances from a power schedule.\n" +
|
466
|
+
"[name] is required. This is the name or id of a power schedule.\n" +
|
467
|
+
"[instance] is required. This is the name or id of an instance. More than one can be passed."
|
468
|
+
end
|
469
|
+
optparse.parse!(args)
|
470
|
+
if args.count < 2
|
471
|
+
puts optparse
|
472
|
+
return 1
|
473
|
+
end
|
474
|
+
connect(options)
|
475
|
+
begin
|
476
|
+
schedule = find_schedule_by_name_or_id(args[0])
|
477
|
+
if schedule.nil?
|
478
|
+
return 1
|
479
|
+
end
|
480
|
+
|
481
|
+
# construct payload
|
482
|
+
payload = nil
|
483
|
+
if options[:payload]
|
484
|
+
payload = options[:payload]
|
485
|
+
else
|
486
|
+
instance_ids = args[1..-1]
|
487
|
+
instances = []
|
488
|
+
instance_ids.each do |instance_id|
|
489
|
+
instance = find_instance_by_name_or_id(instance_id)
|
490
|
+
return 1 if instance.nil?
|
491
|
+
instances << instance
|
492
|
+
end
|
493
|
+
payload = {'instances' => instances.collect {|it| it['id'] } }
|
494
|
+
end
|
495
|
+
if options[:dry_run]
|
496
|
+
print_dry_run @power_scheduling_interface.dry.remove_instances(schedule["id"], payload)
|
497
|
+
return 0
|
498
|
+
end
|
499
|
+
json_response = @power_scheduling_interface.remove_instances(schedule["id"], payload)
|
500
|
+
if options[:json]
|
501
|
+
puts as_json(json_response, options)
|
502
|
+
elsif !options[:quiet]
|
503
|
+
if instances.size == 1
|
504
|
+
print_green_success "Removed instance #{instances[0]['name']} from power schedule #{schedule['name']}"
|
505
|
+
else
|
506
|
+
print_green_success "Removed #{instances.size} instances from power schedule #{schedule['name']}"
|
507
|
+
end
|
508
|
+
_get(schedule['id'], {})
|
509
|
+
end
|
510
|
+
return 0
|
511
|
+
rescue RestClient::Exception => e
|
512
|
+
print_rest_exception(e, options)
|
513
|
+
return 1
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
def add_hosts(args)
|
518
|
+
options = {}
|
519
|
+
params = {}
|
520
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
521
|
+
opts.banner = subcommand_usage("[name] [host]")
|
522
|
+
build_common_options(opts, options, [:payload, :json, :dry_run, :remote, :quiet])
|
523
|
+
opts.footer = "Assign hosts to a power schedule.\n" +
|
524
|
+
"[name] is required. This is the name or id of a power schedule.\n" +
|
525
|
+
"[host] is required. This is the name or id of a host. More than one can be passed."
|
526
|
+
end
|
527
|
+
optparse.parse!(args)
|
528
|
+
if args.count < 2
|
529
|
+
puts optparse
|
530
|
+
return 1
|
531
|
+
end
|
532
|
+
connect(options)
|
533
|
+
begin
|
534
|
+
schedule = find_schedule_by_name_or_id(args[0])
|
535
|
+
if schedule.nil?
|
536
|
+
return 1
|
537
|
+
end
|
538
|
+
|
539
|
+
# construct payload
|
540
|
+
payload = nil
|
541
|
+
if options[:payload]
|
542
|
+
payload = options[:payload]
|
543
|
+
else
|
544
|
+
server_ids = args[1..-1]
|
545
|
+
servers = []
|
546
|
+
server_ids.each do |server_id|
|
547
|
+
server = find_server_by_name_or_id(server_id)
|
548
|
+
return 1 if server.nil?
|
549
|
+
servers << server
|
550
|
+
end
|
551
|
+
payload = {'servers' => servers.collect {|it| it['id'] } }
|
552
|
+
end
|
553
|
+
if options[:dry_run]
|
554
|
+
print_dry_run @power_scheduling_interface.dry.add_servers(schedule["id"], payload)
|
555
|
+
return 0
|
556
|
+
end
|
557
|
+
json_response = @power_scheduling_interface.add_servers(schedule["id"], payload)
|
558
|
+
if options[:json]
|
559
|
+
puts as_json(json_response, options)
|
560
|
+
elsif !options[:quiet]
|
561
|
+
if servers.size == 1
|
562
|
+
print_green_success "Added host #{servers[0]['name']} to power schedule #{schedule['name']}"
|
563
|
+
else
|
564
|
+
print_green_success "Added #{servers.size} hosts to power schedule #{schedule['name']}"
|
565
|
+
end
|
566
|
+
_get(schedule['id'], {})
|
567
|
+
end
|
568
|
+
return 0
|
569
|
+
rescue RestClient::Exception => e
|
570
|
+
print_rest_exception(e, options)
|
571
|
+
return 1
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
def remove_hosts(args)
|
576
|
+
options = {}
|
577
|
+
params = {}
|
578
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
579
|
+
opts.banner = subcommand_usage("[name] [host]")
|
580
|
+
build_common_options(opts, options, [:payload, :json, :dry_run, :remote, :quiet])
|
581
|
+
opts.footer = "Remove hosts from a power schedule.\n" +
|
582
|
+
"[name] is required. This is the name or id of a power schedule.\n" +
|
583
|
+
"[host] is required. This is the name or id of a host. More than one can be passed."
|
584
|
+
end
|
585
|
+
optparse.parse!(args)
|
586
|
+
if args.count < 2
|
587
|
+
puts optparse
|
588
|
+
return 1
|
589
|
+
end
|
590
|
+
connect(options)
|
591
|
+
begin
|
592
|
+
schedule = find_schedule_by_name_or_id(args[0])
|
593
|
+
if schedule.nil?
|
594
|
+
return 1
|
595
|
+
end
|
596
|
+
|
597
|
+
# construct payload
|
598
|
+
payload = nil
|
599
|
+
if options[:payload]
|
600
|
+
payload = options[:payload]
|
601
|
+
else
|
602
|
+
server_ids = args[1..-1]
|
603
|
+
servers = []
|
604
|
+
server_ids.each do |server_id|
|
605
|
+
server = find_server_by_name_or_id(server_id)
|
606
|
+
return 1 if server.nil?
|
607
|
+
servers << server
|
608
|
+
end
|
609
|
+
payload = {'servers' => servers.collect {|it| it['id'] } }
|
610
|
+
end
|
611
|
+
if options[:dry_run]
|
612
|
+
print_dry_run @power_scheduling_interface.dry.remove_servers(schedule["id"], payload)
|
613
|
+
return 0
|
614
|
+
end
|
615
|
+
json_response = @power_scheduling_interface.remove_servers(schedule["id"], payload)
|
616
|
+
if options[:json]
|
617
|
+
puts as_json(json_response, options)
|
618
|
+
elsif !options[:quiet]
|
619
|
+
if servers.size == 1
|
620
|
+
print_green_success "Removed host #{servers[0]['name']} from power schedule #{schedule['name']}"
|
621
|
+
else
|
622
|
+
print_green_success "Removed #{servers.size} hosts from power schedule #{schedule['name']}"
|
623
|
+
end
|
624
|
+
_get(schedule['id'], {})
|
625
|
+
end
|
626
|
+
return 0
|
627
|
+
rescue RestClient::Exception => e
|
628
|
+
print_rest_exception(e, options)
|
629
|
+
return 1
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
|
634
|
+
private
|
635
|
+
|
636
|
+
def find_schedule_by_name_or_id(val)
|
637
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
638
|
+
return find_schedule_by_id(val)
|
639
|
+
else
|
640
|
+
return find_schedule_by_name(val)
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
def find_schedule_by_id(id)
|
645
|
+
begin
|
646
|
+
json_response = @power_scheduling_interface.get(id.to_i)
|
647
|
+
return json_response['powerScheduleType']
|
648
|
+
rescue RestClient::Exception => e
|
649
|
+
if e.response && e.response.code == 404
|
650
|
+
print_red_alert "Power Schedule not found by id #{id}"
|
651
|
+
else
|
652
|
+
raise e
|
653
|
+
end
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
def find_schedule_by_name(name)
|
658
|
+
schedules = @power_scheduling_interface.list({name: name.to_s})['powerScheduleTypes']
|
659
|
+
if schedules.empty?
|
660
|
+
print_red_alert "Power Schedule not found by name #{name}"
|
661
|
+
return nil
|
662
|
+
elsif schedules.size > 1
|
663
|
+
print_red_alert "#{schedules.size} power schedules found by name #{name}"
|
664
|
+
print_schedules_table(schedules, {color: red})
|
665
|
+
print_red_alert "Try using ID instead"
|
666
|
+
print reset,"\n"
|
667
|
+
return nil
|
668
|
+
else
|
669
|
+
return schedules[0]
|
670
|
+
end
|
671
|
+
end
|
672
|
+
|
673
|
+
def print_schedules_table(schedules, opts={})
|
674
|
+
columns = [
|
675
|
+
{"ID" => lambda {|schedule| schedule['id'] } },
|
676
|
+
{"NAME" => lambda {|schedule| schedule['name'] } },
|
677
|
+
{"DESCRIPTION" => lambda {|schedule| schedule['description'] } },
|
678
|
+
{"TYPE" => lambda {|schedule| format_schedule_type(schedule['scheduleType']) } },
|
679
|
+
#{"TIMES" => lambda {|schedule| format_schedule_days_short(schedule) } },
|
680
|
+
]
|
681
|
+
if opts[:include_fields]
|
682
|
+
columns = opts[:include_fields]
|
683
|
+
end
|
684
|
+
print as_pretty_table(schedules, columns, opts)
|
685
|
+
end
|
686
|
+
|
687
|
+
def format_schedule_type(val)
|
688
|
+
case val.to_s.downcase
|
689
|
+
when "power" then "Power On"
|
690
|
+
when "power off" then "Power Off"
|
691
|
+
else
|
692
|
+
val.to_s #.capitalize
|
693
|
+
end
|
694
|
+
end
|
695
|
+
|
696
|
+
# convert the schedule on/off values [0-24] to a time
|
697
|
+
def schedule_hour_to_time(val, format=nil)
|
698
|
+
hour = val.to_f.floor
|
699
|
+
remainder = val.to_f % 1
|
700
|
+
minute = remainder == 0 ? 0 : (60 * remainder).floor
|
701
|
+
# if hour > 23
|
702
|
+
# "Midnight" # "12:00 AM"
|
703
|
+
# elsif hour > 12
|
704
|
+
# "#{(hour-12).to_s.rjust(2,'0')}:#{minute.to_s.rjust(2,'0')} PM"
|
705
|
+
# else
|
706
|
+
# "#{hour.to_s.rjust(2,'0')}:#{minute.to_s.rjust(2,'0')} AM"
|
707
|
+
# end
|
708
|
+
if format == :short
|
709
|
+
if minute == 0
|
710
|
+
"#{hour}"
|
711
|
+
else
|
712
|
+
"#{hour}:#{minute.to_s.rjust(2,'0')}"
|
713
|
+
end
|
714
|
+
else
|
715
|
+
"#{hour.to_s.rjust(2,'0')}:#{minute.to_s.rjust(2,'0')}"
|
716
|
+
end
|
717
|
+
end
|
718
|
+
|
719
|
+
def format_schedule_days_short(schedule)
|
720
|
+
# [
|
721
|
+
# "Sn: #{schedule_hour_to_time(schedule['sundayOn'])}-#{schedule_hour_to_time(schedule['sundayOff'])}",
|
722
|
+
# "M: #{schedule_hour_to_time(schedule['mondayOn'])}-#{schedule_hour_to_time(schedule['mondayOff'])}",
|
723
|
+
# "T: #{schedule_hour_to_time(schedule['tuesdayOn'])}-#{schedule_hour_to_time(schedule['tuesdayOff'])}",
|
724
|
+
# "W: #{schedule_hour_to_time(schedule['wednesdayOn'])}-#{schedule_hour_to_time(schedule['wednesdayOff'])}",
|
725
|
+
# "Th: #{schedule_hour_to_time(schedule['thursdayOn'])}-#{schedule_hour_to_time(schedule['thursdayOff'])}",
|
726
|
+
# "F: #{schedule_hour_to_time(schedule['fridayOn'])}-#{schedule_hour_to_time(schedule['fridayOff'])}",
|
727
|
+
# "S: #{schedule_hour_to_time(schedule['saturdayOn'])}-#{schedule_hour_to_time(schedule['saturdayOff'])}",
|
728
|
+
# ].join(", ")
|
729
|
+
[
|
730
|
+
"Sn: #{schedule_hour_to_time(schedule['sundayOn'],:short)}-#{schedule_hour_to_time(schedule['sundayOff'],:short)}",
|
731
|
+
"M: #{schedule_hour_to_time(schedule['mondayOn'],:short)}-#{schedule_hour_to_time(schedule['mondayOff'],:short)}",
|
732
|
+
"T: #{schedule_hour_to_time(schedule['tuesdayOn'],:short)}-#{schedule_hour_to_time(schedule['tuesdayOff'],:short)}",
|
733
|
+
"W: #{schedule_hour_to_time(schedule['wednesdayOn'],:short)}-#{schedule_hour_to_time(schedule['wednesdayOff'],:short)}",
|
734
|
+
"Th: #{schedule_hour_to_time(schedule['thursdayOn'],:short)}-#{schedule_hour_to_time(schedule['thursdayOff'],:short)}",
|
735
|
+
"F: #{schedule_hour_to_time(schedule['fridayOn'],:short)}-#{schedule_hour_to_time(schedule['fridayOff'],:short)}",
|
736
|
+
"S: #{schedule_hour_to_time(schedule['saturdayOn'],:short)}-#{schedule_hour_to_time(schedule['saturdayOff'],:short)}",
|
737
|
+
].join(", ")
|
738
|
+
end
|
739
|
+
|
740
|
+
|
741
|
+
def find_instance_by_name_or_id(val)
|
742
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
743
|
+
return find_instance_by_id(val)
|
744
|
+
else
|
745
|
+
return find_instance_by_name(val)
|
746
|
+
end
|
747
|
+
end
|
748
|
+
|
749
|
+
def find_instance_by_id(id)
|
750
|
+
begin
|
751
|
+
json_response = @instances_interface.get(id.to_i)
|
752
|
+
return json_response['instance']
|
753
|
+
rescue RestClient::Exception => e
|
754
|
+
if e.response && e.response.code == 404
|
755
|
+
print_red_alert "Instance not found by id #{id}"
|
756
|
+
else
|
757
|
+
raise e
|
758
|
+
end
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
def find_instance_by_name(name)
|
763
|
+
instances = @instances_interface.get({name: name.to_s})['instances']
|
764
|
+
if instances.empty?
|
765
|
+
print_red_alert "Instance not found by name #{name}"
|
766
|
+
return nil
|
767
|
+
elsif instances.size > 1
|
768
|
+
print_red_alert "#{instances.size} instances found by name #{name}"
|
769
|
+
as_pretty_table(instances, [:id, :name], {color: red})
|
770
|
+
print_red_alert "Try using ID instead"
|
771
|
+
print reset,"\n"
|
772
|
+
return nil
|
773
|
+
else
|
774
|
+
return instances[0]
|
775
|
+
end
|
776
|
+
end
|
777
|
+
|
778
|
+
def find_server_by_name_or_id(val)
|
779
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
780
|
+
return find_server_by_id(val)
|
781
|
+
else
|
782
|
+
return find_server_by_name(val)
|
783
|
+
end
|
784
|
+
end
|
785
|
+
|
786
|
+
def find_server_by_id(id)
|
787
|
+
begin
|
788
|
+
json_response = @servers_interface.get(id.to_i)
|
789
|
+
return json_response['server']
|
790
|
+
rescue RestClient::Exception => e
|
791
|
+
if e.response && e.response.code == 404
|
792
|
+
print_red_alert "Server not found by id #{id}"
|
793
|
+
else
|
794
|
+
raise e
|
795
|
+
end
|
796
|
+
end
|
797
|
+
end
|
798
|
+
|
799
|
+
def find_server_by_name(name)
|
800
|
+
servers = @servers_interface.get({name: name.to_s})['servers']
|
801
|
+
if servers.empty?
|
802
|
+
print_red_alert "Host not found by name #{name}"
|
803
|
+
return nil
|
804
|
+
elsif servers.size > 1
|
805
|
+
print_red_alert "#{servers.size} hosts found by name #{name}"
|
806
|
+
as_pretty_table(servers, [:id, :name], {color: red})
|
807
|
+
print_red_alert "Try using ID instead"
|
808
|
+
print reset,"\n"
|
809
|
+
return nil
|
810
|
+
else
|
811
|
+
return servers[0]
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
end
|