morpheus-cli 3.2.0.1 → 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- # todo: show public key
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 !params['publicKey']
184
- print_red_alert "publicKey is required"
185
- exit 1
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
- :md5,
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' => true, 'displayOrder' => 2},
369
- {'fieldName' => 'privateKey', 'fieldLabel' => 'Private Key', 'type' => 'text', 'required' => true, 'displayOrder' => 3},
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