Skip to content

Commit

Permalink
First pass at MiqExpression 'includes' as SQL
Browse files Browse the repository at this point in the history
This sets up support MiqExpression to introspect the current model of
the expression fragment to see if there are any methods defined there
that can help allow SQL to be executed in MiqExpression for that given
field.

Vm.ipaddresses has been the first attempt at this.
  • Loading branch information
NickLaMuro committed Jun 11, 2018
1 parent 89f2a63 commit 8fd09bb
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 0 deletions.
33 changes: 33 additions & 0 deletions app/models/vm_or_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1618,6 +1618,39 @@ def self.vms_by_ipaddress(ipaddress)
end
end

# This creates the following SQL conditional:
#
# 1 = (SELECT 1
# FROM hardwares
# JOIN networks ON networks.hardware_id = hardwares.id
# WHERE hardwares.vm_or_template_id = vms.id
# AND (networks.ipaddress LIKE "%IPADDRESS%"
# OR networks.ipv6address LIKE "%IPADDRESS%")
# LIMIT 1
# )
#
# This is simply an existance check, so when one record is found matching the
# following conditions:
#
# - It is a hardware record that is associated with the vm
# - It has an ipaddress or ipv6address that matches the search
#
# It will return the VM record.
def self.miq_expression_includes_any_ipaddresses_arel(ipaddress)
vms = arel_table
networks = Network.arel_table
hardwares = Hardware.arel_table

match_grouping = networks[:ipaddress].matches("%#{ipaddress}%")
.or(networks[:ipv6address].matches("%#{ipaddress}%"))

query = hardwares.project(1)
.join(networks).on(networks[:hardware_id].eq(hardwares[:id]))
.where(hardwares[:vm_or_template_id].eq(vms[:id]).and(match_grouping))
.take(1)
Arel::Nodes::SqlLiteral.new("1").eq(query)
end

def self.scan_by_property(property, value, _options = {})
_log.info("scan_vm_by_property called with property:[#{property}] value:[#{value}]")
case property
Expand Down
14 changes: 14 additions & 0 deletions lib/miq_expression.rb
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,15 @@ def sql_supports_atom?(exp)
# TODO: Support includes operator for sub-sub-tables
return false
end
when "includes any", "includes all", "includes only"
# Support this only from the main model (for now)
if exp[operator].keys.include?("field") && exp[operator]["field"].split(".").length == 1
model, field = exp[operator]["field"].split("-")
method = "miq_expression_#{operator.downcase.tr(' ', '_')}_#{field}_arel"
return model.constantize.respond_to?(method)
else
return false
end
when "find", "regular expression matches", "regular expression does not match", "key exists", "value exists"
return false
else
Expand Down Expand Up @@ -1343,6 +1352,11 @@ def to_arel(exp, tz)
escape = nil
case_sensitive = true
arel_attribute.matches("%#{parsed_value}%", escape, case_sensitive)
when "includes all", "includes any", "includes only"
method = "miq_expression_"
method << "#{operator.downcase.tr(' ', '_')}_"
method << "#{field.column}_arel"
field.model.send(method, parsed_value)
when "starts with"
escape = nil
case_sensitive = true
Expand Down
24 changes: 24 additions & 0 deletions spec/models/vm_or_template_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,30 @@
end
end

describe ".miq_expression_includes_any_ipaddresses_arel" do
subject { FactoryGirl.create(:vm) }
let(:no_hardware_vm) { FactoryGirl.create(:vm) }
let(:wrong_ip_vm) { FactoryGirl.create(:vm) }

before do
hw1 = FactoryGirl.create(:hardware, :vm => subject)
FactoryGirl.create(:network, :hardware => hw1, :ipaddress => "10.11.11.11")
FactoryGirl.create(:network, :hardware => hw1, :ipaddress => "10.10.10.10")
FactoryGirl.create(:network, :hardware => hw1, :ipaddress => "10.10.10.11")

hw2 = FactoryGirl.create(:hardware, :vm => wrong_ip_vm)
FactoryGirl.create(:network, :hardware => hw2, :ipaddress => "11.11.11.11")
end

it "runs a single query, returning only the valid vm" do
expect do
query = Vm.miq_expression_includes_any_ipaddresses_arel("10.10.10")
result = Vm.where(query)
expect(result.to_a).to eq([subject])
end.to match_query_limit_of(1)
end
end

context ".event_by_property" do
context "should add an EMS event" do
before do
Expand Down

0 comments on commit 8fd09bb

Please sign in to comment.