morpheus-cli 3.5.1.1 → 3.5.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7d4e2ec7679868b364bceefa80e3532b0393f7fddfd7ff42e3040e975409159
4
- data.tar.gz: 580d2398452ad35d7a68fd84159baf86bfa6b737c4ea044dfb384ec9553ea855
3
+ metadata.gz: a2c377489b360bdecb8e09beb55f26f0132f86fdfe9de85b7199f185640290d3
4
+ data.tar.gz: 98a031f9eb75699ff0e6f7420fead42ba257e47a5876503b6c1bc36f1f355617
5
5
  SHA512:
6
- metadata.gz: cf0bd19fc111f2d64ff3886b9cd8d9a60aa5f103de1e514faa77be3d126f5ae69f91c51738b1436257145ea63f4004c1464e1067654967750afa6a2718ded8c7
7
- data.tar.gz: c77e6e3866469ff4941affa0785556f27fe6cd2ea41c5cf14f998faf724c293e7c60193e29dce60b4d6a3f9811e7df2b2766e91c846006577536c38c1b452cbf
6
+ metadata.gz: 36d00338167560525f3110e7c931d22d20f8d5c036cab21df50ac84b1a0ec2487978f02f3692256fb27227def5e1051fdd941736b08974f869accff76b4de8c0
7
+ data.tar.gz: b351fac642c9a816da640331e97482ece7d4d27b4a018d5a1f89344c2061e0385b3820f84ca41d09ff236ce702ecad1af8a421a627abf15ac6ce9b268b6cfdc5
@@ -189,8 +189,12 @@ class Morpheus::APIClient
189
189
  Morpheus::DashboardInterface.new(@access_token, @refresh_token, @expires_at, @base_url)
190
190
  end
191
191
 
192
- def power_scheduling
193
- Morpheus::PowerSchedulingInterface.new(@access_token, @refresh_token, @expires_at, @base_url)
192
+ def power_schedules
193
+ Morpheus::PowerSchedulesInterface.new(@access_token, @refresh_token, @expires_at, @base_url)
194
+ end
195
+
196
+ def execute_schedules
197
+ Morpheus::ExecuteSchedulesInterface.new(@access_token, @refresh_token, @expires_at, @base_url)
194
198
  end
195
199
 
196
200
  def setup
@@ -0,0 +1,78 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ class Morpheus::ExecuteSchedulesInterface < Morpheus::APIClient
4
+ def initialize(access_token, refresh_token, expires_at = nil, base_url=nil)
5
+ @access_token = access_token
6
+ @refresh_token = refresh_token
7
+ @base_url = base_url
8
+ @expires_at = expires_at
9
+ end
10
+
11
+ def get(id)
12
+ raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
13
+ url = "#{@base_url}/api/execute-schedules/#{id}"
14
+ headers = { params: {}, authorization: "Bearer #{@access_token}" }
15
+ opts = {method: :get, url: url, timeout: 10, headers: headers}
16
+ execute(opts)
17
+ end
18
+
19
+ def list(options={})
20
+ url = "#{@base_url}/api/execute-schedules"
21
+ headers = { params: {}, authorization: "Bearer #{@access_token}" }
22
+ headers[:params].merge!(options)
23
+ opts = {method: :get, url: url, timeout: 10, headers: headers}
24
+ execute(opts)
25
+ end
26
+
27
+ def create(options)
28
+ url = "#{@base_url}/api/execute-schedules"
29
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
30
+ payload = options
31
+ opts = {method: :post, url: url, timeout: 10, headers: headers, payload: payload.to_json}
32
+ execute(opts)
33
+ end
34
+
35
+ def update(id, options)
36
+ url = "#{@base_url}/api/execute-schedules/#{id}"
37
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
38
+ payload = options
39
+ opts = {method: :put, url: url, timeout: 10, headers: headers, payload: payload.to_json}
40
+ execute(opts)
41
+ end
42
+
43
+ def destroy(id)
44
+ url = "#{@base_url}/api/execute-schedules/#{id}"
45
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
46
+ opts = {method: :delete, url: url, timeout: 10, headers: headers}
47
+ execute(opts)
48
+ end
49
+
50
+ def add_instances(id, payload)
51
+ url = "#{@base_url}/api/execute-schedules/#{id}/add-instances"
52
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
53
+ opts = {method: :put, url: url, timeout: 10, headers: headers, payload: payload.to_json}
54
+ execute(opts)
55
+ end
56
+
57
+ def remove_instances(id, payload)
58
+ url = "#{@base_url}/api/execute-schedules/#{id}/remove-instances"
59
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
60
+ opts = {method: :put, url: url, timeout: 10, headers: headers, payload: payload.to_json}
61
+ execute(opts)
62
+ end
63
+
64
+ def add_servers(id, payload)
65
+ url = "#{@base_url}/api/execute-schedules/#{id}/add-servers"
66
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
67
+ opts = {method: :put, url: url, timeout: 10, headers: headers, payload: payload.to_json}
68
+ execute(opts)
69
+ end
70
+
71
+ def remove_servers(id, payload)
72
+ url = "#{@base_url}/api/execute-schedules/#{id}/remove-servers"
73
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
74
+ opts = {method: :put, url: url, timeout: 10, headers: headers, payload: payload.to_json}
75
+ execute(opts)
76
+ end
77
+
78
+ end
@@ -1,6 +1,6 @@
1
1
  require 'morpheus/api/api_client'
2
2
 
