vcoworkflows-ruby2 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,44 @@
1
+ require 'vcoworkflows'
2
+
3
+ # rubocop:disable all
4
+ module VcoWorkflows
5
+ # wrapper to assist aruba in single process execution
6
+ class Runner
7
+ # Allow everything fun to be injected from the outside while defaulting to normal implementations.
8
+ def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
9
+ @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
10
+ end
11
+
12
+ # Do the things!
13
+ def execute!
14
+ exit_code = begin
15
+ # Thor accesses these streams directly rather than letting them be
16
+ # injected, so we replace them...
17
+ $stderr = @stderr
18
+ $stdin = @stdin
19
+ $stdout = @stdout
20
+
21
+ VcoWorkflows::CLI.start(@argv)
22
+
23
+ # Thor::Base#start does not have a return value, assume success if no
24
+ # exception is raised.
25
+ 0
26
+ rescue StandardError => e
27
+ # The ruby interpreter would pipe this to STDERR and exit 1 in the
28
+ # case of an unhandled exception
29
+ b = e.backtrace
30
+ b.unshift("#{b.shift}: #{e.message} (#{e.class})")
31
+ @stderr.puts(b.map { |s| "\tfrom #{s}" }.join("\n"))
32
+ 1
33
+ ensure
34
+ # put them back.
35
+ $stderr = STDERR
36
+ $stdin = STDIN
37
+ $stdout = STDOUT
38
+ end
39
+ # Proxy exit code back to the injected kernel.
40
+ @kernel.exit(exit_code)
41
+ end
42
+ end
43
+ end
44
+ # rubocop:enable all
@@ -0,0 +1,71 @@
1
+ require 'vcoworkflows/constants'
2
+ require 'vcoworkflows/config'
3
+ require 'rest_client'
4
+
5
+ # VcoWorkflows
6
+ module VcoWorkflows
7
+ # VcoSession is a simple wrapper for RestClient::Resource, and supports
8
+ # GET and POST operations against the vCO API.
9
+ class VcoSession
10
+ # Accessor for rest-client object, primarily for testing purposes
11
+ attr_reader :rest_resource
12
+
13
+ # rubocop:disable MethodLength
14
+
15
+ # Initialize the session
16
+ #
17
+ # When specifying a config, do not provide other parameters. Likewise,
18
+ # if providing uri, user, and password, a config object is not necessary.
19
+ #
20
+ # @param [VcoWorkflows::Config] config Configuration object for the connection
21
+ # @param [String] uri URI for the vCenter Orchestrator API endpoint
22
+ # @param [String] user User name for vCO
23
+ # @param [String] password Password for vCO
24
+ # @param [Boolean] verify_ssl Whether or not to verify SSL certificates
25
+ def initialize(config: nil, uri: nil, user: nil, password: nil, verify_ssl: true)
26
+ # If a configuration object was provided, use it.
27
+ # If we got a URL and no config, build a new config with the URL and any
28
+ # other options that passed in.
29
+ # Otherwise, load the default config file if possible...
30
+ if config
31
+ config = config
32
+ elsif uri && config.nil?
33
+ config = VcoWorkflows::Config.new(url: uri,
34
+ username: user,
35
+ password: password,
36
+ verify_ssl: verify_ssl)
37
+ elsif uri.nil? && config.nil?
38
+ config = VcoWorkflows::Config.new
39
+ end
40
+
41
+ RestClient.proxy = ENV['http_proxy'] # Set a proxy if present
42
+ @rest_resource = RestClient::Resource.new(config.url,
43
+ user: config.username,
44
+ password: config.password,
45
+ verify_ssl: config.verify_ssl)
46
+ end
47
+ # rubocop:enable MethodLength, LineLength
48
+
49
+ # Perform a REST GET operation against the specified endpoint
50
+ #
51
+ # @param [String] endpoint REST endpoint to use
52
+ # @param [Hash] headers Optional headers to use in request
53
+ # @return [String] JSON response body
54
+ def get(endpoint, headers = {})
55
+ headers = { accept: :json }.merge(headers)
56
+ @rest_resource[endpoint].get headers
57
+ end
58
+
59
+ # Perform a REST POST operation against the specified endpoint with the
60
+ # given data body
61
+ #
62
+ # @param [String] endpoint REST endpoint to use
63
+ # @param [String] body JSON data body to post
64
+ # @param [Hash] headers Optional headers to use in request
65
+ # @return [String] JSON response body
66
+ def post(endpoint, body, headers = {})
67
+ headers = { accept: :json, content_type: :json }.merge(headers)
68
+ @rest_resource[endpoint].post body, headers
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,5 @@
1
+ # VcoWorkflows
2
+ module VcoWorkflows
3
+ # Gem Version
4
+ VERSION = '0.2.3'.freeze
5
+ end
@@ -0,0 +1,395 @@
1
+ require_relative 'constants'
2
+ require_relative 'workflowservice'
3
+ require_relative 'workflowpresentation'
4
+ require_relative 'workflowtoken'
5
+ require_relative 'workflowparameter'
6
+ require 'json'
7
+
8
+ # VcoWorkflows
9
+ module VcoWorkflows
10
+ # rubocop:disable ClassLength
11
+
12
+ # Class to represent a Workflow as presented by vCenter Orchestrator.
13
+ class Workflow
14
+ # Workflow GUID
15
+ # @return [String] workflow GUID
16
+ attr_reader :id
17
+
18
+ # Workflow name
19
+ # @return [String] workflow name
20
+ attr_reader :name
21
+
22
+ # Workflow version
23
+ # @return [String] workflow version
24
+ attr_reader :version
25
+
26
+ # Workflow description
27
+ # @return [String] workflow description
28
+ attr_reader :description
29
+
30
+ # Workflow Input Parameters: Hash of WorkflowParameters, keyed by name
31
+ # @return [Hash<VcoWorkflows::WorkflowParameter>]
32
+ attr_reader :input_parameters
33
+
34
+ # Workflow Output Parameters: Hash of WorkflowParameters, keyed by name
35
+ # @return [Hash<VcoWorkflows::WorkflowParameter>]
36
+ attr_reader :output_parameters
37
+
38
+ # Workflow Service in use by this Workflow
39
+ # @return [VcoWorkflows::WorkflowService]
40
+ attr_accessor :service
41
+
42
+ # Workflow execution ID
43
+ # @return [String]
44
+ attr_reader :execution_id
45
+
46
+ # Workflow source JSON
47
+ # @return [String]
48
+ attr_reader :source_json
49
+
50
+ # rubocop:disable CyclomaticComplexity, PerceivedComplexity, MethodLength
51
+
52
+ # Create a Workflow object given vCenter Orchestrator's JSON description
53
+ #
54
+ # When passed `url`, `username` and `password` the necessary session and
55
+ # service objects will be created behind the scenes. Alternatively you can
56
+ # pass in a Config or a WorkflowService object if you have
57
+ # constructed them yourself. You may also pass in the path to a
58
+ # configuration file (`config_file`).
59
+ #
60
+ # @param [String] name Name of the requested workflow
61
+ # @param [Hash] options Hash of options:
62
+ # - id: (String) GUID for the Workflow
63
+ # - url: (String) vCO REST API URL
64
+ # - username: (String) User to authenticate as
65
+ # - password: (String) Password for username
66
+ # - verify_ssl: (Boolean) Perform TLS/SSL certificate validation
67
+ # - service: (VcoWorkflows::WorkflowService) WorkflowService to use for communicating to vCO
68
+ # - config: (VcoWorkflows::Config) Configuration object to use for this workflow's session
69
+ # - config_file: (String) Path to load configuration file from for this workflow's session
70
+ # @return [VcoWorkflows::Workflow]
71
+ def initialize(name = nil, options = {})
72
+ @options = {
73
+ id: nil,
74
+ url: nil,
75
+ username: nil,
76
+ password: nil,
77
+ verify_ssl: true,
78
+ service: nil,
79
+ config: nil,
80
+ config_file: nil
81
+ }.merge(options)
82
+
83
+ config = nil
84
+ @service = nil
85
+ @execution_id = nil
86
+
87
+ # -------------------------------------------------------------
88
+ # Figure out how to get a workflow service. If I can't, I die.
89
+ # (DUN dun dun...)
90
+
91
+ if options[:service]
92
+ @service = options[:service]
93
+ else
94
+ # If we were given a configuration object, use it
95
+ # If we were given a config file path, use it
96
+ # If we have a url, username and password, use them
97
+ # If all we have is a URL, try anyway, maybe we'll get username and
98
+ # password from ENV values (hey, it might work...)
99
+ if @options[:config]
100
+ config = @options[:config]
101
+ elsif @options[:config_file]
102
+ config = VcoWorkflows::Config.new(config_file: @options[:config_file])
103
+ elsif @options[:url] && @options[:username] && @options[:password]
104
+ config = VcoWorkflows::Config.new(url: @options[:url],
105
+ username: @options[:username],
106
+ password: @options[:password],
107
+ verify_ssl: @options[:verify_ssl])
108
+ elsif @options[:url]
109
+ config = VcoWorkflows::Config.new(url: @options[:url],
110
+ verify_ssl: @options[:verify_ssl])
111
+ end
112
+
113
+ # If we got a config object above, great. If it's still nil, VcoSession
114
+ # will accept that and try to load the default config file.
115
+ session = VcoWorkflows::VcoSession.new(config: config)
116
+ @service = VcoWorkflows::WorkflowService.new(session)
117
+ end
118
+
119
+ raise(IOError, 'Unable to create/use a WorkflowService!') if @service.nil?
120
+
121
+ # -------------------------------------------------------------
122
+ # Retrieve the workflow and parse it into a data structure
123
+ # If we're given both a name and ID, prefer the id
124
+ workflow_json = if @options[:id]
125
+ @service.get_workflow_for_id(@options[:id])
126
+ else
127
+ @service.get_workflow_for_name(name)
128
+ end
129
+ workflow_data = JSON.parse(workflow_json)
130
+
131
+ # Set up the attributes if they exist in the data json,
132
+ # otherwise nil them
133
+ @id = workflow_data.key?('id') ? workflow_data['id'] : nil
134
+ @name = workflow_data.key?('name') ? workflow_data['name'] : nil
135
+ @version = workflow_data.key?('version') ? workflow_data['version'] : nil
136
+ @description = workflow_data.key?('description') ? workflow_data['description'] : nil
137
+
138
+ # Process the input parameters
139
+ if workflow_data.key?('input-parameters')
140
+ @input_parameters = Workflow.parse_parameters(workflow_data['input-parameters'])
141
+ else
142
+ @input_parameters = {}
143
+ end
144
+
145
+ # Identify required input_parameters
146
+ wfpres = VcoWorkflows::WorkflowPresentation.new(@service, @id)
147
+ wfpres.required.each do |req_param|
148
+ @input_parameters[req_param].required(true)
149
+ end
150
+
151
+ # Process the output parameters
152
+ if workflow_data.key?('output-parameters')
153
+ @output_parameters = Workflow.parse_parameters(workflow_data['output-parameters'])
154
+ else
155
+ @output_parameters = {}
156
+ end
157
+ end
158
+ # rubocop:enable CyclomaticComplexity, PerceivedComplexity, MethodLength, LineLength
159
+
160
+ # vCO API URL used when creating this workflow
161
+ # @return [String]
162
+ def url
163
+ options[:url]
164
+ end
165
+
166
+ # vCO user name used when creating this workflow object
167
+ # @return [String]
168
+ def username
169
+ options[:username]
170
+ end
171
+
172
+ # vCO password used when creating this workflow object
173
+ # @return [String]
174
+ def password
175
+ options[:password]
176
+ end
177
+
178
+ # Verify SSL?
179
+ # @return [Boolean]
180
+ def verify_ssl?
181
+ options[:verify_ssl]
182
+ end
183
+
184
+ # rubocop:disable MethodLength
185
+
186
+ # Parse json parameters and return a nice hash
187
+ # @param [Array<Hash>] parameter_data Array of parameter data hashes
188
+ # by vCO
189
+ # @return [Hash]
190
+ def self.parse_parameters(parameter_data = [])
191
+ wfparams = {}
192
+ parameter_data.each do |parameter|
193
+ wfparam = VcoWorkflows::WorkflowParameter.new(parameter['name'], parameter['type'])
194
+ if parameter['value']
195
+ if wfparam.type.eql?('Array')
196
+ value = []
197
+ begin
198
+ parameter['value'][wfparam.type.downcase]['elements'].each do |element|
199
+ value << element[element.keys.first]['value']
200
+ end
201
+ rescue StandardError => error
202
+ parse_failure(error, wfparam, parameter)
203
+ end
204
+ else
205
+ begin
206
+ value = parameter['value'][parameter['value'].keys.first]['value']
207
+ rescue StandardError => error
208
+ parse_failure(error, wfparam, parameter)
209
+ end
210
+ end
211
+ value = nil if value.eql?('null')
212
+ wfparam.set(value)
213
+ end
214
+ wfparams[parameter['name']] = wfparam
215
+ end
216
+ wfparams
217
+ end
218
+ # rubocop:enable MethodLength
219
+
220
+ # Process exceptions raised in parse_parameters by bravely ignoring them
221
+ # and forging ahead blindly!
222
+ # @param [Exception] error
223
+ def self.parse_failure(error, wfparam, parameter)
224
+ $stderr.puts "\nWhoops!"
225
+ $stderr.puts "Ran into a problem parsing parameter #{wfparam.name} (#{wfparam.type})!"
226
+ $stderr.puts "Source data: #{JSON.pretty_generate(parameter)}\n"
227
+ $stderr.puts error.message
228
+ $stderr.puts "\nBravely forging on and ignoring parameter #{wfparam.name}!"
229
+ end
230
+
231
+ # Get an array of the names of all the required input parameters
232
+ # @return [Hash] Hash of WorkflowParameter input parameters which
233
+ # are required for this workflow
234
+ def required_parameters
235
+ required = {}
236
+ @input_parameters.each_value { |v| required[v.name] = v if v.required? }
237
+ required
238
+ end
239
+
240
+ # Get the parameter object named. If a value is provided, set the value
241
+ # and return the parameter object.
242
+ #
243
+ # To get a parameter value, use parameter(parameter_name).value
244
+ #
245
+ # @param [String] parameter_name Name of the parameter to get
246
+ # @param [Object, nil] parameter_value Optional value for parameter.
247
+ # @return [VcoWorkflows::WorkflowParameter] The resulting WorkflowParameter
248
+ #
249
+ # rubocop:disable Metrics/MethodLength
250
+ def parameter(parameter_name, parameter_value = nil)
251
+ unless parameter_value.nil?
252
+ if @input_parameters.key?(parameter_name)
253
+ @input_parameters[parameter_name].set parameter_value
254
+ else
255
+ $stderr.puts "\nAttempted to set a value for a non-existent WorkflowParameter!"
256
+ $stderr.puts "It appears that there is no parameter \"#{parameter}\"."
257
+ $stderr.puts "Valid parameter names are: #{@input_parameters.keys.join(', ')}"
258
+ $stderr.puts ''
259
+ raise(IOError, ERR[:no_such_parameter])
260
+ end
261
+ end
262
+ @input_parameters[parameter_name]
263
+ end
264
+ # rubocop:enable MethodLength
265
+
266
+ # Set a parameter with a WorkflowParameter object
267
+ # @param [VcoWorkflows::WorkflowParameter] wfparameter New parameter
268
+ def parameter=(wfparameter)
269
+ @input_parameters[wfparameter.name] = wfparameter
270
+ end
271
+
272
+ # Determine whether a parameter has been set
273
+ # @param [String] parameter_name Name of the parameter to check
274
+ # @return [Boolean]
275
+ def parameter?(parameter_name)
276
+ parameter(parameter_name).set?
277
+ end
278
+
279
+ # Set all input parameters using the given hash
280
+ # @param [Hash] parameter_hash input parameter values keyed by
281
+ # input_parameter name
282
+ def parameters=(parameter_hash)
283
+ parameter_hash.each { |name, value| parameter(name, value) }
284
+ end
285
+
286
+ # Set a parameter to a value.
287
+ # @deprecated Use {#parameter} instead
288
+ # @param [String] parameter_name name of the parameter to set
289
+ # @param [Object] value value to set
290
+ # @return [VcoWorkflows::WorkflowParameter] The resulting WorkflowParameter
291
+ def set_parameter(parameter_name, value)
292
+ parameter(parameter_name, value)
293
+ end
294
+
295
+ # Get the value for an input parameter
296
+ # @deprecated Use {#parameter} to retrieve the
297
+ # {VcoWorkflows::WorkflowParameter} object, instead
298
+ # @param [String] parameter_name Name of the input parameter
299
+ # whose value to get
300
+ # @return [Object]
301
+ def get_parameter(parameter_name)
302
+ parameter(parameter_name).value
303
+ end
304
+
305
+ # Execute this workflow
306
+ # @param [VcoWorkflows::WorkflowService] workflow_service
307
+ # @return [String] Workflow Execution ID
308
+ def execute(workflow_service = nil)
309
+ # If we're not given an explicit workflow service for this execution
310
+ # request, use the one defined when we were created.
311
+ workflow_service = @service if workflow_service.nil?
312
+ # If we still have a nil workflow_service, go home.
313
+ raise(IOError, ERR[:no_workflow_service_defined]) if workflow_service.nil?
314
+ # Make sure we didn't forget any required parameters
315
+ verify_parameters
316
+ # Let's get this thing running!
317
+ @execution_id = workflow_service.execute_workflow(@id, input_parameter_json)
318
+ end
319
+
320
+ # Get a list of all the executions of this workflow. Wrapper for
321
+ # VcoWorkflows::WorkflowService#get_execution_list
322
+ # @return [Hash]
323
+ def executions
324
+ @service.get_execution_list(@id)
325
+ end
326
+
327
+ # Return a WorkflowToken
328
+ # @param [String] execution_id optional execution id to get logs for
329
+ # @return [VcoWorkflows::WorkflowToken]
330
+ def token(execution_id = nil)
331
+ execution_id = @execution_id if execution_id.nil?
332
+ VcoWorkflows::WorkflowToken.new(@service, @id, execution_id)
333
+ end
334
+
335
+ # Return logs for the given execution
336
+ # @param [String] execution_id optional execution id to get logs for
337
+ # @return [VcoWorkflows::WorkflowExecutionLog]
338
+ def log(execution_id = nil)
339
+ execution_id = @execution_id if execution_id.nil?
340
+ log_json = @service.get_log(@id, execution_id)
341
+ VcoWorkflows::WorkflowExecutionLog.new(log_json)
342
+ end
343
+
344
+ # rubocop:disable MethodLength
345
+
346
+ # Stringify the workflow
347
+ # @return [String]
348
+ def to_s
349
+ string = "Workflow: #{@name}\n"
350
+ string << "ID: #{@id}\n"
351
+ string << "Description: #{@description}\n"
352
+ string << "Version: #{@version}\n"
353
+
354
+ string << "\nInput Parameters:\n"
355
+ unless @input_parameters.empty?
356
+ @input_parameters.each_value { |wf_param| string << " #{wf_param}" }
357
+ end
358
+
359
+ string << "\nOutput Parameters:" << "\n"
360
+ unless @output_parameters.empty?
361
+ @output_parameters.each_value { |wf_param| string << " #{wf_param}" }
362
+ end
363
+
364
+ # Assert
365
+ string
366
+ end
367
+ # rubocop:enable MethodLength
368
+
369
+ private
370
+
371
+ # Convert the input parameters to a JSON document
372
+ # @return [String]
373
+ def input_parameter_json
374
+ tmp_params = []
375
+ @input_parameters.each_value { |v| tmp_params << v.as_struct if v.set? }
376
+ param_struct = { parameters: tmp_params }
377
+ param_struct.to_json
378
+ end
379
+
380
+ # Verify that all mandatory input parameters have values
381
+ def verify_parameters
382
+ required_parameters.each do |name, wfparam|
383
+ # rubocop:disable Style/Next
384
+ if wfparam.required? && (wfparam.value.nil? || wfparam.value.to_s.empty?)
385
+ raise(
386
+ IOError,
387
+ ERR[:param_verify_failed] << "#{name} required but not present."
388
+ )
389
+ end
390
+ # rubocop:enable Style/Next
391
+ end
392
+ end
393
+ end
394
+ # rubocop:enable ClassLength
395
+ end