morpheus-cli 3.5.1.1 → 3.5.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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