3
- class Morpheus::PowerSchedulingInterface < Morpheus::APIClient
3
+ class Morpheus::PowerSchedulesInterface < Morpheus::APIClient
4
4
  def initialize(access_token, refresh_token, expires_at = nil, base_url=nil)
5
5
  @access_token = access_token
6
6
  @refresh_token = refresh_token
@@ -10,14 +10,14 @@ class Morpheus::PowerSchedulingInterface < Morpheus::APIClient
10
10
 
11
11
  def get(id)
12
12
  raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
13
- url = "#{@base_url}/api/power-scheduling/#{id}"
13
+ url = "#{@base_url}/api/power-schedules/#{id}"
14
14
  headers = { params: {}, authorization: "Bearer #{@access_token}" }
15
15
  opts = {method: :get, url: url, timeout: 10, headers: headers}
16
16
  execute(opts)
17
17
  end
18
18
 
19
19
  def list(options={})
20
- url = "#{@base_url}/api/power-scheduling"
20
+ url = "#{@base_url}/api/power-schedules"
21
21
  headers = { params: {}, authorization: "Bearer #{@access_token}" }
22
22
  headers[:params].merge!(options)
23
23
  opts = {method: :get, url: url, timeout: 10, headers: headers}
@@ -25,7 +25,7 @@ class Morpheus::PowerSchedulingInterface < Morpheus::APIClient
25
25
  end
26
26
 
27
27
  def create(options)
28
- url = "#{@base_url}/api/power-scheduling"
28
+ url = "#{@base_url}/api/power-schedules"
29
29
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
30
30
  payload = options
31
31
  opts = {method: :post, url: url, timeout: 10, headers: headers, payload: payload.to_json}
@@ -33,7 +33,7 @@ class Morpheus::PowerSchedulingInterface < Morpheus::APIClient
33
33
  end
34
34
 
35
35
  def update(id, options)
36
- url = "#{@base_url}/api/power-scheduling/#{id}"
36
+ url = "#{@base_url}/api/power-schedules/#{id}"
37
37
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
38
38
  payload = options
39
39
  opts = {method: :put, url: url, timeout: 10, headers: headers, payload: payload.to_json}
@@ -41,35 +41,35 @@ class Morpheus::PowerSchedulingInterface < Morpheus::APIClient
41
41
  end
42
42
 
43
43
  def destroy(id)
44
- url = "#{@base_url}/api/power-scheduling/#{id}"
44
+ url = "#{@base_url}/api/power-schedules/#{id}"
45
45
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
46
46
  opts = {method: :delete, url: url, timeout: 10, headers: headers}
47
47
  execute(opts)
48
48
  end
49
49
 
50
50
  def add_instances(id, payload)
51
- url = "#{@base_url}/api/power-scheduling/#{id}/add-instances"
51
+ url = "#{@base_url}/api/power-schedules/#{id}/add-instances"
52
52
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
53
53
  opts = {method: :put, url: url, timeout: 10, headers: headers, payload: payload.to_json}
54
54
  execute(opts)
55
55
  end
56
56
 
57
57
  def remove_instances(id, payload)
58
- url = "#{@base_url}/api/power-scheduling/#{id}/remove-instances"
58
+ url = "#{@base_url}/api/power-schedules/#{id}/remove-instances"
59
59
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
60
60
  opts = {method: :put, url: url, timeout: 10, headers: headers, payload: payload.to_json}
61
61
  execute(opts)
62
62
  end
63
63
 
64
64
  def add_servers(id, payload)
65
- url = "#{@base_url}/api/power-scheduling/#{id}/add-servers"
65
+ url = "#{@base_url}/api/power-schedules/#{id}/add-servers"
66
66
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
67
67
  opts = {method: :put, url: url, timeout: 10, headers: headers, payload: payload.to_json}
68
68
  execute(opts)
69
69
  end
70
70
 
71
71
  def remove_servers(id, payload)
72
- url = "#{@base_url}/api/power-scheduling/#{id}/remove-servers"
72
+ url = "#{@base_url}/api/power-schedules/#{id}/remove-servers"
73
73
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
74
74
  opts = {method: :put, url: url, timeout: 10, headers: headers, payload: payload.to_json}
75
75
  execute(opts)
@@ -80,7 +80,8 @@ module Morpheus
80
80
  load 'morpheus/cli/logout.rb'
81
81
  load 'morpheus/cli/whoami.rb'
82
82
  load 'morpheus/cli/dashboard_command.rb'
83
- load 'morpheus/cli/power_scheduling_command.rb'
83
+ load 'morpheus/cli/power_schedules_command.rb'
84
+ load 'morpheus/cli/execute_schedules_command.rb'
84
85
  load 'morpheus/cli/recent_activity_command.rb'
85
86
  load 'morpheus/cli/groups.rb'
86
87
  load 'morpheus/cli/clouds.rb'
@@ -258,14 +258,6 @@ class Morpheus::Cli::ArchivesCommand
258
258
  opts.on('--storageProvider VALUE', String, "Storage Provider ID") do |val|
259
259
  options['storageProvider'] = val.to_s
260
260
  end
261
- opts.on('--payload JSON', String, "JSON Payload") do |val|
262
- options['payload'] = JSON.parse(val.to_s)
263
- end
264
- opts.on('--payload-file FILE', String, "JSON Payload from a local file") do |val|
265
- payload_file = val.to_s
266
- options['payload'] = JSON.parse(File.read(payload_file))
267
- end
268
-
269
261
  opts.on('--visibility [private|public]', String, "Visibility determines if read access is restricted to the specified Tenants (Private) or all tenants (Public).") do |val|
