Skip to content

Commit

Permalink
Merge pull request ManageIQ#49 from mkanoor/epxression_method_engine_…
Browse files Browse the repository at this point in the history
…changes

Support for expression methods
  • Loading branch information
gmcculloug authored Jul 27, 2017
2 parents c455f22 + 23d3509 commit 6480698
Show file tree
Hide file tree
Showing 5 changed files with 347 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
module MiqAeEngine
class MiqAeExpressionMethod
include ApplicationController::Filter::SubstMixin
def initialize(method_obj, obj, inputs)
@edit = {}
@name = method_obj.name
@workspace = obj.workspace
@inputs = inputs
@attributes = inputs['distinct'] || inputs['attributes'] || %w(name)
load_expression(method_obj.data)
process_filter
end

def run
@search_objects = Rbac.search(:filter => MiqExpression.new(@exp),
:class => @exp_object,
:results_format => :objects).first
@search_objects.empty? ? error_handler : set_result
end

private

def load_expression(data)
raise MiqAeException::MethodExpressionEmpty, "Empty expression" if data.blank?
begin
hash = YAML.load(data)
if hash[:expression] && hash[:db]
@exp = hash[:expression]
@exp_object = hash[:db]
else
raise MiqAeException::MethodExpressionInvalid, "Invalid expression #{data}"
end
rescue
raise MiqAeException::MethodExpressionInvalid, "Invalid expression #{data}"
end
end

def process_filter
exp_table = exp_build_table(@exp, true)
qs_tokens = create_tokens(exp_table, @exp)
values = get_args(qs_tokens.keys.length)
qs_tokens.keys.each_with_index { |token_at, index| qs_tokens[token_at][:value] = values[index] }
exp_replace_qs_tokens(@exp, qs_tokens)
end

def result_hash(obj)
@attributes.each_with_object({}) do |attr, hash|
hash[attr] = result_simple(obj, attr)
end
end

def result_array
multiple = @attributes.count > 1
result = @search_objects.collect do |obj|
multiple ? @attributes.collect { |attr| result_simple(obj, attr) } : result_simple(obj, @attributes.first)
end
@inputs['distinct'].blank? ? result : result.uniq
end

def result_dialog_hash
key = @inputs['key'] || 'id'
@search_objects.each_with_object({}) do |obj, hash|
hash[result_simple(obj, key)] = result_simple(obj, @attributes.first)
end
end

def result_simple(obj, attr)
raise MiqAeException::MethodNotDefined, "Undefined method #{attr} in class #{obj.class}" unless obj.respond_to?(attr.to_sym)
obj.send(attr.to_sym)
end

def set_result
target_object.attributes[attribute_name] = exp_value
@workspace.root['ae_result'] = 'ok'
end

def error_handler
disposition = @inputs['on_empty'] || 'error'
case disposition.to_sym
when :error
set_error
when :warn
set_warn
when :abort
set_abort
end
end

def set_error
$miq_ae_logger.error("Expression method ends")
@workspace.root['ae_result'] = 'error'
end

def set_abort
$miq_ae_logger.error("Expression method aborted")
raise MiqAeException::AbortInstantiation, "Expression method #{@name} aborted"
end

def set_warn
$miq_ae_logger.warn("Expression method ends")
@workspace.root['ae_result'] = 'warn'
set_default_value
end

def set_default_value
return unless @inputs.key?('default')
target_object.attributes[attribute_name] = @inputs['default']
end

def attribute_name
@inputs['result_attr'] || 'values'
end

def target_object
@workspace.get_obj_from_path(@inputs['result_obj'] || '.').tap do |obj|
raise MiqAeException::MethodExpressionTargetObjectMissing, @inputs['result_obj'] unless obj
end
end

def exp_value
type = @inputs['result_type'] || 'dialog_hash'
case type.downcase.to_sym
when :hash
result_hash(@search_objects.first)
when :dialog_hash
result_dialog_hash
when :array
result_array
when :simple
result_simple(@search_objects.first, @inputs['attributes'].first)
else
raise MiqAeException::MethodExpressionResultTypeInvalid, "Invalid Result type, should be hash, array or dialog_hash"
end
end

def get_args(num_token)
params = []
(1..num_token).each do |i|
key = "arg#{i}"
raise MiqAeException::MethodParameterNotFound, key unless @inputs.key?(key)
params[i - 1] = @inputs[key]
end
params
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ def self.invoke_inline(aem, obj, inputs)
raise MiqAeException::InvalidMethod, "Inline Method Language [#{aem.language}] not supported"
end

