app/models/custom_button.rb
class CustomButton < ApplicationRecord
has_one :resource_action, :as => :resource, :dependent => :destroy, :autosave => true
scope :with_array_order, lambda { |ids, column = :id, column_type = :bigint|
order = sanitize_sql_array(["array_position(ARRAY[?]::#{column_type}[], #{table_name}.#{column}::#{column_type})", ids])
order(Arel.sql(order))
}
serialize :options, Hash
serialize :visibility_expression
serialize :enablement_expression
serialize :visibility
validates :applies_to_class, :presence => true
validates :name, :description, :uniqueness_when_changed => {:scope => [:applies_to_class, :applies_to_id]}, :presence => true
virtual_attribute :uri_attributes, :string
include UuidMixin
acts_as_miq_set_member
TYPES = {"default" => N_("Default"),
"ansible_playbook" => N_("Ansible Playbook")}.freeze
PLAYBOOK_METHOD = "Order_Ansible_Playbook".freeze
BUTTON_CLASSES = [
AvailabilityZone,
CloudNetwork,
CloudObjectStoreContainer,
CloudSubnet,
CloudTenant,
CloudVolume,
ContainerGroup,
ContainerImage,
ContainerNode,
ContainerProject,
ContainerTemplate,
ContainerVolume,
EmsCluster,
ExtManagementSystem,
GenericObject,
Host,
MiqGroup,
MiqTemplate,
NetworkRouter,
NetworkService,
OrchestrationStack,
SecurityGroup,
SecurityPolicy,
SecurityPolicyRule,
Service,
Storage,
Switch,
Tenant,
User,
Vm,
PhysicalChassis,
PhysicalServer,
PhysicalStorage,
PhysicalRack,
].freeze
def self.buttons_for(other, applies_to_id = nil)
if other.kind_of?(Class)
applies_to_class = other.base_model.name
elsif other.kind_of?(String)
applies_to_class = other
else
raise _("Instance has no id") if other.id.nil?
applies_to_class = other.class.base_model.name
applies_to_id = other.id
end
where(:applies_to_class => applies_to_class, :applies_to_id => applies_to_id)
end
def expanded_serializable_hash
serializable_hash.tap do |button_hash|
button_hash[:resource_action] = resource_action.serializable_hash if resource_action
end
end
def applies_to
klass = applies_to_class.constantize
applies_to_id.nil? ? klass : klass.find_by(:id => applies_to_id)
end
def applies_to=(other)
if other.kind_of?(Class)
self.applies_to_class = other.base_model.name
self.applies_to_id = nil
elsif other.kind_of?(String)
self.applies_to_class = other
self.applies_to_id = nil
else
raise _("Instance has no id") if other.id.nil?
self.applies_to_class = other.class.base_model.name
self.applies_to_id = other.id
end
end
def invoke(target, source = nil)
args = resource_action.automate_queue_hash(target, {"result_format" => 'ignore'}, User.current_user)
publish_event(source, target, args)
MiqQueue.put(queue_opts(target, args))
end
def publish_event(source, target, args = nil)
args ||= resource_action.automate_queue_hash(target, {}, User.current_user)
Array(target).each { |t| create_event(source, t, args) }
end
def create_event(source, target, args)
CustomButtonEvent.create!(
:event_type => 'button.trigger.start',
:message => 'Custom button launched',
:source => source,
:target => target,
:username => args[:username],
:user_id => args[:user_id],
:group_id => args[:miq_group_id],
:tenant_id => args[:tenant_id],
:timestamp => Time.now.utc,
:full_data => {
:args => args,
:automate_entry_point => resource_action.ae_path,
:button_id => id,
:button_name => name
}
)
end
def queue_opts(target, args)
{
:class_name => 'MiqAeEngine',
:method_name => 'deliver',
:args => [args],
:role => 'automate',
:zone => target.try(:my_zone),
:priority => MiqQueue::HIGH_PRIORITY,
}
end
def invoke_async(target, source = nil)
task_opts = {
:action => "Calling automate for user #{userid}",
:userid => User.current_user
}
args = resource_action.automate_queue_hash(target, {"result_format" => 'ignore'}, User.current_user)
publish_event(source, target, args)
MiqTask.generic_action_with_callback(task_opts, queue_opts(target, args))
end
def to_export_xml(_options)
end
# Helper methods to support moving automate columns to resource_actions table
def uri=(_value)
end
def uri
resource_action.try(:ae_uri)
end
def uri_path=(value)
ra = get_resource_action
ra.ae_namespace, ra.ae_class, ra.ae_instance, _attr_name = MiqAeEngine::MiqAePath.split(value)
end
def uri_path
get_resource_action.try(:ae_path)
end
def uri_message=(value)
get_resource_action.ae_message = value
end
def uri_message
get_resource_action.ae_message
end
def uri_attributes=(value)
attrs = value.reject { |k, _v| MiqAeEngine::DEFAULT_ATTRIBUTES.include?(k) }
get_resource_action.ae_attributes = attrs
end
def uri_attributes
get_resource_action.ae_attributes
end
def uri_object_name
get_resource_action.ae_instance
end
def get_resource_action
resource_action || build_resource_action
end
def evaluate_enablement_expression_for(object)
return true unless enablement_expression
return false if enablement_expression && !object # list
enablement_expression.lenient_evaluate(object)
end
def evaluate_visibility_expression_for(object)
return true unless visibility_expression
return false if visibility_expression && !object # object == nil, method is called for list of objects
visibility_expression.lenient_evaluate(object)
end
# End - Helper methods to support moving automate columns to resource_actions table
def self.parse_uri(uri)
_scheme, _userinfo, _host, _port, _registry, path, _opaque, query, fragment = MiqAeEngine::MiqAeUri.split(uri)
return path, MiqAeEngine::MiqAeUri.query2hash(query), fragment
end
def self.button_classes
BUTTON_CLASSES.collect(&:name)
end
def visible_for_current_user?
return false unless visibility.key?(:roles)
visibility[:roles].include?(User.current_user.miq_user_role_name) || visibility[:roles].include?("_ALL_")
end
def self.get_user(user)
user = User.lookup_by_userid(user) if user.kind_of?(String)
raise _("Unable to find user '%{user}'") % {:user => user} if user.nil?
user
end
def copy(options = {})
options[:guid] = SecureRandom.uuid
options.each_with_object(dup) { |(k, v), button| button.send(:"#{k}=", v) }.tap(&:save!)
end
def self.display_name(number = 1)
n_('Button', 'Buttons', number)
end
def open_url?
options[:open_url] == true
end
end