270
262
  options['visibility'] = val.to_s
271
263
  end
@@ -276,14 +268,14 @@ class Morpheus::Cli::ArchivesCommand
276
268
  opts.on('--isPublic [on|off]', String, "Enabling Public URL allows files to be downloaded without any authentication.") do |val|
277
269
  options['isPublic'] = (val.to_s == 'on' || val.to_s == 'true')
278
270
  end
279
- build_common_options(opts, options, [:options, :json, :dry_run, :quiet])
271
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet])
280
272
  opts.footer = "Create a new archive bucket."
281
273
  end
282
274
  optparse.parse!(args)
283
275
  connect(options)
284
276
  begin
285
277
  options.merge!(options[:options]) if options[:options] # so -O var= works..
286
-
278
+ option_params = options.reject {|k,v| k.is_a?(Symbol) }
287
279
  # use the -g GROUP or active group by default
288
280
  # options['group'] ||= @active_group_id
289
281
 
@@ -291,10 +283,15 @@ class Morpheus::Cli::ArchivesCommand
291
283
  if args[0] && !options['name']
292
284
  options['name'] = args[0]
293
285
  end
294
-
295
- archive_bucket_payload = prompt_new_archive_bucket(options)
296
- return 1 if !archive_bucket_payload
297
- payload = {'archiveBucket' => archive_bucket_payload}
286
+ payload = nil
287
+ if options[:payload]
288
+ payload = options[:payload]
289
+ payload.deep_merge!({'archiveBucket' => option_params}) if !option_params.empty?
290
+ else
291
+ archive_bucket_payload = prompt_new_archive_bucket(options)
292
+ return 1 if !archive_bucket_payload
293
+ payload = {'archiveBucket' => archive_bucket_payload}
294
+ end
298
295
 
299
296
  if options[:dry_run]
300
297
  print_dry_run @archive_buckets_interface.dry.create(payload)
@@ -349,7 +346,7 @@ class Morpheus::Cli::ArchivesCommand
349
346
  opts.on('--isPublic [on|off]', String, "Enabling Public URL allows files to be downloaded without any authentication.") do |val|
350
347
  options['isPublic'] = (val.to_s == 'on' || val.to_s == 'true')
351
348
  end
352
- build_common_options(opts, options, [:options, :json, :dry_run, :quiet])
349
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet])
353
350
  opts.footer = "Update an existing archive bucket."
354
351
  end
355
352
  optparse.parse!(args)
@@ -362,9 +359,18 @@ class Morpheus::Cli::ArchivesCommand
362
359
  begin
363
360
  archive_bucket = find_archive_bucket_by_name_or_id(args[0])
364
361
 
365
- archive_bucket_payload = prompt_edit_archive_bucket(archive_bucket, options)
366
- return 1 if !archive_bucket_payload
367
- payload = {'archiveBucket' => archive_bucket_payload}
362
+ options.merge!(options[:options]) if options[:options] # so -O var= works..
363
+ option_params = options.reject {|k,v| k.is_a?(Symbol) }
364
+
365
+ payload = nil
366
+ if options[:payload]
367
+ payload = options[:payload]
368
+ payload.deep_merge!({'archiveBucket' => option_params}) if !option_params.empty?
369
+ else
370
+ archive_bucket_payload = prompt_edit_archive_bucket(archive_bucket, options)
371
+ return 1 if !archive_bucket_payload
372
+ payload = {'archiveBucket' => archive_bucket_payload}
373
+ end
368
374
 
369
375
  if options[:dry_run]
370
376
  print_dry_run @archive_buckets_interface.dry.update(archive_bucket["id"], payload)
@@ -1,6 +1,7 @@
1
1
  require 'optparse'
2
2
  require 'morpheus/logging'
3
3
  require 'morpheus/cli/cli_command'
4
+ require 'json'
4
5
 
5
6
  class Morpheus::Cli::CurlCommand
6
7
  include Morpheus::Cli::CliCommand
@@ -16,6 +17,9 @@ class Morpheus::Cli::CurlCommand
16
17
  options = {}
17
18
  optparse = Morpheus::Cli::OptionParser.new do|opts|
18
19
  opts.banner = "Usage: morpheus curl [path] -- [*args]"
20
+ opts.on( '-p', '--pretty', "Print result as parsed JSON." ) do
21
+ options[:pretty] = true
22
+ end
19
23
  build_common_options(opts, options, [:remote])
20
24
  opts.footer = <<-EOT
21
25
  This invokes the `curl` command with url "appliance_url/api/$0
@@ -63,9 +67,15 @@ EOT
63
67
  end
64
68
 
65
69
  # determine curl url
