Skip to content

Commit

Permalink
Support for copy and compare of ae components
Browse files Browse the repository at this point in the history
 Allows for copy of class, instances and methods
 across domains.
  • Loading branch information
mkanoor committed Jun 30, 2014
1 parent 86580e9 commit bb3167d
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 bb3167d

Please sign in to comment.