
This can happen due to Chef's stupid approach to loading library files, which uses Kernel#load rather than #require. Consequently a library can get loaded twice: once by Chef::RunContext#load_libraries and once by another library file which depends on it. This is also the cause of the TYPE constants being redefined.
153 lines
3.7 KiB
Ruby
153 lines
3.7 KiB
Ruby
require 'mixlib/shellout'
|
|
|
|
module Pacemaker
|
|
class CIBObject
|
|
attr_accessor :name, :definition
|
|
|
|
@@subclasses = { } unless class_variable_defined?(:@@subclasses)
|
|
|
|
class << self
|
|
attr_reader :object_type
|
|
|
|
def register_type(type_name)
|
|
@object_type = type_name
|
|
@@subclasses[type_name] = self
|
|
end
|
|
|
|
def get_definition(name)
|
|
cmd = Mixlib::ShellOut.new("crm configure show #{name}")
|
|
cmd.environment['HOME'] = ENV.fetch('HOME', '/root')
|
|
cmd.run_command
|
|
begin
|
|
cmd.error!
|
|
cmd.stdout
|
|
rescue
|
|
nil
|
|
end
|
|
end
|
|
|
|
def type(definition)
|
|
unless definition =~ /\A(\w+)\s/
|
|
raise "Couldn't extract CIB object type from '#{definition}'"
|
|
end
|
|
return $1
|
|
end
|
|
|
|
def from_name(name)
|
|
definition = get_definition(name)
|
|
return nil unless definition and ! definition.empty?
|
|
from_definition(definition)
|
|
end
|
|
|
|
# Make sure this works on Ruby 1.8.7 which is missing
|
|
# Object#singleton_class.
|
|
def singleton_class
|
|
class << self; self; end
|
|
end
|
|
|
|
def from_definition(definition)
|
|
calling_class = self.singleton_class
|
|
this_class = method(__method__).owner
|
|
if calling_class == this_class
|
|
# Invoked via (this) base class
|
|
obj_type = type(definition)
|
|
subclass = @@subclasses[obj_type]
|
|
unless subclass
|
|
raise "No subclass of #{self.name} was registered with type '#{obj_type}'"
|
|
end
|
|
return subclass.from_definition(definition)
|
|
else
|
|
# Invoked via subclass
|
|
obj = new(name)
|
|
unless name == obj.name
|
|
raise "Name '#{obj.name}' in definition didn't match name '#{name}' used for retrieval"
|
|
end
|
|
obj.definition = definition
|
|
obj.parse_definition
|
|
obj
|
|
end
|
|
end
|
|
end
|
|
|
|
def initialize(name)
|
|
@name = name
|
|
@definition = nil
|
|
end
|
|
|
|
def copy_attrs_from_chef_resource(resource, *attrs)
|
|
attrs.each do |attr|
|
|
value = resource.send(attr.to_sym)
|
|
writer = (attr + '=').to_sym
|
|
send(writer, value)
|
|
end
|
|
self
|
|
end
|
|
|
|
def copy_attrs_to_chef_resource(resource, *attrs)
|
|
attrs.each do |attr|
|
|
value = send(attr.to_sym)
|
|
writer = attr.to_sym
|
|
resource.send(writer, value)
|
|
end
|
|
end
|
|
|
|
def load_definition
|
|
@definition = self.class.get_definition(name)
|
|
|
|
if @definition and ! @definition.empty? and type != self.class.object_type
|
|
raise CIBObject::TypeMismatch, \
|
|
"Expected #{self.class.object_type} type but loaded definition was type #{type}"
|
|
end
|
|
end
|
|
|
|
def parse_definition
|
|
raise NotImplementedError, "#{self.class} must implement #parse_definition"
|
|
end
|
|
|
|
def exists?
|
|
!! (definition && ! definition.empty?)
|
|
end
|
|
|
|
def type
|
|
self.class.type(definition)
|
|
end
|
|
|
|
def to_s
|
|
"%s '%s'" % [self.class.description, name]
|
|
end
|
|
|
|
def definition_indent
|
|
' ' * 9
|
|
end
|
|
|
|
def continuation_line(text)
|
|
" \\\n#{definition_indent}#{text}"
|
|
end
|
|
|
|
# Returns a single-quoted shell-escaped version of the definition
|
|
# string, suitable for use in a command like:
|
|
#
|
|
# echo '...' | crm configure load update -
|
|
def quoted_definition_string
|
|
"'%s'" % \
|
|
definition_string \
|
|
.gsub('\\') { '\\\\' } \
|
|
.gsub("'") { "\\'" }
|
|
end
|
|
|
|
def reconfigure_command
|
|
"echo #{quoted_definition_string} | crm configure load update -"
|
|
end
|
|
|
|
def delete_command
|
|
"crm configure delete '#{name}'"
|
|
end
|
|
end
|
|
|
|
class CIBObject::DefinitionParseError < StandardError
|
|
end
|
|
|
|
class CIBObject::TypeMismatch < StandardError
|
|
end
|
|
end
|