morpheus-cli 4.1.8 → 4.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +24 -0
- data/lib/morpheus/api/{old_cypher_interface.rb → budgets_interface.rb} +10 -11
- data/lib/morpheus/api/cloud_datastores_interface.rb +7 -0
- data/lib/morpheus/api/cloud_resource_pools_interface.rb +2 -2
- data/lib/morpheus/api/cypher_interface.rb +18 -12
- data/lib/morpheus/api/health_interface.rb +72 -0
- data/lib/morpheus/api/instances_interface.rb +1 -1
- data/lib/morpheus/api/library_instance_types_interface.rb +7 -0
- data/lib/morpheus/api/log_settings_interface.rb +6 -0
- data/lib/morpheus/api/network_security_servers_interface.rb +30 -0
- data/lib/morpheus/api/price_sets_interface.rb +42 -0
- data/lib/morpheus/api/prices_interface.rb +68 -0
- data/lib/morpheus/api/provisioning_settings_interface.rb +29 -0
- data/lib/morpheus/api/servers_interface.rb +1 -1
- data/lib/morpheus/api/service_plans_interface.rb +34 -11
- data/lib/morpheus/api/task_sets_interface.rb +8 -0
- data/lib/morpheus/api/tasks_interface.rb +8 -0
- data/lib/morpheus/cli.rb +6 -3
- data/lib/morpheus/cli/appliance_settings_command.rb +13 -5
- data/lib/morpheus/cli/approvals_command.rb +1 -1
- data/lib/morpheus/cli/apps.rb +88 -28
- data/lib/morpheus/cli/backup_settings_command.rb +1 -1
- data/lib/morpheus/cli/blueprints_command.rb +2 -0
- data/lib/morpheus/cli/budgets_command.rb +672 -0
- data/lib/morpheus/cli/cli_command.rb +13 -2
- data/lib/morpheus/cli/cli_registry.rb +1 -0
- data/lib/morpheus/cli/clusters.rb +40 -274
- data/lib/morpheus/cli/commands/standard/benchmark_command.rb +114 -66
- data/lib/morpheus/cli/commands/standard/coloring_command.rb +12 -0
- data/lib/morpheus/cli/commands/standard/curl_command.rb +31 -6
- data/lib/morpheus/cli/commands/standard/echo_command.rb +8 -3
- data/lib/morpheus/cli/commands/standard/set_prompt_command.rb +1 -1
- data/lib/morpheus/cli/containers_command.rb +37 -24
- data/lib/morpheus/cli/cypher_command.rb +191 -150
- data/lib/morpheus/cli/health_command.rb +903 -0
- data/lib/morpheus/cli/hosts.rb +43 -32
- data/lib/morpheus/cli/instances.rb +119 -68
- data/lib/morpheus/cli/jobs_command.rb +1 -1
- data/lib/morpheus/cli/library_instance_types_command.rb +61 -11
- data/lib/morpheus/cli/library_option_types_command.rb +2 -2
- data/lib/morpheus/cli/log_settings_command.rb +46 -3
- data/lib/morpheus/cli/logs_command.rb +24 -17
- data/lib/morpheus/cli/mixins/accounts_helper.rb +2 -0
- data/lib/morpheus/cli/mixins/logs_helper.rb +73 -19
- data/lib/morpheus/cli/mixins/print_helper.rb +29 -1
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +554 -96
- data/lib/morpheus/cli/mixins/whoami_helper.rb +13 -1
- data/lib/morpheus/cli/networks_command.rb +3 -0
- data/lib/morpheus/cli/option_types.rb +83 -53
- data/lib/morpheus/cli/price_sets_command.rb +543 -0
- data/lib/morpheus/cli/prices_command.rb +669 -0
- data/lib/morpheus/cli/processes_command.rb +0 -2
- data/lib/morpheus/cli/provisioning_settings_command.rb +237 -0
- data/lib/morpheus/cli/remote.rb +9 -4
- data/lib/morpheus/cli/reports_command.rb +10 -4
- data/lib/morpheus/cli/roles.rb +93 -38
- data/lib/morpheus/cli/security_groups.rb +10 -0
- data/lib/morpheus/cli/service_plans_command.rb +736 -0
- data/lib/morpheus/cli/tasks.rb +220 -8
- data/lib/morpheus/cli/tenants_command.rb +3 -16
- data/lib/morpheus/cli/users.rb +2 -25
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/whitelabel_settings_command.rb +18 -18
- data/lib/morpheus/cli/whoami.rb +28 -10
- data/lib/morpheus/cli/workflows.rb +488 -36
- data/lib/morpheus/formatters.rb +22 -0
- data/morpheus-cli.gemspec +1 -0
- metadata +28 -5
- data/lib/morpheus/cli/accounts.rb +0 -335
- data/lib/morpheus/cli/old_cypher_command.rb +0 -412
data/lib/morpheus/cli/whoami.rb
CHANGED
@@ -26,7 +26,7 @@ class Morpheus::Cli::Whoami
|
|
26
26
|
# begin
|
27
27
|
@api_client = establish_remote_appliance_connection(opts.merge({:no_prompt => true, :skip_verify_access_token => true}))
|
28
28
|
@groups_interface = @api_client.groups
|
29
|
-
@active_group_id = Morpheus::Cli::Groups.
|
29
|
+
@active_group_id = Morpheus::Cli::Groups.active_groups[@appliance_name]
|
30
30
|
# rescue Morpheus::Cli::CommandError => err
|
31
31
|
# puts_error err
|
32
32
|
# end
|
@@ -46,6 +46,9 @@ class Morpheus::Cli::Whoami
|
|
46
46
|
opts.on( '-n', '--name', "Print only your username." ) do
|
47
47
|
username_only = true
|
48
48
|
end
|
49
|
+
opts.on('-a','--all', "Display All Details (Feature Access)") do
|
50
|
+
options[:include_feature_access] = true
|
51
|
+
end
|
49
52
|
opts.on('-f','--feature-access', "Display Feature Access") do
|
50
53
|
options[:include_feature_access] = true
|
51
54
|
end
|
@@ -59,12 +62,12 @@ class Morpheus::Cli::Whoami
|
|
59
62
|
# opts.on(nil,'--instance-type-access', "Display Instance Type Access") do
|
60
63
|
# options[:include_instance_type_access] = true
|
61
64
|
# end
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
65
|
+
opts.on('-a','--all-access', "Display All Access Lists") do
|
66
|
+
options[:include_feature_access] = true
|
67
|
+
options[:include_group_access] = true
|
68
|
+
options[:include_cloud_access] = true
|
69
|
+
options[:include_instance_type_access] = true
|
70
|
+
end
|
68
71
|
opts.on('-t','--token-only', "Print your access token only") do
|
69
72
|
access_token_only = true
|
70
73
|
end
|
@@ -188,10 +191,25 @@ class Morpheus::Cli::Whoami
|
|
188
191
|
if @user_permissions
|
189
192
|
print_h2 "Feature Permissions", options
|
190
193
|
print cyan
|
191
|
-
|
192
|
-
|
194
|
+
begin
|
195
|
+
rows = []
|
196
|
+
if @user_permissions.is_a?(Hash)
|
197
|
+
# api used to return map like [code:access]
|
198
|
+
rows = @user_permissions.collect do |code, access|
|
199
|
+
{permission: code, access: get_access_string(access) }
|
200
|
+
end
|
201
|
+
else
|
202
|
+
# api now returns an array of objects like [[name:"Foo",code:"foo",access:"full"], ...]
|
203
|
+
rows = @user_permissions.collect do |it|
|
204
|
+
{permission: (it['name'] || it['code']), access: get_access_string(it['access']) }
|
205
|
+
end
|
206
|
+
end
|
207
|
+
# api sort sux right now
|
208
|
+
rows = rows.sort {|a,b| a[:permission] <=> b[:permission] }
|
209
|
+
print as_pretty_table(rows, [:permission, :access], options)
|
210
|
+
rescue => ex
|
211
|
+
puts_error "Failed to parse feature permissions: #{ex}"
|
193
212
|
end
|
194
|
-
print as_pretty_table(rows, [:code, :access], options)
|
195
213
|
else
|
196
214
|
puts yellow,"No permissions found.",reset
|
197
215
|
end
|
@@ -6,7 +6,7 @@ require 'morpheus/cli/cli_command'
|
|
6
6
|
class Morpheus::Cli::Workflows
|
7
7
|
include Morpheus::Cli::CliCommand
|
8
8
|
|
9
|
-
register_subcommands :list, :get, :add, :update, :remove
|
9
|
+
register_subcommands :list, :get, :add, :update, :remove, :execute
|
10
10
|
set_default_subcommand :list
|
11
11
|
|
12
12
|
# def initialize()
|
@@ -15,8 +15,11 @@ class Morpheus::Cli::Workflows
|
|
15
15
|
|
16
16
|
def connect(opts)
|
17
17
|
@api_client = establish_remote_appliance_connection(opts)
|
18
|
-
@
|
19
|
-
@
|
18
|
+
@task_sets_interface = @api_client.task_sets
|
19
|
+
@tasks_interface = @api_client.tasks
|
20
|
+
@option_types_interface = @api_client.option_types
|
21
|
+
@instances_interface = @api_client.instances
|
22
|
+
@servers_interface = @api_client.servers
|
20
23
|
end
|
21
24
|
|
22
25
|
|
@@ -77,54 +80,185 @@ class Morpheus::Cli::Workflows
|
|
77
80
|
def add(args)
|
78
81
|
options = {}
|
79
82
|
params = {}
|
83
|
+
tasks = nil
|
80
84
|
task_arg_list = nil
|
85
|
+
option_types = nil
|
86
|
+
option_type_arg_list = nil
|
87
|
+
workflow_type = nil # 'provision'
|
81
88
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
82
89
|
opts.banner = subcommand_usage("[name] --tasks taskId:phase,taskId2:phase,taskId3:phase")
|
83
90
|
opts.on("--name NAME", String, "Name for workflow") do |val|
|
84
91
|
params['name'] = val
|
85
92
|
end
|
86
|
-
opts.on("--
|
93
|
+
opts.on("--description DESCRIPTION", String, "Description of workflow") do |val|
|
94
|
+
params['description'] = val
|
95
|
+
end
|
96
|
+
opts.on("-t", "--type TYPE", "Type of workflow. i.e. provision or operation. Default is provision.") do |val|
|
97
|
+
workflow_type = val.to_s.downcase
|
98
|
+
if workflow_type.include?('provision')
|
99
|
+
workflow_type = 'provision'
|
100
|
+
elsif workflow_type.include?('operation')
|
101
|
+
workflow_type = 'operation'
|
102
|
+
end
|
103
|
+
params['type'] = workflow_type
|
104
|
+
end
|
105
|
+
opts.on("--operational", "--operational", "Create an operational workflow, alias for --type operational.") do |val|
|
106
|
+
workflow_type = 'operation'
|
107
|
+
params['type'] = workflow_type
|
108
|
+
end
|
109
|
+
opts.on("--tasks [x,y,z]", Array, "List of tasks to run in order, in the format <Task ID>:<Task Phase> Task Phase is optional. Default is the same workflow type: 'provision' or 'operation'.") do |list|
|
87
110
|
task_arg_list = []
|
88
111
|
list.each do |it|
|
89
112
|
task_id, task_phase = it.split(":")
|
90
113
|
task_arg_list << {task_id: task_id.to_s.strip, task_phase: task_phase.to_s.strip}
|
114
|
+
end if list
|
115
|
+
end
|
116
|
+
opts.on("--option-types x,y,z", Array, "List of option type name or IDs. For use with operational workflows to add configuration during execution.") do |list|
|
117
|
+
option_type_arg_list = []
|
118
|
+
list.each do |it|
|
119
|
+
option_type_arg_list << {option_type_id: it.to_s.strip}
|
91
120
|
end
|
92
121
|
end
|
122
|
+
opts.on('--platform [PLATFORM]', String, "Platform, eg. linux, windows or osx") do |val|
|
123
|
+
params['platform'] = val.to_s.empty? ? nil : val.to_s.downcase
|
124
|
+
end
|
125
|
+
opts.on('--allow-custom-config [on|off]', String, "Allow Custom Config") do |val|
|
126
|
+
params['allowCustomConfig'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
127
|
+
end
|
128
|
+
opts.on('--visibility VISIBILITY', String, "Visibility, eg. private or public") do |val|
|
129
|
+
params['visibility'] = val.to_s.downcase
|
130
|
+
end
|
93
131
|
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
|
94
132
|
end
|
95
133
|
optparse.parse!(args)
|
96
|
-
|
134
|
+
if args.count > 1
|
135
|
+
raise_command_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
136
|
+
end
|
97
137
|
connect(options)
|
98
138
|
begin
|
139
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
99
140
|
payload = nil
|
100
141
|
if options[:payload]
|
101
142
|
payload = options[:payload]
|
143
|
+
payload.deep_merge!({'taskSet' => passed_options}) unless passed_options.empty?
|
102
144
|
else
|
103
|
-
|
145
|
+
params.deep_merge!(passed_options) unless passed_options.empty?
|
146
|
+
if args[0]
|
104
147
|
params['name'] = args[0]
|
105
148
|
end
|
106
|
-
if
|
107
|
-
|
108
|
-
|
149
|
+
# if params['name'].to_s.empty?
|
150
|
+
# puts_error "#{Morpheus::Terminal.angry_prompt}missing required option: [name]\n#{optparse}"
|
151
|
+
# return 1
|
152
|
+
# end
|
153
|
+
# if task_arg_list.nil?
|
154
|
+
# puts_error "#{Morpheus::Terminal.angry_prompt}missing required option: --tasks\n#{optparse}"
|
155
|
+
# return 1
|
156
|
+
# end
|
157
|
+
|
158
|
+
# Name
|
159
|
+
if params['name'].nil?
|
160
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Name'}], options[:options], @api_client)
|
161
|
+
params['name'] = v_prompt['name'] unless v_prompt['name'].to_s.empty?
|
109
162
|
end
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
163
|
+
|
164
|
+
# Description
|
165
|
+
if params['description'].nil?
|
166
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'description' => 'Description'}], options[:options], @api_client)
|
167
|
+
params['description'] = v_prompt['description'] unless v_prompt['description'].to_s.empty?
|
168
|
+
end
|
169
|
+
|
170
|
+
# Type
|
171
|
+
if workflow_type.nil?
|
172
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => get_available_workflow_types(), 'required' => true, 'description' => 'Workflow Type', 'defaultValue' => workflow_type || 'provision'}], options[:options], @api_client)
|
173
|
+
workflow_type = v_prompt['type'] unless v_prompt['type'].to_s.empty?
|
174
|
+
params['type'] = workflow_type
|
175
|
+
end
|
176
|
+
|
177
|
+
# Tasks
|
178
|
+
while tasks.nil? do
|
179
|
+
if task_arg_list.nil?
|
180
|
+
tasks_val = nil
|
181
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tasks', 'fieldLabel' => 'Tasks', 'type' => 'text', 'required' => false, 'description' => "List of tasks to run in order, in the format <Task ID>:<Task Phase> Task Phase is optional. Default is the same workflow type: 'provision' or 'operation'."}], options[:options], @api_client)
|
182
|
+
tasks_val = v_prompt['tasks'] unless v_prompt['tasks'].to_s.empty?
|
183
|
+
if tasks_val
|
184
|
+
task_arg_list = []
|
185
|
+
tasks_val.split(",").each do |it|
|
186
|
+
task_id, task_phase = it.split(":")
|
187
|
+
task_arg_list << {task_id: task_id.to_s.strip, task_phase: task_phase.to_s.strip}
|
188
|
+
end
|
189
|
+
else
|
190
|
+
# empty array is allowed
|
191
|
+
tasks = []
|
192
|
+
end
|
193
|
+
end
|
194
|
+
if task_arg_list
|
195
|
+
tasks = []
|
196
|
+
task_arg_list.each do |task_arg|
|
197
|
+
found_task = find_task_by_name_or_id(task_arg[:task_id])
|
198
|
+
#return 1 if found_task.nil?
|
199
|
+
if found_task.nil?
|
200
|
+
task_arg_list = nil
|
201
|
+
tasks = nil
|
202
|
+
break
|
203
|
+
end
|
204
|
+
row = {'taskId' => found_task['id']}
|
205
|
+
if !task_arg[:task_phase].to_s.strip.empty?
|
206
|
+
row['taskPhase'] = task_arg[:task_phase]
|
207
|
+
elsif workflow_type == 'operation'
|
208
|
+
row['taskPhase'] = 'operation'
|
209
|
+
end
|
210
|
+
tasks << row
|
211
|
+
end
|
212
|
+
else
|
213
|
+
if options[:no_prompt]
|
214
|
+
# empty array is allowed
|
215
|
+
tasks = []
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Option Types
|
221
|
+
if workflow_type == 'operation'
|
222
|
+
while option_types.nil? do
|
223
|
+
if option_type_arg_list.nil?
|
224
|
+
option_types_val = nil
|
225
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'optionTypes', 'fieldLabel' => 'Option Types', 'type' => 'text', 'description' => "List of option type name or IDs. For use with operational workflows to add configuration during execution."}], options[:options], @api_client)
|
226
|
+
option_types_val = v_prompt['optionTypes'] unless v_prompt['optionTypes'].to_s.empty?
|
227
|
+
if option_types_val
|
228
|
+
option_type_arg_list = []
|
229
|
+
option_types_val.split(",").each do |it|
|
230
|
+
option_type_arg_list << {option_type_id: it.to_s.strip}
|
231
|
+
end
|
232
|
+
else
|
233
|
+
option_types = [] # not required, break out
|
234
|
+
end
|
235
|
+
end
|
236
|
+
if option_type_arg_list
|
237
|
+
option_types = []
|
238
|
+
option_type_arg_list.each do |option_type_arg|
|
239
|
+
found_option_type = find_option_type_by_name_or_id(option_type_arg[:option_type_id])
|
240
|
+
#return 1 if found_option_type.nil?
|
241
|
+
if found_option_type.nil?
|
242
|
+
option_type_arg_list = nil
|
243
|
+
option_types = nil
|
244
|
+
break
|
245
|
+
end
|
246
|
+
option_types << found_option_type['id']
|
247
|
+
end
|
118
248
|
end
|
119
|
-
tasks << row
|
120
249
|
end
|
121
250
|
end
|
251
|
+
|
122
252
|
payload = {'taskSet' => {}}
|
123
253
|
params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
254
|
+
# params['type'] = workflow_type
|
124
255
|
payload['taskSet'].deep_merge!(params)
|
125
|
-
if
|
256
|
+
if tasks
|
126
257
|
payload['taskSet']['tasks'] = tasks
|
127
258
|
end
|
259
|
+
if option_types && option_types.size > 0
|
260
|
+
payload['taskSet']['optionTypes'] = option_types
|
261
|
+
end
|
128
262
|
end
|
129
263
|
@task_sets_interface.setopts(options)
|
130
264
|
if options[:dry_run]
|
@@ -136,7 +270,7 @@ class Morpheus::Cli::Workflows
|
|
136
270
|
print JSON.pretty_generate(json_response)
|
137
271
|
else
|
138
272
|
workflow = json_response['taskSet']
|
139
|
-
|
273
|
+
print_green_success "Workflow #{workflow['name']} created"
|
140
274
|
get([workflow['id']])
|
141
275
|
end
|
142
276
|
rescue RestClient::Exception => e
|
@@ -205,6 +339,10 @@ class Morpheus::Cli::Workflows
|
|
205
339
|
"ID" => 'id',
|
206
340
|
"Name" => 'name',
|
207
341
|
"Description" => 'description',
|
342
|
+
"Type" => lambda {|workflow| format_workflow_type(workflow) },
|
343
|
+
"Platform" => lambda {|it| format_platform(it['platform']) },
|
344
|
+
"Allow Custom Config" => lambda {|it| format_boolean(it['allowCustomConfig']) },
|
345
|
+
"Visibility" => lambda {|it| it['visibility'].to_s.capitalize },
|
208
346
|
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
209
347
|
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
210
348
|
}
|
@@ -228,9 +366,26 @@ class Morpheus::Cli::Workflows
|
|
228
366
|
{"PHASE" => lambda {|it| it['taskPhase'] } }, # not returned yet?
|
229
367
|
]
|
230
368
|
print cyan
|
231
|
-
|
369
|
+
print as_pretty_table(tasks, task_set_task_columns)
|
370
|
+
end
|
371
|
+
|
372
|
+
workflow_option_types = workflow['optionTypes']
|
373
|
+
|
374
|
+
if workflow_option_types && workflow_option_types.size() > 0
|
375
|
+
print_h2 "Workflow Option Types"
|
376
|
+
columns = [
|
377
|
+
{"ID" => lambda {|it| it['id'] } },
|
378
|
+
{"NAME" => lambda {|it| it['name'] } },
|
379
|
+
{"TYPE" => lambda {|it| it['type'] } },
|
380
|
+
{"FIELD NAME" => lambda {|it| it['fieldName'] } },
|
381
|
+
{"FIELD LABEL" => lambda {|it| it['fieldLabel'] } },
|
382
|
+
{"DEFAULT" => lambda {|it| it['defaultValue'] } },
|
383
|
+
{"REQUIRED" => lambda {|it| format_boolean it['required'] } },
|
384
|
+
]
|
385
|
+
print as_pretty_table(workflow_option_types, columns)
|
232
386
|
end
|
233
387
|
print reset
|
388
|
+
print "\n"
|
234
389
|
end
|
235
390
|
rescue RestClient::Exception => e
|
236
391
|
print_rest_exception(e, options)
|
@@ -241,25 +396,45 @@ class Morpheus::Cli::Workflows
|
|
241
396
|
def update(args)
|
242
397
|
options = {}
|
243
398
|
params = {}
|
399
|
+
tasks = nil
|
244
400
|
task_arg_list = nil
|
401
|
+
option_types = nil
|
402
|
+
option_type_arg_list = nil
|
245
403
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
246
404
|
opts.banner = subcommand_usage("[name] --tasks taskId:phase,taskId2:phase,taskId3:phase")
|
247
|
-
opts.on("--
|
405
|
+
opts.on("--name NAME", String, "New name for workflow") do |val|
|
406
|
+
params['name'] = val
|
407
|
+
end
|
408
|
+
opts.on("--description DESCRIPTION", String, "Description of workflow") do |val|
|
409
|
+
params['description'] = val
|
410
|
+
end
|
411
|
+
opts.on("--tasks [x,y,z]", Array, "List of tasks to run in order, in the format <Task ID>:<Task Phase> Task Phase is optional. Default is the same workflow type: 'provision' or 'operation'.") do |list|
|
248
412
|
task_arg_list = []
|
249
413
|
list.each do |it|
|
250
414
|
task_id, task_phase = it.split(":")
|
251
415
|
task_arg_list << {task_id: task_id.to_s.strip, task_phase: task_phase.to_s.strip}
|
252
|
-
end
|
416
|
+
end if list
|
253
417
|
end
|
254
|
-
opts.on("--
|
255
|
-
|
418
|
+
opts.on("--option-types [x,y,z]", Array, "List of option type name or IDs. For use with operational workflows to add configuration during execution.") do |list|
|
419
|
+
option_type_arg_list = []
|
420
|
+
list.each do |it|
|
421
|
+
option_type_arg_list << {option_type_id: it.to_s.strip}
|
422
|
+
end if list
|
423
|
+
end
|
424
|
+
opts.on('--platform [PLATFORM]', String, "Platform, eg. linux, windows or osx") do |val|
|
425
|
+
params['platform'] = val.to_s.empty? ? nil : val.to_s.downcase
|
426
|
+
end
|
427
|
+
opts.on('--allow-custom-config [on|off]', String, "Allow Custom Config") do |val|
|
428
|
+
params['allowCustomConfig'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
|
429
|
+
end
|
430
|
+
opts.on('--visibility VISIBILITY', String, "Visibility, eg. private or public") do |val|
|
431
|
+
params['visibility'] = val.to_s.downcase
|
256
432
|
end
|
257
433
|
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
|
258
434
|
end
|
259
435
|
optparse.parse!(args)
|
260
|
-
if args.count
|
261
|
-
|
262
|
-
exit 1
|
436
|
+
if args.count != 1
|
437
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
263
438
|
end
|
264
439
|
workflow_name = args[0]
|
265
440
|
connect(options)
|
@@ -270,8 +445,8 @@ class Morpheus::Cli::Workflows
|
|
270
445
|
if options[:payload]
|
271
446
|
payload = options[:payload]
|
272
447
|
else
|
273
|
-
tasks = []
|
274
448
|
if task_arg_list
|
449
|
+
tasks = []
|
275
450
|
task_arg_list.each do |task_arg|
|
276
451
|
found_task = find_task_by_name_or_id(task_arg[:task_id])
|
277
452
|
return 1 if found_task.nil?
|
@@ -282,12 +457,23 @@ class Morpheus::Cli::Workflows
|
|
282
457
|
tasks << row
|
283
458
|
end
|
284
459
|
end
|
460
|
+
if option_type_arg_list
|
461
|
+
option_types = []
|
462
|
+
option_type_arg_list.each do |option_type_arg|
|
463
|
+
found_option_type = find_option_type_by_name_or_id(option_type_arg[:option_type_id])
|
464
|
+
return 1 if found_option_type.nil?
|
465
|
+
option_types << found_option_type['id']
|
466
|
+
end
|
467
|
+
end
|
285
468
|
payload = {'taskSet' => {}}
|
286
469
|
params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
287
470
|
payload['taskSet'].deep_merge!(params)
|
288
|
-
if
|
471
|
+
if tasks
|
289
472
|
payload['taskSet']['tasks'] = tasks
|
290
473
|
end
|
474
|
+
if option_types
|
475
|
+
payload['taskSet']['optionTypes'] = option_types
|
476
|
+
end
|
291
477
|
end
|
292
478
|
@task_sets_interface.setopts(options)
|
293
479
|
if options[:dry_run]
|
@@ -298,7 +484,7 @@ class Morpheus::Cli::Workflows
|
|
298
484
|
if options[:json]
|
299
485
|
print JSON.pretty_generate(json_response)
|
300
486
|
elsif !options[:quiet]
|
301
|
-
|
487
|
+
print_green_success "Workflow #{json_response['taskSet']['name']} updated"
|
302
488
|
get([workflow['id']])
|
303
489
|
end
|
304
490
|
rescue RestClient::Exception => e
|
@@ -335,7 +521,7 @@ class Morpheus::Cli::Workflows
|
|
335
521
|
if options[:json]
|
336
522
|
print JSON.pretty_generate(json_response)
|
337
523
|
elsif !options[:quiet]
|
338
|
-
|
524
|
+
print_green_success "Workflow #{workflow['name']} removed"
|
339
525
|
end
|
340
526
|
rescue RestClient::Exception => e
|
341
527
|
print_rest_exception(e, options)
|
@@ -343,9 +529,141 @@ class Morpheus::Cli::Workflows
|
|
343
529
|
end
|
344
530
|
end
|
345
531
|
|
532
|
+
def execute(args)
|
533
|
+
params = {}
|
534
|
+
options = {}
|
535
|
+
target_type = nil
|
536
|
+
instance_ids = []
|
537
|
+
instances = []
|
538
|
+
server_ids = []
|
539
|
+
servers = []
|
540
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
541
|
+
opts.banner = subcommand_usage("[workflow] --instance [instance] [options]")
|
542
|
+
opts.on('--instance INSTANCE', String, "Instance name or id to execute the workflow on. This option can be passed more than once.") do |val|
|
543
|
+
target_type = 'instance'
|
544
|
+
instance_ids << val
|
545
|
+
end
|
546
|
+
opts.on('--instances [LIST]', Array, "Instances, comma separated list of instance names or IDs.") do |list|
|
547
|
+
target_type = 'instance'
|
548
|
+
instance_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
549
|
+
end
|
550
|
+
opts.on('--host HOST', String, "Host name or id to execute the workflow on. This option can be passed more than once.") do |val|
|
551
|
+
target_type = 'server'
|
552
|
+
server_ids << val
|
553
|
+
end
|
554
|
+
opts.on('--hosts [LIST]', Array, "Hosts, comma separated list of host names or IDs.") do |list|
|
555
|
+
target_type = 'server'
|
556
|
+
server_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
557
|
+
end
|
558
|
+
opts.on('--server HOST', String, "alias for --host") do |val|
|
559
|
+
target_type = 'server'
|
560
|
+
server_ids << val
|
561
|
+
end
|
562
|
+
opts.on('--servers [LIST]', Array, "alias for --hosts") do |list|
|
563
|
+
target_type = 'server'
|
564
|
+
server_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
|
565
|
+
end
|
566
|
+
opts.add_hidden_option('--server')
|
567
|
+
opts.add_hidden_option('--servers')
|
568
|
+
opts.on('--config [TEXT]', String, "Custom config") do |val|
|
569
|
+
params['customConfig'] = val.to_s
|
570
|
+
end
|
571
|
+
build_common_options(opts, options, [:payload, :options, :json, :dry_run, :remote])
|
572
|
+
end
|
573
|
+
optparse.parse!(args)
|
574
|
+
if args.count != 1
|
575
|
+
raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
|
576
|
+
end
|
577
|
+
workflow_name = args[0]
|
578
|
+
connect(options)
|
579
|
+
begin
|
580
|
+
workflow = find_workflow_by_name_or_id(workflow_name)
|
581
|
+
return 1 if workflow.nil?
|
582
|
+
|
583
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
584
|
+
payload = nil
|
585
|
+
if options[:payload]
|
586
|
+
payload = options[:payload]
|
587
|
+
payload.deep_merge!({'job' => passed_options}) unless passed_options.empty?
|
588
|
+
else
|
589
|
+
if instance_ids.size > 0 && server_ids.size > 0
|
590
|
+
raise_command_error "Pass --instance or --host, not both.\n#{optparse}"
|
591
|
+
elsif instance_ids.size > 0
|
592
|
+
instance_ids.each do |instance_id|
|
593
|
+
instance = find_instance_by_name_or_id(instance_id)
|
594
|
+
return 1 if instance.nil?
|
595
|
+
instances << instance
|
596
|
+
end
|
597
|
+
params['instances'] = instances.collect {|it| it['id'] }
|
598
|
+
elsif server_ids.size > 0
|
599
|
+
server_ids.each do |server_id|
|
600
|
+
server = find_server_by_name_or_id(server_id)
|
601
|
+
return 1 if server.nil?
|
602
|
+
servers << server
|
603
|
+
end
|
604
|
+
params['servers'] = servers.collect {|it| it['id'] }
|
605
|
+
else
|
606
|
+
raise_command_error "missing required option: --instance or --host\n#{optparse}"
|
607
|
+
end
|
608
|
+
|
609
|
+
# todo: prompt to workflow optionTypes for customOptions
|
610
|
+
custom_options = nil
|
611
|
+
if workflow['optionTypes'] && workflow['optionTypes'].size() > 0
|
612
|
+
custom_option_types = workflow['optionTypes'].collect {|it|
|
613
|
+
it['fieldContext'] = 'customOptions'
|
614
|
+
it
|
615
|
+
}
|
616
|
+
custom_options = Morpheus::Cli::OptionTypes.prompt(custom_option_types, options[:options], @api_client, {})
|
617
|
+
end
|
618
|
+
|
619
|
+
params['targetType'] = target_type
|
620
|
+
|
621
|
+
job_payload = {}
|
622
|
+
job_payload.deep_merge!(params)
|
623
|
+
passed_options.delete('customOptions')
|
624
|
+
job_payload.deep_merge!(passed_options) unless passed_options.empty?
|
625
|
+
if custom_options
|
626
|
+
# job_payload.deep_merge!('config' => custom_options)
|
627
|
+
job_payload.deep_merge!(custom_options)
|
628
|
+
end
|
629
|
+
payload = {'job' => job_payload}
|
630
|
+
end
|
631
|
+
|
632
|
+
@task_sets_interface.setopts(options)
|
633
|
+
if options[:dry_run]
|
634
|
+
print_dry_run @task_sets_interface.dry.run(workflow['id'], payload)
|
635
|
+
return 0
|
636
|
+
end
|
637
|
+
response = @task_sets_interface.run(workflow['id'], payload)
|
638
|
+
if options[:json]
|
639
|
+
print JSON.pretty_generate(json_response)
|
640
|
+
if !response['success']
|
641
|
+
return 1
|
642
|
+
end
|
643
|
+
else
|
644
|
+
target_desc = ""
|
645
|
+
if instances.size() > 0
|
646
|
+
target_desc = (instances.size() == 1) ? "instance #{instances[0]['name']}" : "#{instances.size()} instances"
|
647
|
+
elsif servers.size() > 0
|
648
|
+
target_desc = (servers.size() == 1) ? "host #{servers[0]['name']}" : "#{servers.size()} hosts"
|
649
|
+
end
|
650
|
+
print_green_success "Executing workflow #{workflow['name']} on #{target_desc}"
|
651
|
+
# todo: load job/execution
|
652
|
+
# get([workflow['id']])
|
653
|
+
end
|
654
|
+
return 0
|
655
|
+
rescue RestClient::Exception => e
|
656
|
+
print_rest_exception(e, options)
|
657
|
+
return 1
|
658
|
+
end
|
659
|
+
end
|
346
660
|
|
347
661
|
private
|
348
662
|
|
663
|
+
def get_available_workflow_types
|
664
|
+
[{"name" => "Provisioning", "value" => "provision", "isDefault" => true}, {"name" => "Operational", "value" => "operation"}]
|
665
|
+
end
|
666
|
+
|
349
667
|
def find_workflow_by_name_or_id(val)
|
350
668
|
if val.to_s =~ /\A\d{1,}\Z/
|
351
669
|
return find_workflow_by_id(val)
|
@@ -361,6 +679,7 @@ class Morpheus::Cli::Workflows
|
|
361
679
|
rescue RestClient::Exception => e
|
362
680
|
if e.response && e.response.code == 404
|
363
681
|
print_red_alert "Workflow not found by id #{id}"
|
682
|
+
return nil
|
364
683
|
else
|
365
684
|
raise e
|
366
685
|
end
|
@@ -397,6 +716,7 @@ class Morpheus::Cli::Workflows
|
|
397
716
|
rescue RestClient::Exception => e
|
398
717
|
if e.response && e.response.code == 404
|
399
718
|
print_red_alert "Task not found by id #{id}"
|
719
|
+
return nil
|
400
720
|
else
|
401
721
|
raise e
|
402
722
|
end
|
@@ -422,16 +742,148 @@ class Morpheus::Cli::Workflows
|
|
422
742
|
columns = [
|
423
743
|
{"ID" => lambda {|workflow| workflow['id'] } },
|
424
744
|
{"NAME" => lambda {|workflow| workflow['name'] } },
|
745
|
+
{"DESCRIPTION" => lambda {|workflow| workflow['description'] } },
|
746
|
+
{"TYPE" => lambda {|workflow| format_workflow_type(workflow) } },
|
425
747
|
{"TASKS" => lambda {|workflow|
|
426
|
-
(workflow['taskSetTasks'] || []).sort { |x,y| x['taskOrder'].to_i <=> y['taskOrder'].to_i }.collect { |taskSetTask|
|
427
|
-
|
428
|
-
}.join(', ')
|
748
|
+
# (workflow['taskSetTasks'] || []).sort { |x,y| x['taskOrder'].to_i <=> y['taskOrder'].to_i }.collect { |taskSetTask|
|
749
|
+
# taskSetTask['task']['name']
|
750
|
+
# }.join(', ')
|
751
|
+
(workflow['taskSetTasks'] || []).size.to_s
|
429
752
|
} },
|
430
|
-
{"
|
753
|
+
{"CREATED" => lambda {|workflow| format_local_dt(workflow['dateCreated']) } },
|
431
754
|
]
|
432
755
|
if opts[:include_fields]
|
433
756
|
columns = opts[:include_fields]
|
434
757
|
end
|
435
758
|
print as_pretty_table(workflows, columns, opts)
|
436
759
|
end
|
760
|
+
|
761
|
+
def format_workflow_type(workflow)
|
762
|
+
if workflow['type'] == 'provision'
|
763
|
+
"Provisioning"
|
764
|
+
elsif workflow['type'] == 'operation'
|
765
|
+
"Operational"
|
766
|
+
else
|
767
|
+
workflow['type']
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
def format_platform(platform)
|
772
|
+
if platform.nil?
|
773
|
+
"All"
|
774
|
+
elsif platform == 'osx'
|
775
|
+
"OSX"
|
776
|
+
else
|
777
|
+
platform.to_s.capitalize
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
781
|
+
def find_option_type_by_name_or_id(val)
|
782
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
783
|
+
return find_option_type_by_id(val)
|
784
|
+
else
|
785
|
+
return find_option_type_by_name(val)
|
786
|
+
end
|
787
|
+
end
|
788
|
+
|
789
|
+
def find_option_type_by_id(id)
|
790
|
+
begin
|
791
|
+
json_response = @option_types_interface.get(id.to_i)
|
792
|
+
return json_response['optionType']
|
793
|
+
rescue RestClient::Exception => e
|
794
|
+
if e.response && e.response.code == 404
|
795
|
+
print_red_alert "Option Type not found by id #{id}"
|
796
|
+
return nil
|
797
|
+
else
|
798
|
+
raise e
|
799
|
+
end
|
800
|
+
end
|
801
|
+
end
|
802
|
+
|
803
|
+
def find_option_type_by_name(name)
|
804
|
+
json_results = @option_types_interface.list({name: name.to_s})
|
805
|
+
if json_results['optionTypes'].empty?
|
806
|
+
print_red_alert "Option Type not found by name #{name}"
|
807
|
+
return nil
|
808
|
+
end
|
809
|
+
option_type = json_results['optionTypes'][0]
|
810
|
+
return option_type
|
811
|
+
end
|
812
|
+
|
813
|
+
def find_instance_by_name_or_id(val)
|
814
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
815
|
+
return find_instance_by_id(val)
|
816
|
+
else
|
817
|
+
return find_instance_by_name(val)
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
def find_instance_by_id(id)
|
822
|
+
begin
|
823
|
+
json_response = @instances_interface.get(id.to_i)
|
824
|
+
return json_response['instance']
|
825
|
+
rescue RestClient::Exception => e
|
826
|
+
if e.response && e.response.code == 404
|
827
|
+
print_red_alert "Instance not found by id #{id}"
|
828
|
+
return nil
|
829
|
+
else
|
830
|
+
raise e
|
831
|
+
end
|
832
|
+
end
|
833
|
+
end
|
834
|
+
|
835
|
+
def find_instance_by_name(name)
|
836
|
+
instances = @instances_interface.list({name: name.to_s})['instances']
|
837
|
+
if instances.empty?
|
838
|
+
print_red_alert "Instance not found by name #{name}"
|
839
|
+
return nil
|
840
|
+
elsif instances.size > 1
|
841
|
+
print_red_alert "#{instances.size} instances found by name #{name}"
|
842
|
+
as_pretty_table(instances, [:id, :name], {color: red})
|
843
|
+
print_red_alert "Try using ID instead"
|
844
|
+
print reset,"\n"
|
845
|
+
return nil
|
846
|
+
else
|
847
|
+
return instances[0]
|
848
|
+
end
|
849
|
+
end
|
850
|
+
|
851
|
+
def find_server_by_name_or_id(val)
|
852
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
853
|
+
return find_server_by_id(val)
|
854
|
+
else
|
855
|
+
return find_server_by_name(val)
|
856
|
+
end
|
857
|
+
end
|
858
|
+
|
859
|
+
def find_server_by_id(id)
|
860
|
+
begin
|
861
|
+
json_response = @servers_interface.get(id.to_i)
|
862
|
+
return json_response['server']
|
863
|
+
rescue RestClient::Exception => e
|
864
|
+
if e.response && e.response.code == 404
|
865
|
+
print_red_alert "Server not found by id #{id}"
|
866
|
+
return nil
|
867
|
+
else
|
868
|
+
raise e
|
869
|
+
end
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
def find_server_by_name(name)
|
874
|
+
servers = @servers_interface.list({name: name.to_s})['servers']
|
875
|
+
if servers.empty?
|
876
|
+
print_red_alert "Host not found by name #{name}"
|
877
|
+
return nil
|
878
|
+
elsif servers.size > 1
|
879
|
+
print_red_alert "#{servers.size} hosts found by name #{name}"
|
880
|
+
as_pretty_table(servers, [:id, :name], {color: red})
|
881
|
+
print_red_alert "Try using ID instead"
|
882
|
+
print reset,"\n"
|
883
|
+
return nil
|
884
|
+
else
|
885
|
+
return servers[0]
|
886
|
+
end
|
887
|
+
end
|
888
|
+
|
437
889
|
end
|