Skip to content

Commit

Permalink
Merge pull request #73 from mkanoor/automate_copy_compare
Browse files Browse the repository at this point in the history
 Support for copy and compare of automate class, instance and methods
  • Loading branch information
gmcculloug committed Jul 1, 2014
2 parents 8d9f1cf + bb3167d commit 7505a09
Show file tree
Hide file tree
Showing 32 changed files with 8,783 additions and 4 deletions.
10 changes: 10 additions & 0 deletions vmdb/app/models/miq_ae_class.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,16 @@ def editable?
ae_namespace.editable?
end

def field_names
ae_fields.collect { |x| x.name.downcase }
end

def field_hash(name)
field = ae_fields.detect { |f| f.name.casecmp(name) == 0 }
raise "field #{name} not found in class #{@name}" if field.nil?
field.attributes
end

private

def scoped_methods(s)
Expand Down
87 changes: 87 additions & 0 deletions vmdb/app/models/miq_ae_class_compare_fields.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
class MiqAeClassCompareFields
attr_reader :compatibilities
attr_reader :incompatibilities
attr_reader :fields_in_use
attr_reader :adds
IGNORE_PROPERTY_NAMES = %w(name owner created_on updated_on updated_by
updated_by_user_id id class_id)
WARNING_PROPERTY_NAMES = %w(priority message display_name default_value substitute
visibility collect scope description condition on_entry
on_exit on_error max_retries max_time)
ERROR_PROPERTY_NAMES = %w(aetype datatype)

CONGRUENT_SCHEMA = 1
COMPATIBLE_SCHEMA = 2
INCOMPATIBLE_SCHEMA = 4

def initialize(new_class, old_class)
@new_class = new_class
@old_class = old_class
end

def compare
load_field_names
initialize_results
venn_list
validate_similar
status
end

def status
return CONGRUENT_SCHEMA if congruent?
return COMPATIBLE_SCHEMA if compatible?
INCOMPATIBLE_SCHEMA
end

def congruent?
@adds.empty? && @incompatibilities.empty? &&
@compatibilities.empty? && @fields_in_use.empty? &&
@deletes.empty?
end

def compatible?
@incompatibilities.empty? && @deletes.empty?
end

private

def initialize_results
@incompatibilities = []
@compatibilities = []
@fields_in_use = []
@adds = []
@deletes = []
end

def load_field_names
@old_names = @old_class.field_names
@new_names = @new_class.field_names
end

def venn_list
@similar = @new_names & @old_names
@adds = @new_names - @old_names
@deletes = @old_names - @new_names
end

def validate_similar
@similar.each do |name|
old_field = @old_class.field_hash(name)
new_field = @new_class.field_hash(name)
compare_field_properties(name, old_field, new_field)
end
end

def compare_field_properties(field_name, old_field, new_field)
old_field.each do |property, value|
next if IGNORE_PROPERTY_NAMES.include?(property)
next if value == new_field[property]
hash = {'property' => property,
'old_value' => value,
'new_value' => new_field[property],
'field_name' => field_name}
@compatibilities << hash if WARNING_PROPERTY_NAMES.include?(property)
@incompatibilities << hash if ERROR_PROPERTY_NAMES.include?(property)
end
end
end
80 changes: 80 additions & 0 deletions vmdb/app/models/miq_ae_class_copy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
class MiqAeClassCopy
include MiqAeCopyMixin
DELETE_PROPERTIES = %w(updated_by updated_by_user_id updated_on id
created_on updated_on method_id owner class_id)

def initialize(class_fqname)
@class_fqname = class_fqname
@src_domain, @partial_ns, @ae_class = MiqAeClassCopy.split(@class_fqname, false)
@src_class = MiqAeClass.find_by_fqname(@class_fqname)
raise "Source class not found #{@class_fqname}" unless @src_class
end

def to_domain(domain, ns = nil, overwrite = false)
check_duplicity(domain, ns, @src_class.name)
@overwrite = overwrite
@target_ns_fqname = target_ns(domain, ns)
@target_name = @src_class.name
copy
end

def as(new_name, ns = nil, overwrite = false)
check_duplicity(@src_domain, ns, new_name)
@overwrite = overwrite
@target_ns_fqname = target_ns(@src_domain, ns)
@target_name = new_name
copy
end

private

def target_ns(domain, ns)
return "#{domain}/#{@partial_ns}" if ns.nil?
MiqAeNamespace.find_by_fqname(ns, false).nil? ? "#{domain}/#{ns}" : ns
end

def copy
validate
create_class
copy_schema
@dest_class
end

def create_class
ns = MiqAeNamespace.find_or_create_by_fqname(@target_ns_fqname, false)
ns.save!
@dest_class = MiqAeClass.create!(:namespace_id => ns.id,
:name => @target_name,
:description => @src_class.description,
:type => @src_class.type,
:display_name => @src_class.display_name,
:inherits => @src_class.inherits,
:visibility => @src_class.visibility)
end