66
- base_url = @appliance_url.chomp("/")
67
- api_path = args[0].sub(/^\//, "")
68
- url = "#{base_url}/#{api_path}"
70
+ url = nil
71
+ api_path = args[0].to_s.strip
72
+ # allow absolute path for the current appliance url only
73
+ if api_path.match(/^#{Regexp.escape(@appliance_url)}/)
74
+ url = api_path
75
+ else
76
+ api_path = api_path.sub(/^\//, "") # strip leading slash
77
+ url = "#{@appliance_url.chomp('/')}/#{api_path}"
78
+ end
69
79
  curl_cmd = "curl \"#{url}\""
70
80
  if @access_token
71
81
  curl_cmd << " -H \"Authorization: Bearer #{@access_token}\""
@@ -81,7 +91,21 @@ EOT
81
91
  print reset
82
92
  # print result
83
93
  curl_output = `#{curl_cmd}`
84
- puts curl_output
94
+ if options[:pretty]
95
+ output_lines = curl_output.split("\n")
96
+ last_line = output_lines.pop
97
+ puts output_lines.join("\n")
98
+ begin
99
+ json_data = JSON.parse(last_line)
100
+ json_string = JSON.pretty_generate(json_data)
101
+ puts json_string
102
+ rescue => ex
103
+ Morpheus::Logging::DarkPrinter.puts "failed to parse curl result as JSON data Error: #{ex.message}" if Morpheus::Logging.debug?
104
+ puts last_line
105
+ end
106
+ else
107
+ puts curl_output
108
+ end
85
109
  return $?.success?
86
110
 
87
111
  end
@@ -0,0 +1,727 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::ExecuteSchedulesCommand
4
+ include Morpheus::Cli::CliCommand
5
+ # include Morpheus::Cli::ProvisioningHelper
6
+ set_command_name :'execute-schedules'
7
+
8
+ register_subcommands :list, :get, :add, :update, :remove
9
+ register_subcommands :'add-instances' => :add_instances
10
+ register_subcommands :'remove-instances' => :remove_instances
11
+ register_subcommands :'add-hosts' => :add_hosts
12
+ register_subcommands :'remove-hosts' => :remove_hosts
13
+
14
+ def connect(opts)
15
+ @api_client = establish_remote_appliance_connection(opts)
16
+ @execute_schedules_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).execute_schedules
17
+ @instances_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instances
18
+ @servers_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).servers
19
+ end
20
+
21
+ def handle(args)
22
+ handle_subcommand(args)
23
+ end
24
+
25
+ def list(args)
26
+ options = {}
27
+ params = {}
28
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
29
+ opts.banner = subcommand_usage()
30
+ build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :dry_run, :remote])
31
+ end
32
+ optparse.parse!(args)
33
+ connect(options)
34
+ begin
35
+ params.merge!(parse_list_options(options))
36
+ if options[:dry_run]
37
+ print_dry_run @execute_schedules_interface.dry.list(params)
38
+ return
39
+ end
40
+ json_response = @execute_schedules_interface.list(params)
41
+ if options[:json]
42
+ puts as_json(json_response, options, "schedules")
43
+ return 0
44
+ elsif options[:csv]
45
+ puts records_as_csv(json_response['schedules'], options)
46
+ return 0
47
+ elsif options[:yaml]
48
+ puts as_yaml(json_response, options, "schedules")
49
+ return 0
50
+ end
51
+ schedules = json_response['schedules']
52
+ title = "Morpheus Execute Schedules"
53
+ subtitles = []
54
+ subtitles += parse_list_subtitles(options)
55
+ print_h1 title, subtitles
56
+ if schedules.empty?
57
+ print cyan,"No execute schedules found.",reset,"\n"
58
+ else
59
+ print_schedules_table(schedules, options)
60
+ print_results_pagination(json_response, {:label => "schedule", :n_label => "schedules"})
61
+ # print_results_pagination(json_response)
62
+ end
63
+ print reset,"\n"
64
+ rescue RestClient::Exception => e
65
+ print_rest_exception(e, options)
66
+ return 1
67
+ end
68
+ end
69
+
70
+ def get(args)
71
+ options = {}
72
+ options[:max_instances] = 10
73
+ options[:max_servers] = 10
74
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
75
+ opts.banner = subcommand_usage("[name]")
76
+ opts.on('--max-instances VALUE', String, "Display a limited number of instances in schedule. Default is 25") do |val|
77
+ options[:max_instances] = val.to_i
78
+ end
79
+ opts.on('--max-hosts VALUE', String, "Display a limited number of hosts in schedule. Default is 25") do |val|
80
+ options[:max_servers] = val.to_i
81
+ end
82
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
83
+ end
84
+ optparse.parse!(args)
85
+ if args.count < 1
86
+ puts optparse
87
+ return 1
88
+ end
89
+ connect(options)
90
+ id_list = parse_id_list(args)
91
+ return run_command_for_each_arg(id_list) do |arg|
92
+ _get(arg, options)
93
+ end
94
+ end
95
+
96
+ def _get(id, options)
97
+ options ||= {}
98
+ options[:max_servers] ||= 10
99
+ options[:max_instances] ||= 10
100
+ begin
101
+ schedule = find_schedule_by_name_or_id(id)
102
+ if schedule.nil?
103
+ return 1
104
+ end
105
+ if options[:dry_run]
106
+ print_dry_run @execute_schedules_interface.dry.get(schedule['id'])
107
+ return
108
+ end
109
+ json_response = @execute_schedules_interface.get(schedule['id'])
110
+ schedule = json_response['schedule']
111
+ instances = json_response['instances'] || []
112
+ servers = json_response['servers'] || []
113
+ if options[:json]
114
+ puts as_json(json_response, options, "schedule")
115
+ return 0
116
+ elsif options[:yaml]
117
+ puts as_yaml(json_response, options, "schedule")
118
+ return 0
119
+ elsif options[:csv]
120
+ puts records_as_csv([json_response['schedule']], options)
121
+ return 0
122
+ end
123
+
124
+ print_h1 "Execute Schedule Details"
125
+ print cyan
126
+ description_cols = {
127
+ "ID" => lambda {|it| it['id'] },
128
+ #"Account" => lambda {|it| it['owner'] ? it['owner']['name'] : '' },
129
+ "Name" => lambda {|it| it['name'] },
130
+ "Description" => lambda {|it| it['description'] },
131
+ "Type" => lambda {|it| format_schedule_type(it['scheduleType']) },
132
+ "Enabled" => lambda {|it| format_boolean it['enabled'] },
133
+ "Time Zone" => lambda {|it| it['scheduleTimezone'] || 'UTC (default)' },
134
+ "Cron" => lambda {|it| it['cron'] },
135
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
136
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
137
+ }
138
+ print_description_list(description_cols, schedule)
139
+
140
+ ## Instances
141
+ if instances.size == 0
142
+ # print cyan,"No instances",reset,"\n"
143
+ else
144
+ print_h2 "Instances (#{instances.size})"
145
+ instance_rows = instances.first(options[:max_instances])
146
+ print as_pretty_table(instance_rows, [:id, :name])
147
+ print_results_pagination({'meta'=>{'total'=>instances.size,'size'=>instance_rows.size,'max'=>options[:max_servers],'offset'=>0}}, {:label => "instance in schedule", :n_label => "instances in schedule"})
148
+ end
149
+
150
+ ## Hosts
151
+ if servers.size == 0
152
+ # print cyan,"No hosts",reset,"\n"
153
+ else
154
+ options[:max_servers] ||= 10
155
+ print_h2 "Hosts (#{servers.size})"
156
+ server_rows = servers.first(options[:max_servers])
157
+ print as_pretty_table(server_rows, [:id, :name])
158
+ print_results_pagination({'meta'=>{'total'=>servers.size,'size'=>server_rows.size,'max'=>options[:max_servers],'offset'=>0}}, {:label => "host in schedule", :n_label => "hosts in schedule"})
159
+ end
160
+
161
+ print reset,"\n"
162
+
163
+ rescue RestClient::Exception => e
164
+ print_rest_exception(e, options)
165
+ return 1
166
+ end
167
+ end
168
+
169
+ def add(args)
170
+ options = {}
171
+ params = {'scheduleType' => 'execute'}
172
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
173
+ opts.banner = subcommand_usage("[name]")
174
+ opts.on('--name VALUE', String, "Name") do |val|
175
+ params['name'] = val
176
+ end
177
+ # opts.on('--code VALUE', String, "Code") do |val|
178
+ # params['code'] = val
179
+ # end
180
+ opts.on('--description VALUE', String, "Description") do |val|
181
+ params['description'] = val
182
+ end
183
+ opts.on('--type [execute]', String, "Type of Schedule. Default is 'execute'") do |val|
184
+ params['scheduleType'] = val
185
+ end
186
+ opts.on('--timezone CODE', String, "The timezone. Default is UTC.") do |val|
187
+ params['scheduleTimezone'] = val
188
+ end
189
+ opts.on('--cron EXPRESSION', String, "Cron Expression. Default is daily at midnight '0 0 * * *'") do |val|
190
+ params['cron'] = val
191
+ end
192
+ opts.on('--enabled [on|off]', String, "Can be used to disable it") do |val|
193
+ params['enabled'] = !(val.to_s == 'off' || val.to_s == 'false')
194
+ end
195
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
196
+ opts.footer = "Create a new execute schedule." + "\n" +
197
+ "[name] is required and can be passed as --name instead."
198
+ end
199
+ optparse.parse!(args)
200
+ if args.count > 1
201
+ print_error Morpheus::Terminal.angry_prompt
202
+ puts_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
203
+ return 1
204
+ end
205
+ # support [name] as first argument
206
+ if args[0]
207
+ params['name'] = args[0]
208
+ end
209
+ connect(options)
210
+ begin
211
+ # construct payload
212
+ payload = nil
213
+ if options[:payload]
214
+ payload = options[:payload]
215
+ else
216
+ # merge -O options into normally parsed options
217
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
218
+ # todo: prompt?
219
+ payload = {'schedule' => params}
220
+ end
221
+ if options[:dry_run]
222
+ print_dry_run @execute_schedules_interface.dry.create(payload)
223
+ return
224
+ end
225
+ json_response = @execute_schedules_interface.create(payload)
226
+ if options[:json]
227
+ puts as_json(json_response, options)
228
+ elsif !options[:quiet]
229
+ schedule = json_response['schedule']
230
+ print_green_success "Added execute schedule #{schedule['name']}"
231
+ _get(schedule['id'], {})
232
+ end
233
+ return 0
234
+ rescue RestClient::Exception => e
235
+ print_rest_exception(e, options)
236
+ return 1
237
+ end
238
+ end
239
+
240
+
241
+ def update(args)
242
+ options = {}
243
+ params = {}
244
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
245
+ opts.banner = subcommand_usage("[name]")
246
+ opts.on('--name VALUE', String, "Name") do |val|
247
+ params['name'] = val
248
+ end
249
+ # opts.on('--code VALUE', String, "Code") do |val|
250
+ # params['code'] = val
251
+ # end
252
+ opts.on('--description VALUE', String, "Description") do |val|
253
+ params['description'] = val
254
+ end
255
+ opts.on('--type [execute]', String, "Type of Schedule. Default is 'execute'") do |val|
256
+ params['scheduleType'] = val
257
+ end
258
+ opts.on('--timezone CODE', String, "The timezone. Default is UTC.") do |val|
259
+ params['scheduleTimezone'] = val
260
+ end
261
+ opts.on('--cron EXPRESSION', String, "Cron Expression") do |val|
262
+ params['cron'] = val
263
+ end
264
+ opts.on('--enabled [on|off]', String, "Can be used to disable it") do |val|
265
+ params['enabled'] = !(val.to_s == 'off' || val.to_s == 'false')
266
+ end
267
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
268
+ opts.footer = "Update a execute schedule." + "\n" +
269
+ "[name] is required. This is the name or id of a execute schedule."
270
+ end
271
+ optparse.parse!(args)
272
+ if args.count != 1
273
+ print_error Morpheus::Terminal.angry_prompt
274
+ puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
275
+ return 1
276
+ end
277
+ connect(options)
278
+ begin
279
+ schedule = find_schedule_by_name_or_id(args[0])
280
+ if schedule.nil?
281
+ return 1
282
+ end
283
+ # construct payload
284
+ payload = nil
285
+ if options[:payload]
286
+ payload = options[:payload]
287
+ else
288
+ # merge -O options into normally parsed options
289
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
290
+ payload = {'schedule' => params}
291
+ end
292
+ if options[:dry_run]
293
+ print_dry_run @execute_schedules_interface.dry.update(schedule["id"], payload)
294
+ return
295
+ end
296
+ json_response = @execute_schedules_interface.update(schedule["id"], payload)
297
+ if options[:json]
298
+ puts as_json(json_response, options)
299
+ elsif !options[:quiet]
300
+ print_green_success "Updated execute schedule #{schedule['name']}"
301
+ _get(schedule['id'], {})
302
+ end
303
+ return 0
304
+ rescue RestClient::Exception => e
305
+ print_rest_exception(e, options)
306
+ return 1
307
+ end
308
+ end
309
+
310
+ def remove(args)
311
+ options = {}
312
+ params = {}
313
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
314
+ opts.banner = subcommand_usage("[name]")
315
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :auto_confirm])
316
+ end
317
+ optparse.parse!(args)
318
+ if args.count < 1
319
+ puts optparse
320
+ return 127
321
+ end
322
+ connect(options)
323
+
324
+ begin
325
+ schedule = find_schedule_by_name_or_id(args[0])
326
+ if schedule.nil?
327
+ return 1
328
+ end
329
+
330
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to delete execute schedule '#{schedule['name']}'?", options)
331
+ return false
332
+ end
333
+
334
+ # payload = {
335
+ # 'schedule' => {id: schedule["id"]}
336
+ # }
337
+ # payload['schedule'].merge!(schedule)
338
+ payload = params
339
+
340
+ if options[:dry_run]
341
+ print_dry_run @execute_schedules_interface.dry.destroy(schedule["id"])
342
+ return
343
+ end
344
+
345
+ json_response = @execute_schedules_interface.destroy(schedule["id"])
346
+ if options[:json]
347
+ puts as_json(json_response, options)
348
+ elsif !options[:quiet]
349
+ print_green_success "Deleted execute schedule #{schedule['name']}"
350
+ end
351
+ return 0, nil
352
+ rescue RestClient::Exception => e
353
+ print_rest_exception(e, options)
354
+ return 1
355
+ end
356
+ end
357
+
358
+ def add_instances(args)
359
+ options = {}
360
+ params = {}
361
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
362
+ opts.banner = subcommand_usage("[name] [instance]")
363
+ build_common_options(opts, options, [:payload, :json, :dry_run, :remote, :quiet])
364
+ opts.footer = "Assign instances to a execute schedule.\n" +
365
+ "[name] is required. This is the name or id of a execute schedule.\n" +
366
+ "[instance] is required. This is the name or id of an instance. More than one can be passed."
367
+ end
368
+ optparse.parse!(args)
369
+ if args.count < 2
370
+ puts optparse
371
+ return 1
372
+ end
373
+ connect(options)
374
+ begin
375
+ schedule = find_schedule_by_name_or_id(args[0])
376
+ if schedule.nil?
377
+ return 1
378
+ end
379
+
380
+ # construct payload
381
+ payload = nil
382
+ if options[:payload]
383
+ payload = options[:payload]
384
+ else
385
+ instance_ids = args[1..-1]
386
+ instances = []
387
+ instance_ids.each do |instance_id|
388
+ instance = find_instance_by_name_or_id(instance_id)
389
+ return 1 if instance.nil?
390
+ instances << instance
391
+ end
392
+ payload = {'instances' => instances.collect {|it| it['id'] } }
393
+ end
394
+ if options[:dry_run]
395
+ print_dry_run @execute_schedules_interface.dry.add_instances(schedule["id"], payload)
396
+ return 0
397
+ end
398
+ json_response = @execute_schedules_interface.add_instances(schedule["id"], payload)
399
+ if options[:json]
400
+ puts as_json(json_response, options)
401
+ elsif !options[:quiet]
402
+ if instances.size == 1
403
+ print_green_success "Added #{instances[0]['name']} to execute schedule #{schedule['name']}"
404
+ else
405
+ print_green_success "Added #{instances.size} instances to execute schedule #{schedule['name']}"
406
+ end
407
+ _get(schedule['id'], {})
408
+ end
409
+ return 0
410
+ rescue RestClient::Exception => e
411
+ print_rest_exception(e, options)
412
+ return 1
413
+ end
414
+ end
415
+
416
+ def remove_instances(args)
417
+ options = {}
418
+ params = {}
419
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
420
+ opts.banner = subcommand_usage("[name] [instance]")
421
+ build_common_options(opts, options, [:payload, :json, :dry_run, :remote, :quiet])
422
+ opts.footer = "Remove instances from a execute schedule.\n" +
423
+ "[name] is required. This is the name or id of a execute schedule.\n" +
424
+ "[instance] is required. This is the name or id of an instance. More than one can be passed."
425
+ end
426
+ optparse.parse!(args)
427
+ if args.count < 2
428
+ puts optparse
429
+ return 1
430
+ end
431
+ connect(options)
432
+ begin
433
+ schedule = find_schedule_by_name_or_id(args[0])
434
+ if schedule.nil?
435
+ return 1
436
+ end
437
+
438
+ # construct payload
439
+ payload = nil
440
+ if options[:payload]
441
+ payload = options[:payload]
442
+ else
443
+ instance_ids = args[1..-1]
444
+ instances = []
445
+ instance_ids.each do |instance_id|
446
+ instance = find_instance_by_name_or_id(instance_id)
447
+ return 1 if instance.nil?
448
+ instances << instance
449
+ end
450
+ payload = {'instances' => instances.collect {|it| it['id'] } }
451
+ end
452
+ if options[:dry_run]
453
+ print_dry_run @execute_schedules_interface.dry.remove_instances(schedule["id"], payload)
454
+ return 0
455
+ end
456
+ json_response = @execute_schedules_interface.remove_instances(schedule["id"], payload)
457
+ if options[:json]
458
+ puts as_json(json_response, options)
459
+ elsif !options[:quiet]
460
+ if instances.size == 1
461
+ print_green_success "Removed instance #{instances[0]['name']} from execute schedule #{schedule['name']}"
462
+ else
463
+ print_green_success "Removed #{instances.size} instances from execute schedule #{schedule['name']}"
464
+ end
465
+ _get(schedule['id'], {})
466
+ end
467
+ return 0
468
+ rescue RestClient::Exception => e
469
+ print_rest_exception(e, options)
470
+ return 1
471
+ end
472
+ end
473
+
474
+ def add_hosts(args)
475
+ options = {}
476
+ params = {}
477
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
478
+ opts.banner = subcommand_usage("[name] [host]")
479
+ build_common_options(opts, options, [:payload, :json, :dry_run, :remote, :quiet])
480
+ opts.footer = "Assign hosts to a execute schedule.\n" +
481
+ "[name] is required. This is the name or id of a execute schedule.\n" +
482
+ "[host] is required. This is the name or id of a host. More than one can be passed."
483
+ end
484
+ optparse.parse!(args)
485
+ if args.count < 2
486
+ puts optparse
487
+ return 1
488
+ end
489
+ connect(options)
490
+ begin
491
+ schedule = find_schedule_by_name_or_id(args[0])
492
+ if schedule.nil?
493
+ return 1
494
+ end
495
+
496
+ # construct payload
497
+ payload = nil
498
+ if options[:payload]
499
+ payload = options[:payload]
500
+ else
501
+ server_ids = args[1..-1]
502
+ servers = []
503
+ server_ids.each do |server_id|
504
+ server = find_server_by_name_or_id(server_id)
505
+ return 1 if server.nil?
506
+ servers << server
507
+ end
508
+ payload = {'servers' => servers.collect {|it| it['id'] } }
509
+ end
510
+ if options[:dry_run]
511
+ print_dry_run @execute_schedules_interface.dry.add_servers(schedule["id"], payload)
512
+ return 0
513
+ end
514
+ json_response = @execute_schedules_interface.add_servers(schedule["id"], payload)
515
+ if options[:json]
516
+ puts as_json(json_response, options)
517
+ elsif !options[:quiet]
518
+ if servers.size == 1
519
+ print_green_success "Added host #{servers[0]['name']} to execute schedule #{schedule['name']}"
520
+ else
521
+ print_green_success "Added #{servers.size} hosts to execute schedule #{schedule['name']}"
522
+ end
523
+ _get(schedule['id'], {})
524
+ end
525
+ return 0
526
+ rescue RestClient::Exception => e
527
+ print_rest_exception(e, options)
528
+ return 1
529
+ end
530
+ end
531
+
532
+ def remove_hosts(args)
533
+ options = {}
534
+ params = {}
535
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
536
+ opts.banner = subcommand_usage("[name] [host]")
537
+ build_common_options(opts, options, [:payload, :json, :dry_run, :remote, :quiet])
538
+ opts.footer = "Remove hosts from a execute schedule.\n" +
539
+ "[name] is required. This is the name or id of a execute schedule.\n" +
540
+ "[host] is required. This is the name or id of a host. More than one can be passed."
541
+ end
542
+ optparse.parse!(args)
543
+ if args.count < 2
544
+ puts optparse
545
+ return 1
546
+ end
547
+ connect(options)
548
+ begin
549
+ schedule = find_schedule_by_name_or_id(args[0])
550
+ if schedule.nil?
551
+ return 1
552
+ end
553
+
554
+ # construct payload
555
+ payload = nil
556
+ if options[:payload]
557
+ payload = options[:payload]
558
+ else
559
+ server_ids = args[1..-1]
560
+ servers = []
561
+ server_ids.each do |server_id|
562
+ server = find_server_by_name_or_id(server_id)
563
+ return 1 if server.nil?
564
+ servers << server
565
+ end
566
+ payload = {'servers' => servers.collect {|it| it['id'] } }
567
+ end
568
+ if options[:dry_run]
569
+ print_dry_run @execute_schedules_interface.dry.remove_servers(schedule["id"], payload)
570
+ return 0
571
+ end
572
+ json_response = @execute_schedules_interface.remove_servers(schedule["id"], payload)
573
+ if options[:json]
574
+ puts as_json(json_response, options)
575
+ elsif !options[:quiet]
576
+ if servers.size == 1
577
+ print_green_success "Removed host #{servers[0]['name']} from execute schedule #{schedule['name']}"
578
+ else
579
+ print_green_success "Removed #{servers.size} hosts from execute schedule #{schedule['name']}"
580
+ end
581
+ _get(schedule['id'], {})
582
+ end
583
+ return 0
584
+ rescue RestClient::Exception => e
585
+ print_rest_exception(e, options)
586
+ return 1
587
+ end
588
+ end
589
+
590
+
591
+ private
592
+
593
+ def find_schedule_by_name_or_id(val)
594
+ if val.to_s =~ /\A\d{1,}\Z/
595
+ return find_schedule_by_id(val)
596
+ else
597
+ return find_schedule_by_name(val)
598
+ end
599
+ end
600
+
601
+ def find_schedule_by_id(id)
602
+ begin
603
+ json_response = @execute_schedules_interface.get(id.to_i)
604
+ return json_response['schedule']
605
+ rescue RestClient::Exception => e
606
+ if e.response && e.response.code == 404
607
+ print_red_alert "Execute Schedule not found by id #{id}"
608
+ else
609
+ raise e
610
+ end
611
+ end
612
+ end
613
+
614
+ def find_schedule_by_name(name)
615
+ schedules = @execute_schedules_interface.list({name: name.to_s})['schedules']
616
+ if schedules.empty?
617
+ print_red_alert "Execute Schedule not found by name #{name}"
618
+ return nil
619
+ elsif schedules.size > 1
620
+ print_red_alert "#{schedules.size} execute schedules found by name #{name}"
621
+ print_schedules_table(schedules, {color: red})
622
+ print_red_alert "Try using ID instead"
623
+ print reset,"\n"
624
+ return nil
625
+ else
626
+ return schedules[0]
627
+ end
628
+ end
629
+
630
+ def print_schedules_table(schedules, opts={})
631
+ columns = [
632
+ {"ID" => lambda {|schedule| schedule['id'] } },
633
+ {"NAME" => lambda {|schedule| schedule['name'] } },
634
+ {"DESCRIPTION" => lambda {|schedule| schedule['description'] } },
635
+ {"CRON" => lambda {|schedule| schedule['cron'] } },
636
+ #{"TYPE" => lambda {|schedule| format_schedule_type(schedule['scheduleType']) } },
637
+ #{"TIMES" => lambda {|schedule| format_schedule_days_short(schedule) } },
638
+ ]
639
+ if opts[:include_fields]
640
+ columns = opts[:include_fields]
641
+ end
642
+ print as_pretty_table(schedules, columns, opts)
643
+ end
644
+
645
+ def format_schedule_type(val)
646
+ case val.to_s.downcase
647
+ when "execute" then "Execute"
648
+ else
649
+ val.to_s #.capitalize
650
+ end
651
+ end
652
+
653
+ def find_instance_by_name_or_id(val)
654
+ if val.to_s =~ /\A\d{1,}\Z/
655
+ return find_instance_by_id(val)
656
+ else
657
+ return find_instance_by_name(val)
658
+ end
659
+ end
660
+
661
+ def find_instance_by_id(id)
662
+ begin
663
+ json_response = @instances_interface.get(id.to_i)
664
+ return json_response['instance']
665
+ rescue RestClient::Exception => e
666
+ if e.response && e.response.code == 404
667
+ print_red_alert "Instance not found by id #{id}"
668
+ else
669
+ raise e
670
+ end
671
+ end
672
+ end
673
+
674
+ def find_instance_by_name(name)
675
+ instances = @instances_interface.get({name: name.to_s})['instances']
676
+ if instances.empty?
677
+ print_red_alert "Instance not found by name #{name}"
678
+ return nil
679
+ elsif instances.size > 1
680
+ print_red_alert "#{instances.size} instances found by name #{name}"
681
+ as_pretty_table(instances, [:id, :name], {color: red})
682
+ print_red_alert "Try using ID instead"
683
+ print reset,"\n"
684
+ return nil
685
+ else
686
+ return instances[0]
687
+ end
688
+ end
689
+
690
+ def find_server_by_name_or_id(val)
691
+ if val.to_s =~ /\A\d{1,}\Z/
692
+ return find_server_by_id(val)
693
+ else
694
+ return find_server_by_name(val)
695
+ end
696
+ end
697
+
698
+ def find_server_by_id(id)
699
+ begin
700
+ json_response = @servers_interface.get(id.to_i)
701
+ return json_response['server']
702
+ rescue RestClient::Exception => e
703
+ if e.response && e.response.code == 404
704
+ print_red_alert "Server not found by id #{id}"
705
+ else
706
+ raise e
707
+ end
708
+ end
709
+ end
710
+
711
+ def find_server_by_name(name)
712
+ servers = @servers_interface.list({name: name.to_s})['servers']
713
+ if servers.empty?
714
+ print_red_alert "Host not found by name #{name}"
715
+ return nil
716
+ elsif servers.size > 1
717
+ print_red_alert "#{servers.size} hosts found by name #{name}"
718
+ as_pretty_table(servers, [:id, :name], {color: red})
719
+ print_red_alert "Try using ID instead"
720
+ print reset,"\n"
721
+ return nil
722
+ else
723
+ return servers[0]
724
+ end
725
+ end
726
+
727
+ end