app/models/asset_tag_import.rb
class AssetTagImport
include Vmdb::Logging
attr_accessor :errors
attr_accessor :stats
# The required fields list is not limited anymore, so pass nil.
REQUIRED_COLS = {VmOrTemplate => nil, Host => nil}
MATCH_KEYS = {
VmOrTemplate => ["name", "hardware.networks.hostname", "guid"],
Host => ["name", "hostname"]
}
def initialize(opts = {})
opts.each do |k, v|
var = "@#{k}"
instance_variable_set(var, v) unless v.nil?
end
@errors = ActiveModel::Errors.new(self)
end
def self.upload(klass, fd)
klass = Object.const_get(klass.to_s)
raise _("%{name} not supported for upload!") % {:name => klass} unless REQUIRED_COLS.key?(klass)
raise _("%{name} not supported for upload!") % {:name => klass} unless MATCH_KEYS.key?(klass)
data, keys, tags = MiqBulkImport.upload(fd, REQUIRED_COLS[klass], MATCH_KEYS[klass].dup)
import = new(:data => data, :keys => keys, :tags => tags, :klass => klass)
import.verify
import
end
def verify
@errors.clear
@verified_data = {}
good = bad = 0
@data.each do |line|
keys = []
@keys.each do |k|
t = []
t[0] = k
t[1] = line[k]
keys.push(t)
end
objs = MiqBulkImport.find_entry_by_keys(@klass, keys)
if objs.empty?
bad += 1
_log.warn("#{@keys[0].titleize} #{line[@keys[0]]}: Unable to find a #{@klass.name}")
err = "#{@klass.name.downcase}notfound".to_sym
@errors.add(err, "#{@keys[0].titleize}: #{line[@keys[0]]}: Unable to find a #{@klass.name}")
next
end
if objs.length > 1
bad += 1
err = "serveral#{@klass.name.downcase}sfound4keys".to_sym
_log.warn("#{@keys[0].titleize}: #{line[@keys[0]]}: Could not resolve a #{@klass.name}, an entry will be skipped")
@errors.add(err, "#{@keys[0].titleize}: #{line[@keys[0]]}: Could not resolve a #{@klass.name}, an entry will be skipped")
else
@verified_data[objs[0].id] ||= []
tags = {}
@tags.each do |tag|
tags[tag] = line[tag]
end
@verified_data[objs[0].id].push(tags)
good += 1
end
end
@verified_data.each do |id, data|
if data.length > 1
obj = @klass.find_by(:id => id)
while data.length > 1
data.shift
_log.warn("#{@klass.name} #{obj.name}, Multiple lines for the same object, the last line is applied")
@errors.add(:singlevaluedassettag, "#{@klass.name}: #{obj.name}, Multiple lines for the same object, the last line is applied")
end
end
end
@stats = {:good => good, :bad => bad}
_log.info("Number of valid entries #{@stats[:good]}, number of invalid entries #{@stats[:bad]}")
@stats
end
def apply
@verified_data.each do |id, data|
obj = @klass.find_by(:id => id)
if obj
attrs = obj.miq_custom_attributes
new_attrs = []
data[0].each do |key, value|
# Add custom attribute here.
attr = attrs.detect { |ca| ca.name == key }
if attr.nil?
if value.blank?
_log.info("#{@klass.name}: #{obj.name}, Skipping tag <#{key}> due to blank value")
else
_log.info("#{@klass.name}: #{obj.name}, Adding tag <#{key}>, value <#{value}>")
new_attrs << {:name => key, :value => value, :source => 'EVM'}
end
else
if value.blank?
_log.info("#{@klass.name}: #{obj.name}, Deleting tag <#{key}> due to blank value")
attr.delete
else
_log.info("#{@klass.name}: #{obj.name}, Updating tag <#{key}>, value <#{value}>")
attr.update_attribute(:value, value)
end
end
end
obj.custom_attributes.create(new_attrs)
end
end
end
# part of the errors interface
# not bothering with localization
def self.human_attribute_name(attr_name, *_)
attr_name
end
end