def self.invoke_expression(aem, obj, inputs)
exp_method = MiqAeEngine::MiqAeExpressionMethod.new(aem, obj, inputs)
exp_method.run
end

def self.invoke_uri(aem, obj, _inputs)
scheme, userinfo, host, port, registry, path, opaque, query, fragment = URI.split(aem.data)
raise MiqAeException::MethodNotFound, "Specified URI [#{aem.data}] in Method [#{aem.name}] has unsupported scheme of #{scheme}; supported scheme is file" unless scheme.downcase == "file"
Expand Down Expand Up @@ -45,7 +50,7 @@ def self.invoke(obj, aem, args)
aem.inputs.each do |f|
key = f.name
value = args[key]
value = obj.attributes[key] || f.default_value if value.nil?
value = obj.attributes[key] || obj.substitute_value(f.default_value) if value.nil?
inputs[key] = MiqAeObject.convert_value_based_on_datatype(value, f["datatype"])

if obj.attributes[key] && f["datatype"] != "string"
Expand All @@ -60,7 +65,7 @@ def self.invoke(obj, aem, args)

if obj.workspace.readonly?
$miq_ae_logger.info("Workspace Instantiation is READONLY -- skipping method [#{aem.fqname}] with inputs [#{inputs.inspect}]")
elsif ["inline", "builtin", "uri"].include?(aem.location.downcase.strip)
elsif %w(inline builtin uri expression).include?(aem.location.downcase.strip)
$miq_ae_logger.info("Invoking [#{aem.location}] method [#{aem.fqname}] with inputs [#{inputs.inspect}]")
return MiqAeEngine::MiqAeMethod.send("invoke_#{aem.location.downcase.strip}", aem, obj, inputs)
end
Expand Down
34 changes: 17 additions & 17 deletions lib/miq_automation_engine/engine/miq_ae_engine/miq_ae_object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,23 @@ def uri2value(uri, required = false)
uri # if it was not processed, return the original uri
end

def substitute_value(value, _type = nil, required = false)
Benchmark.current_realtime[:substitution_count] += 1
Benchmark.realtime_block(:substitution_time) do
value = value.gsub(RE_SUBST) do |_s|
subst = uri2value($1, required)
subst &&= subst.to_s
# This encoding of relationship is not needed, until we can get a valid use case
# Based on RFC 3986 Section 2.4 "When to Encode or Decode"
# We are properly encoding when we send URL requests to external systems
# or building an automate request
# subst &&= URI.escape(subst, RE_URI_ESCAPE) if type == :aetype_relationship
subst
end unless value.nil?
return value
end
end

private

def call_method(obj, method)
Expand Down Expand Up @@ -541,23 +558,6 @@ def self.convert_value_based_on_datatype(value, datatype)
value
end

def substitute_value(value, _type = nil, required = false)
Benchmark.current_realtime[:substitution_count] += 1
Benchmark.realtime_block(:substitution_time) do
value = value.gsub(RE_SUBST) do |_s|
subst = uri2value($1, required)
subst &&= subst.to_s
# This encoding of relationship is not needed, until we can get a valid use case
# Based on RFC 3986 Section 2.4 "When to Encode or Decode"
# We are properly encoding when we send URL requests to external systems
# or building an automate request
# subst &&= URI.escape(subst, RE_URI_ESCAPE) if type == :aetype_relationship
subst
end unless value.nil?
return value
end
end

def process_assertion(f, message, args)
Benchmark.current_realtime[:assertion_count] += 1
Benchmark.realtime_block(:assertion_time) do
Expand Down
8 changes: 8 additions & 0 deletions lib/miq_automation_engine/miq_ae_exception.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ class FileExists < MiqAeDatastoreError; end
class DomainNotAccessible < MiqAeDatastoreError; end
class CannotLock < MiqAeDatastoreError; end
class CannotUnlock < MiqAeDatastoreError; end

class MethodExpressionNotFound < MiqAeEngineError; end
class MethodExpressionEmpty < MiqAeEngineError; end
class MethodExpressionInvalid < MiqAeEngineError; end
class MethodExpressionTargetObjectMissing < MiqAeEngineError; end
class MethodExpressionResultTypeInvalid < MiqAeEngineError; end
class MethodParameterNotFound < MiqAeEngineError; end
class MethodNotDefined < MiqAeEngineError; end
end

module MiqException
Expand Down
Loading

0 comments on commit 6480698

Please sign in to comment.