def copy_schema
@dest_class.ae_fields = add_fields
@dest_class.save!
end

def add_fields
@src_class.ae_fields.collect do |src_field|
attrs = src_field.attributes.reject { |k, _| DELETE_PROPERTIES.include?(k) }
MiqAeField.new(attrs)
end
end

def validate
dest_class = MiqAeClass.find_by_fqname("#{@target_ns_fqname}/#{@target_name}")
if dest_class
dest_class.destroy if @overwrite
raise "Destination Class already exists #{dest_class.fqname}" unless @overwrite
end
end

def check_duplicity(domain, ns, classname)
if domain.downcase == @src_domain.downcase && classname.downcase == @ae_class.downcase
raise "Cannot copy class onto itself" if ns.nil? || ns.downcase == @partial_ns.downcase
end
end
end
19 changes: 19 additions & 0 deletions vmdb/app/models/miq_ae_class_yaml.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class MiqAeClassYaml
attr_accessor :ae_class_obj
def initialize(filename = nil)
@filename = filename
@ae_class_obj = YAML.load_file(filename) if filename
end

def field_names
raise "class object has not been set" unless @ae_class_obj
@ae_class_obj['object']['schema'].collect { |x| x['field']['name'].downcase }
end

def field_hash(name)
raise "class object has not been set" unless @ae_class_obj
field = @ae_class_obj['object']['schema'].detect { |f| f['field']['name'].casecmp(name) == 0 }
raise "field #{name} not found in yaml class #{@filename}" if field.nil?
field['field']
end
end
14 changes: 13 additions & 1 deletion vmdb/app/models/miq_ae_instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,20 @@ def editable?
ae_class.ae_namespace.editable?
end

private
def field_names
fields = ae_values.collect { |v| v.field_id }
ae_class.ae_fields.select { |x| fields.include?(x.id) }.collect { |f| f.name.downcase }
end

def field_value_hash(name)
field = ae_class.ae_fields.detect { |f| f.name.casecmp(name) == 0 }
raise "Field #{name} not found in class #{ae_class.fqname}" if field.nil?
value = ae_values.detect { |v| v.field_id == field.id }
raise "Field #{name} not found in instance #{self.name} in class #{ae_class.fqname}" if value.nil?
value.attributes
end

private

def validate_field(field)
if field.kind_of?(MiqAeField)
Expand Down
87 changes: 87 additions & 0 deletions vmdb/app/models/miq_ae_instance_compare_values.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
class MiqAeInstanceCompareValues
attr_reader :compatibilities
attr_reader :incompatibilities
attr_reader :fields_in_use
attr_reader :adds
IGNORE_PROPERTY_NAMES = %w(name owner created_on updated_on updated_by
updated_by_user_id id class_id instance_id field_id)
WARNING_PROPERTY_NAMES = %w(priority message display_name default_value substitute
visibility collect scope description condition on_entry
on_exit on_error max_retries max_time)
ERROR_PROPERTY_NAMES = %w(aetype datatype)

CONGRUENT_INSTANCE = 1
COMPATIBLE_INSTANCE = 2
INCOMPATIBLE_INSTANCE = 4

def initialize(new_instance, old_instance)
@new_instance = new_instance
@old_instance = old_instance
end

def compare
load_field_names
initialize_results
venn_list
validate_similar
status
end

def status
return CONGRUENT_INSTANCE if congruent?
return COMPATIBLE_INSTANCE if compatible?
INCOMPATIBLE_INSTANCE
end

def congruent?
@adds.empty? && @incompatibilities.empty? &&
@compatibilities.empty? && @fields_in_use.empty? &&
@deletes.empty?
end

def compatible?
@incompatibilities.empty? && @deletes.empty?
end

private

def initialize_results
@incompatibilities = []
@compatibilities = []
@fields_in_use = []
@adds = []
@deletes = []
end

def load_field_names
@old_names = @old_instance.field_names
@new_names = @new_instance.field_names
end

def venn_list
@similar = @new_names & @old_names
@adds = @new_names - @old_names
@deletes = @old_names - @new_names
end

def validate_similar
@similar.each do |name|
old_value = @old_instance.field_value_hash(name)
new_value = @new_instance.field_value_hash(name)
compare_value_properties(name, old_value, new_value)
end
end

def compare_value_properties(field_name, old_value, new_value)
old_value.each do |property, data|
next if IGNORE_PROPERTY_NAMES.include?(property)
next if data == new_value[property]
hash = {'property' => property,
'old_data' => data,
'new_data' => new_value[property],
'field_name' => field_name}
@compatibilities << hash if WARNING_PROPERTY_NAMES.include?(property)
@incompatibilities << hash if ERROR_PROPERTY_NAMES.include?(property)
end
end
end
Loading

0 comments on commit 7505a09

Please sign in to comment.