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.
@@ -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