switch to OO classes modelling Pacemaker objects
This commit is contained in:
23
Guardfile
23
Guardfile
@@ -7,25 +7,24 @@ guard_opts = {
|
|||||||
all_after_pass: true,
|
all_after_pass: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
def startup_guards
|
|
||||||
watch(%r{^Gemfile$}) { yield }
|
|
||||||
watch(%r{^Gemfile.lock$}) { yield }
|
|
||||||
watch(%r{^spec/spec_helper\.rb$}) { yield }
|
|
||||||
end
|
|
||||||
|
|
||||||
def all_specs
|
def all_specs
|
||||||
'spec'
|
'spec'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :rspec do
|
group :rspec do
|
||||||
guard 'rspec', guard_opts do
|
guard 'rspec', guard_opts do
|
||||||
startup_guards { all_specs }
|
watch(%r{^Gemfile$}) { all_specs }
|
||||||
watch(%r{^spec_helper\.rb$}) { all_specs }
|
watch(%r{^Gemfile.lock$}) { all_specs }
|
||||||
watch(%r{^helpers/(.+)\.rb$}) { all_specs }
|
watch(%r{^spec/spec_helper\.rb$}) { all_specs }
|
||||||
|
watch(%r{^spec/helpers/(.+)\.rb$}) { all_specs }
|
||||||
|
watch(%r{^libraries/pacemaker\.rb$}) { all_specs }
|
||||||
watch(%r{^spec/.+_spec\.rb$})
|
watch(%r{^spec/.+_spec\.rb$})
|
||||||
watch(%r{^(libraries|providers)/(.+)\.rb$}) do |m|
|
watch(%r{^libraries/pacemaker/(.+)\.rb$}) { |m|
|
||||||
"spec/#{m[1]}/#{m[2]}_spec.rb"
|
"spec/libraries/#{m[1]}_spec.rb"
|
||||||
end
|
}
|
||||||
|
watch(%r{^(resources|providers)/(.+)\.rb$}) { |m|
|
||||||
|
"spec/providers/#{m[1]}_spec.rb"
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -1,94 +0,0 @@
|
|||||||
require 'shellwords'
|
|
||||||
|
|
||||||
module Chef::Libraries
|
|
||||||
module Pacemaker
|
|
||||||
module CIBObjects
|
|
||||||
include Chef::Mixin::ShellOut
|
|
||||||
|
|
||||||
def get_cib_object_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 cib_object_exists?(name)
|
|
||||||
dfn = get_cib_object_definition(name)
|
|
||||||
!! (dfn && ! dfn.empty?)
|
|
||||||
end
|
|
||||||
|
|
||||||
def cib_object_type(obj_definition)
|
|
||||||
unless obj_definition =~ /\A(\w+)\s/
|
|
||||||
raise "Couldn't extract CIB object type from '#{obj_definition}'"
|
|
||||||
end
|
|
||||||
return $1
|
|
||||||
end
|
|
||||||
|
|
||||||
def pacemaker_resource_running?(name)
|
|
||||||
cmd = shell_out! "crm", "resource", "status", name
|
|
||||||
Chef::Log.info cmd.stdout
|
|
||||||
cmd.stdout.include? "resource #{name} is running"
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params_string(params)
|
|
||||||
return "" if ! params or params.empty?
|
|
||||||
s = " params"
|
|
||||||
params.sort.each do |key, value|
|
|
||||||
s << %' #{key}="#{value}"'
|
|
||||||
end
|
|
||||||
s
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_meta_string(meta)
|
|
||||||
return "" if ! meta or meta.empty?
|
|
||||||
s = " meta"
|
|
||||||
meta.sort.each do |key, value|
|
|
||||||
s << %' #{key}="#{value}"'
|
|
||||||
end
|
|
||||||
s
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_op_string(ops)
|
|
||||||
return "" if ! ops or ops.empty?
|
|
||||||
s = " op"
|
|
||||||
ops.sort.each do |op, attrs|
|
|
||||||
s << " #{op}"
|
|
||||||
attrs.sort.each do |key, value|
|
|
||||||
s << %' #{key}="#{value}"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
s
|
|
||||||
end
|
|
||||||
|
|
||||||
# CIB object definitions look something like:
|
|
||||||
#
|
|
||||||
# primitive keystone ocf:openstack:keystone \
|
|
||||||
# params os_username="crowbar" os_password="crowbar" os_tenant_name="openstack" \
|
|
||||||
# meta target-role="Started" is-managed="true" \
|
|
||||||
# op monitor interval="10" timeout=30s \
|
|
||||||
# op start interval="10s" timeout="240" \
|
|
||||||
#
|
|
||||||
# This method extracts a Hash from one of the params / meta / op lines.
|
|
||||||
def extract_hash(name, obj_definition, data_type)
|
|
||||||
unless obj_definition =~ /^\s+#{data_type} (.+?)(\s*\\)?$/
|
|
||||||
raise "Couldn't retrieve #{data_type} for '#{name}' CIB object from [#{obj_definition}]"
|
|
||||||
end
|
|
||||||
|
|
||||||
h = {}
|
|
||||||
Shellwords.split($1).each do |kvpair|
|
|
||||||
unless kvpair =~ /^(.+?)=(.+)$/
|
|
||||||
raise "Couldn't understand '#{kvpair}' for #{data_type} section of '#{name}' primitive"
|
|
||||||
end
|
|
||||||
h[$1] = $2.sub(/^"(.*)"$/, "\1")
|
|
||||||
end
|
|
||||||
h
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
2
libraries/pacemaker.rb
Normal file
2
libraries/pacemaker.rb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
require_relative 'pacemaker/resource/primitive'
|
||||||
|
require_relative 'pacemaker/resource/clone'
|
91
libraries/pacemaker/cib_object.rb
Normal file
91
libraries/pacemaker/cib_object.rb
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
require 'mixlib/shellout'
|
||||||
|
|
||||||
|
module Pacemaker
|
||||||
|
class ObjectTypeMismatch < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class CIBObject
|
||||||
|
attr_accessor :name, :definition
|
||||||
|
|
||||||
|
@@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
|
||||||
|
obj_type = type(definition)
|
||||||
|
subclass = @@subclasses[obj_type]
|
||||||
|
unless subclass
|
||||||
|
raise "No subclass of #{self.name} was registered with type '#{obj_type}'"
|
||||||
|
end
|
||||||
|
obj = subclass.from_definition(definition)
|
||||||
|
unless name == obj.name
|
||||||
|
raise "Name '#{obj.name}' in definition didn't match name '#{name}' used for retrieval"
|
||||||
|
end
|
||||||
|
obj
|
||||||
|
end
|
||||||
|
|
||||||
|
def from_definition(definition)
|
||||||
|
obj = new(name)
|
||||||
|
obj.definition = definition
|
||||||
|
obj.parse_definition
|
||||||
|
obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(name)
|
||||||
|
@name = name
|
||||||
|
@definition = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_definition
|
||||||
|
@definition = self.class.get_definition(name)
|
||||||
|
|
||||||
|
if @definition and ! @definition.empty? and type != self.class.object_type
|
||||||
|
raise ObjectTypeMismatch, "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 delete_command
|
||||||
|
"crm configure delete '#{name}'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
libraries/pacemaker/resource.rb
Normal file
23
libraries/pacemaker/resource.rb
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
require 'chef/mixin/shell_out'
|
||||||
|
require_relative 'cib_object'
|
||||||
|
|
||||||
|
module Pacemaker
|
||||||
|
class Resource < Pacemaker::CIBObject
|
||||||
|
include Chef::Mixin::ShellOut
|
||||||
|
|
||||||
|
def running?
|
||||||
|
cmd = shell_out! "crm", "resource", "status", name
|
||||||
|
Chef::Log.info cmd.stdout
|
||||||
|
!! cmd.stdout.include?("resource #{name} is running")
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_command
|
||||||
|
"crm resource start '#{name}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
def stop_command
|
||||||
|
"crm resource stop '#{name}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
8
libraries/pacemaker/resource/clone.rb
Normal file
8
libraries/pacemaker/resource/clone.rb
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
require_relative File::join(%w(.. resource))
|
||||||
|
|
||||||
|
class Pacemaker::Resource::Clone < Pacemaker::Resource
|
||||||
|
register_type 'clone'
|
||||||
|
|
||||||
|
attr_accessor :primitive
|
||||||
|
|
||||||
|
end
|
124
libraries/pacemaker/resource/primitive.rb
Normal file
124
libraries/pacemaker/resource/primitive.rb
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
require 'shellwords'
|
||||||
|
require_relative File::join(%w(.. resource))
|
||||||
|
|
||||||
|
class Pacemaker::Resource::Primitive < Pacemaker::Resource
|
||||||
|
TYPE = 'primitive'
|
||||||
|
|
||||||
|
register_type TYPE
|
||||||
|
|
||||||
|
attr_accessor :agent, :params, :meta, :op
|
||||||
|
|
||||||
|
def initialize(*args)
|
||||||
|
super(*args)
|
||||||
|
|
||||||
|
@agent = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_chef_resource(resource)
|
||||||
|
primitive = new(resource.name)
|
||||||
|
%w(agent params meta op).each do |data|
|
||||||
|
value = resource.send(data.to_sym)
|
||||||
|
writer = (data + '=').to_sym
|
||||||
|
primitive.send(writer, value)
|
||||||
|
end
|
||||||
|
return primitive
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_definition
|
||||||
|
unless definition =~ /\Aprimitive (\S+) (\S+)/
|
||||||
|
raise RuntimeError, "Couldn't parse definition '#{definition}'"
|
||||||
|
end
|
||||||
|
self.name = $1
|
||||||
|
self.agent = $2
|
||||||
|
|
||||||
|
%w(params meta).each do |data_type|
|
||||||
|
hash = self.class.extract_hash(definition, data_type)
|
||||||
|
writer = (data_type + '=').to_sym
|
||||||
|
send(writer, hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
# FIXME: deal with op
|
||||||
|
end
|
||||||
|
|
||||||
|
def params_string
|
||||||
|
self.class.params_string(params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def meta_string
|
||||||
|
self.class.meta_string(meta)
|
||||||
|
end
|
||||||
|
|
||||||
|
def op_string
|
||||||
|
self.class.op_string(op)
|
||||||
|
end
|
||||||
|
|
||||||
|
def definition_string
|
||||||
|
return <<EOF
|
||||||
|
primitive #{name} #{agent} \\
|
||||||
|
#{params_string} \\
|
||||||
|
#{meta_string} \\
|
||||||
|
#{op_string}
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
|
||||||
|
def crm_configure_command
|
||||||
|
args = %w(crm configure primitive)
|
||||||
|
args << [name, agent, params_string, meta_string, op_string]
|
||||||
|
args.join " "
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.params_string(params)
|
||||||
|
return "" if ! params or params.empty?
|
||||||
|
s = " params"
|
||||||
|
params.sort.each do |key, value|
|
||||||
|
s << %' #{key}="#{value}"'
|
||||||
|
end
|
||||||
|
s
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.meta_string(meta)
|
||||||
|
return "" if ! meta or meta.empty?
|
||||||
|
s = " meta"
|
||||||
|
meta.sort.each do |key, value|
|
||||||
|
s << %' #{key}="#{value}"'
|
||||||
|
end
|
||||||
|
s
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.op_string(ops)
|
||||||
|
return "" if ! ops or ops.empty?
|
||||||
|
s = " op"
|
||||||
|
ops.sort.each do |op, attrs|
|
||||||
|
s << " #{op}"
|
||||||
|
attrs.sort.each do |key, value|
|
||||||
|
s << %' #{key}="#{value}"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
s
|
||||||
|
end
|
||||||
|
|
||||||
|
# CIB object definitions look something like:
|
||||||
|
#
|
||||||
|
# primitive keystone ocf:openstack:keystone \
|
||||||
|
# params os_username="crowbar" os_password="crowbar" os_tenant_name="openstack" \
|
||||||
|
# meta target-role="Started" is-managed="true" \
|
||||||
|
# op monitor interval="10" timeout=30s \
|
||||||
|
# op start interval="10s" timeout="240" \
|
||||||
|
#
|
||||||
|
# This method extracts a Hash from one of the params / meta / op lines.
|
||||||
|
def self.extract_hash(obj_definition, data_type)
|
||||||
|
unless obj_definition =~ /^\s+#{data_type} (.+?)(\s*\\)?$/
|
||||||
|
raise "Couldn't retrieve #{data_type} for '#{name}' CIB object from [#{obj_definition}]"
|
||||||
|
end
|
||||||
|
|
||||||
|
h = {}
|
||||||
|
Shellwords.split($1).each do |kvpair|
|
||||||
|
unless kvpair =~ /^(.+?)=(.+)$/
|
||||||
|
raise "Couldn't understand '#{kvpair}' for #{data_type} section of '#{name}' primitive"
|
||||||
|
end
|
||||||
|
h[$1] = $2.sub(/^"(.*)"$/, "\1")
|
||||||
|
end
|
||||||
|
h
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@@ -17,7 +17,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries cib_objects))
|
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries pacemaker cib_object))
|
||||||
|
|
||||||
action :create do
|
action :create do
|
||||||
name = new_resource.name
|
name = new_resource.name
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries cib_objects))
|
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries pacemaker cib_object))
|
||||||
|
|
||||||
action :create do
|
action :create do
|
||||||
name = new_resource.name
|
name = new_resource.name
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries cib_objects))
|
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries pacemaker cib_object))
|
||||||
|
|
||||||
action :create do
|
action :create do
|
||||||
name = new_resource.name
|
name = new_resource.name
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries cib_objects))
|
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries pacemaker cib_object))
|
||||||
|
|
||||||
action :create do
|
action :create do
|
||||||
name = new_resource.name
|
name = new_resource.name
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries cib_objects))
|
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries pacemaker cib_object))
|
||||||
|
|
||||||
action :create do
|
action :create do
|
||||||
name = new_resource.name
|
name = new_resource.name
|
||||||
|
@@ -17,9 +17,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries cib_objects))
|
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries pacemaker cib_object))
|
||||||
|
|
||||||
include Chef::Libraries::Pacemaker::CIBObjects
|
|
||||||
|
|
||||||
# For vagrant env, switch to the following 'require' command.
|
# For vagrant env, switch to the following 'require' command.
|
||||||
#require "/srv/chef/file_store/cookbooks/pacemaker/providers/helper"
|
#require "/srv/chef/file_store/cookbooks/pacemaker/providers/helper"
|
||||||
@@ -43,10 +41,11 @@ end
|
|||||||
action :delete do
|
action :delete do
|
||||||
name = new_resource.name
|
name = new_resource.name
|
||||||
next unless @current_resource
|
next unless @current_resource
|
||||||
if pacemaker_resource_running?(name)
|
rsc = Pacemaker::Resource::Primitive.new(name)
|
||||||
|
if rsc.running?
|
||||||
raise "Cannot delete running resource primitive #{name}"
|
raise "Cannot delete running resource primitive #{name}"
|
||||||
end
|
end
|
||||||
execute "crm configure delete #{name}" do
|
execute rsc.delete_command do
|
||||||
action :nothing
|
action :nothing
|
||||||
end.run_action(:run)
|
end.run_action(:run)
|
||||||
new_resource.updated_by_last_action(true)
|
new_resource.updated_by_last_action(true)
|
||||||
@@ -58,8 +57,9 @@ action :start do
|
|||||||
unless @current_resource
|
unless @current_resource
|
||||||
raise "Cannot start non-existent resource primitive '#{name}'"
|
raise "Cannot start non-existent resource primitive '#{name}'"
|
||||||
end
|
end
|
||||||
next if pacemaker_resource_running?(name)
|
rsc = Pacemaker::Resource::Primitive.new(name)
|
||||||
execute "crm resource start #{name}" do
|
next if rsc.running?
|
||||||
|
execute rsc.start_command do
|
||||||
action :nothing
|
action :nothing
|
||||||
end.run_action(:run)
|
end.run_action(:run)
|
||||||
new_resource.updated_by_last_action(true)
|
new_resource.updated_by_last_action(true)
|
||||||
@@ -71,8 +71,9 @@ action :stop do
|
|||||||
unless @current_resource
|
unless @current_resource
|
||||||
raise "Cannot stop non-existent resource primitive '#{name}'"
|
raise "Cannot stop non-existent resource primitive '#{name}'"
|
||||||
end
|
end
|
||||||
next unless pacemaker_resource_running?(name)
|
rsc = Pacemaker::Resource::Primitive.new(name)
|
||||||
execute "crm resource stop #{name}" do
|
next unless rsc.running?
|
||||||
|
execute rsc.stop_command do
|
||||||
action :nothing
|
action :nothing
|
||||||
end.run_action(:run)
|
end.run_action(:run)
|
||||||
new_resource.updated_by_last_action(true)
|
new_resource.updated_by_last_action(true)
|
||||||
@@ -88,32 +89,36 @@ end
|
|||||||
def load_current_resource
|
def load_current_resource
|
||||||
name = @new_resource.name
|
name = @new_resource.name
|
||||||
|
|
||||||
obj_definition = get_cib_object_definition(name)
|
primitive = Pacemaker::Resource::Primitive.new(name)
|
||||||
return if ! obj_definition or obj_definition.empty?
|
begin
|
||||||
Chef::Log.debug "CIB object definition #{obj_definition}"
|
primitive.load_definition
|
||||||
|
rescue Pacemaker::ObjectTypeMismatch => e
|
||||||
unless obj_definition =~ /\Aprimitive #{name} (\S+)/
|
Chef::Log.warn e.message
|
||||||
Chef::Log.warn "Resource '#{name}' was not a primitive"
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
agent = $1
|
|
||||||
|
|
||||||
@current_resource_definition = obj_definition
|
if ! primitive.definition or primitive.definition.empty?
|
||||||
|
Chef::Log.debug "CIB object definition nil or empty"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
Chef::Log.debug "CIB object definition #{primitive.definition}"
|
||||||
|
@current_resource_definition = primitive.definition
|
||||||
|
primitive.parse_definition
|
||||||
|
|
||||||
|
@current_primitive = primitive
|
||||||
@current_resource = Chef::Resource::PacemakerPrimitive.new(name)
|
@current_resource = Chef::Resource::PacemakerPrimitive.new(name)
|
||||||
@current_resource.agent(agent)
|
@current_resource.agent(primitive.agent)
|
||||||
|
|
||||||
%w(params meta).each do |data_type|
|
%w(params meta).each do |data_type|
|
||||||
h = extract_hash(name, obj_definition, data_type)
|
method = data_type.to_sym
|
||||||
@current_resource.send(data_type.to_sym, h)
|
value = primitive.send(method)
|
||||||
Chef::Log.debug "detected #{name} has #{data_type} #{h}"
|
@current_resource.send(method, value)
|
||||||
|
Chef::Log.debug "detected #{name} has #{data_type} #{value}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_resource(name)
|
def create_resource(name)
|
||||||
cmd = "crm configure primitive #{name} #{new_resource.agent}"
|
primitive = Pacemaker::Resource::Primitive.from_chef_resource(new_resource)
|
||||||
cmd << resource_params_string(new_resource.params)
|
cmd = primitive.crm_configure_command
|
||||||
cmd << resource_meta_string(new_resource.meta)
|
|
||||||
cmd << resource_op_string(new_resource.op)
|
|
||||||
|
|
||||||
Chef::Log.info "Creating new resource primitive #{name}"
|
Chef::Log.info "Creating new resource primitive #{name}"
|
||||||
|
|
||||||
@@ -121,7 +126,7 @@ def create_resource(name)
|
|||||||
action :nothing
|
action :nothing
|
||||||
end.run_action(:run)
|
end.run_action(:run)
|
||||||
|
|
||||||
if cib_object_exists?(name)
|
if primitive.exists?
|
||||||
new_resource.updated_by_last_action(true)
|
new_resource.updated_by_last_action(true)
|
||||||
Chef::Log.info "Successfully configured primitive '#{name}'."
|
Chef::Log.info "Successfully configured primitive '#{name}'."
|
||||||
else
|
else
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries cib_objects))
|
require ::File.join(::File.dirname(__FILE__), *%w(.. libraries pacemaker cib_object))
|
||||||
|
|
||||||
action :create do
|
action :create do
|
||||||
name = new_resource.name
|
name = new_resource.name
|
||||||
|
13
spec/helpers/clone_resource.rb
Normal file
13
spec/helpers/clone_resource.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#require ::File.join(::File.dirname(__FILE__), *%w(.. .. libraries pacemaker clone))
|
||||||
|
require_relative 'keystone_primitive'
|
||||||
|
|
||||||
|
module Chef::RSpec
|
||||||
|
module Pacemaker
|
||||||
|
module Config
|
||||||
|
include Chef::RSpec::Pacemaker::Config
|
||||||
|
|
||||||
|
CLONE = ::Pacemaker::Clone.new('clone1')
|
||||||
|
CLONE.primitive = KEYSTONE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@@ -1,38 +0,0 @@
|
|||||||
require ::File.join(::File.dirname(__FILE__), *%w(.. .. libraries cib_objects))
|
|
||||||
|
|
||||||
module Chef::RSpec
|
|
||||||
module Pacemaker
|
|
||||||
module Config
|
|
||||||
extend Chef::Libraries::Pacemaker::CIBObjects
|
|
||||||
|
|
||||||
RA = {
|
|
||||||
:name => "keystone",
|
|
||||||
:agent => "ocf:openstack:keystone",
|
|
||||||
:params => [
|
|
||||||
[ "os_password", "adminpw" ],
|
|
||||||
[ "os_auth_url", "http://node1:5000/v2.0" ],
|
|
||||||
[ "os_username", "admin" ],
|
|
||||||
[ "os_tenant_name", "openstack" ],
|
|
||||||
[ "user", "openstack-keystone" ],
|
|
||||||
],
|
|
||||||
:meta => [
|
|
||||||
[ "target-role", "Started" ],
|
|
||||||
[ "is-managed", "true" ]
|
|
||||||
],
|
|
||||||
:op => [
|
|
||||||
[ "monitor", { "timeout" => "60", "interval" => "10s" } ],
|
|
||||||
[ "start", { "timeout" => "240", "interval" => "10s" } ]
|
|
||||||
],
|
|
||||||
}
|
|
||||||
RA[:params_string] = resource_params_string(RA[:params])
|
|
||||||
RA[:meta_string] = resource_meta_string(RA[:meta])
|
|
||||||
RA[:op_string] = resource_op_string(RA[:op])
|
|
||||||
RA[:config] = <<EOF
|
|
||||||
primitive keystone ocf:openstack:keystone \\
|
|
||||||
#{RA[:params_string]} \\
|
|
||||||
#{RA[:meta_string]} \\
|
|
||||||
#{RA[:op_string]}
|
|
||||||
EOF
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
31
spec/helpers/keystone_primitive.rb
Normal file
31
spec/helpers/keystone_primitive.rb
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
require ::File.join(::File.dirname(__FILE__), *%w(.. .. libraries pacemaker resource primitive))
|
||||||
|
|
||||||
|
module Chef::RSpec
|
||||||
|
module Pacemaker
|
||||||
|
module Config
|
||||||
|
KEYSTONE_PRIMITIVE = ::Pacemaker::Resource::Primitive.new('keystone')
|
||||||
|
KEYSTONE_PRIMITIVE.agent = "ocf:openstack:keystone"
|
||||||
|
KEYSTONE_PRIMITIVE.params = [
|
||||||
|
[ "os_password", "adminpw" ],
|
||||||
|
[ "os_auth_url", "http://node1:5000/v2.0" ],
|
||||||
|
[ "os_username", "admin" ],
|
||||||
|
[ "os_tenant_name", "openstack" ],
|
||||||
|
[ "user", "openstack-keystone" ],
|
||||||
|
]
|
||||||
|
KEYSTONE_PRIMITIVE.meta = [
|
||||||
|
[ "target-role", "Started" ],
|
||||||
|
[ "is-managed", "true" ]
|
||||||
|
]
|
||||||
|
KEYSTONE_PRIMITIVE.op = [
|
||||||
|
[ "monitor", { "timeout" => "60", "interval" => "10s" } ],
|
||||||
|
[ "start", { "timeout" => "240", "interval" => "10s" } ]
|
||||||
|
]
|
||||||
|
KEYSTONE_PRIMITIVE_DEFINITION = <<'EOF'
|
||||||
|
primitive keystone ocf:openstack:keystone \
|
||||||
|
params os_auth_url="http://node1:5000/v2.0" os_password="adminpw" os_tenant_name="openstack" os_username="admin" user="openstack-keystone" \
|
||||||
|
meta is-managed="true" target-role="Started" \
|
||||||
|
op monitor interval="10s" timeout="60" start interval="10s" timeout="240"
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
112
spec/libraries/cib_object_spec.rb
Normal file
112
spec/libraries/cib_object_spec.rb
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
require 'mixlib/shellout'
|
||||||
|
|
||||||
|
require_relative File.join(%w(.. .. libraries pacemaker))
|
||||||
|
require_relative File.join(%w(.. helpers keystone_primitive))
|
||||||
|
|
||||||
|
describe Pacemaker::CIBObject do
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
Mixlib::ShellOut.any_instance.stub(:run_command)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:cib_object) { Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE.dup }
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
# examples start here
|
||||||
|
|
||||||
|
context "no CIB object" do
|
||||||
|
before(:each) do
|
||||||
|
expect_any_instance_of(Mixlib::ShellOut) \
|
||||||
|
.to receive(:error!) \
|
||||||
|
.and_raise(RuntimeError)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#load_definition" do
|
||||||
|
it "should return nil cluster config" do
|
||||||
|
cib_object.load_definition
|
||||||
|
expect(cib_object.definition).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#exists?" do
|
||||||
|
it "should return false" do
|
||||||
|
cib_object.load_definition
|
||||||
|
expect(cib_object.exists?).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "keystone primitive resource CIB object" do
|
||||||
|
before(:each) do
|
||||||
|
Mixlib::ShellOut.any_instance.stub(:error!)
|
||||||
|
expect_any_instance_of(Mixlib::ShellOut) \
|
||||||
|
.to receive(:stdout) \
|
||||||
|
.and_return(cib_object.definition_string)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with definition loaded" do
|
||||||
|
before(:each) do
|
||||||
|
cib_object.load_definition
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#exists?" do
|
||||||
|
it "should return true" do
|
||||||
|
expect(cib_object.exists?).to be(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#load_definition" do
|
||||||
|
it "should retrieve cluster config" do
|
||||||
|
expect(cib_object.definition).to eq(cib_object.definition_string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#type" do
|
||||||
|
it "should return primitive" do
|
||||||
|
expect(cib_object.type).to eq("primitive")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "::from_name" do
|
||||||
|
it "should instantiate a Pacemaker::Resource::Primitive" do
|
||||||
|
obj = Pacemaker::CIBObject.from_name(cib_object.name)
|
||||||
|
expect(obj.is_a? Pacemaker::Resource::Primitive).to be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "CIB object with unregistered type" do
|
||||||
|
before(:each) do
|
||||||
|
Mixlib::ShellOut.any_instance.stub(:error!)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "::from_name" do
|
||||||
|
it "should refuse to instantiate from any subclass" do
|
||||||
|
expect_any_instance_of(Mixlib::ShellOut) \
|
||||||
|
.to receive(:stdout) \
|
||||||
|
.and_return("unregistered #{cib_object.name} <definition>")
|
||||||
|
expect {
|
||||||
|
Pacemaker::CIBObject.from_name(cib_object.name)
|
||||||
|
}.to raise_error "No subclass of Pacemaker::CIBObject was registered with type 'unregistered'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "invalid CIB object definition" do
|
||||||
|
before(:each) do
|
||||||
|
Mixlib::ShellOut.any_instance.stub(:error!)
|
||||||
|
expect_any_instance_of(Mixlib::ShellOut) \
|
||||||
|
.to receive(:stdout) \
|
||||||
|
.and_return("nonsense")
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#type" do
|
||||||
|
it "should raise an error without a valid definition" do
|
||||||
|
expect { cib_object.load_definition }.to \
|
||||||
|
raise_error(RuntimeError, "Couldn't extract CIB object type from 'nonsense'")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@@ -1,167 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
require_relative File.join(%w(.. .. libraries cib_objects))
|
|
||||||
require_relative File.join(%w(.. helpers keystone_config))
|
|
||||||
|
|
||||||
describe Chef::Libraries::Pacemaker::CIBObjects do
|
|
||||||
include Chef::Libraries::Pacemaker::CIBObjects
|
|
||||||
|
|
||||||
shared_context "shellout stubs" do
|
|
||||||
before(:each) do
|
|
||||||
Mixlib::ShellOut.any_instance.stub(:run_command)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_context "keystone config" do
|
|
||||||
let(:ra) { Chef::RSpec::Pacemaker::Config::RA }
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_context "keystone primitive" do
|
|
||||||
include_context "shellout stubs"
|
|
||||||
include_context "keystone config"
|
|
||||||
|
|
||||||
before(:each) do
|
|
||||||
Mixlib::ShellOut.any_instance.stub(:error!)
|
|
||||||
expect_any_instance_of(Mixlib::ShellOut) \
|
|
||||||
.to receive(:stdout) \
|
|
||||||
.and_return(ra[:config])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_context "no keystone primitive" do
|
|
||||||
include_context "shellout stubs"
|
|
||||||
before(:each) do
|
|
||||||
expect_any_instance_of(Mixlib::ShellOut) \
|
|
||||||
.to receive(:error!) \
|
|
||||||
.and_raise(RuntimeError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#get_cib_object_definition" do
|
|
||||||
include_context "keystone primitive"
|
|
||||||
|
|
||||||
it "should retrieve cluster config" do
|
|
||||||
expect(get_cib_object_definition("keystone")).to eq(ra[:config])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#get_cib_object_definition" do
|
|
||||||
include_context "no keystone primitive"
|
|
||||||
|
|
||||||
it "should return nil cluster config" do
|
|
||||||
expect(get_cib_object_definition("keystone")).to eq(nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#cib_object_exists?" do
|
|
||||||
include_context "keystone primitive"
|
|
||||||
|
|
||||||
it "should return true" do
|
|
||||||
expect(cib_object_exists?("keystone")).to be(true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#cib_object_exists?" do
|
|
||||||
include_context "no keystone primitive"
|
|
||||||
|
|
||||||
it "should return false" do
|
|
||||||
expect(cib_object_exists?("keystone")).to be(false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#cib_object_type" do
|
|
||||||
include_context "keystone config"
|
|
||||||
|
|
||||||
it "should return primitive" do
|
|
||||||
expect(cib_object_type(ra[:config])).to eq("primitive")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should raise an error without valid config" do
|
|
||||||
expect { cib_object_type("nonsense") }.to raise_error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#pacemaker_resource_running?" do
|
|
||||||
before(:each) do
|
|
||||||
@cmd = double(Mixlib::ShellOut)
|
|
||||||
expect(self).to receive(:shell_out!) \
|
|
||||||
.with(*%w(crm resource status keystone)).ordered \
|
|
||||||
.and_return(@cmd)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return true" do
|
|
||||||
expect(@cmd).to receive(:stdout).at_least(:once).and_return("resource keystone is running")
|
|
||||||
expect(pacemaker_resource_running?("keystone")).to be(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return false" do
|
|
||||||
expect(@cmd).to receive(:stdout).at_least(:once).and_return("resource keystone is not running")
|
|
||||||
expect(pacemaker_resource_running?("keystone")).to be(false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#resource_params_string" do
|
|
||||||
it "should return empty string with nil params" do
|
|
||||||
expect(resource_params_string(nil)).to eq("")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return empty string with empty params" do
|
|
||||||
expect(resource_params_string({})).to eq("")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return a resource params string" do
|
|
||||||
params = {
|
|
||||||
"foo" => "bar",
|
|
||||||
"baz" => "qux",
|
|
||||||
}
|
|
||||||
expect(resource_params_string(params)).to eq(%' params baz="qux" foo="bar"')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#resource_meta_string" do
|
|
||||||
it "should return empty string with nil meta" do
|
|
||||||
expect(resource_meta_string(nil)).to eq("")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return empty string with empty meta" do
|
|
||||||
expect(resource_meta_string({})).to eq("")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return a resource meta string" do
|
|
||||||
meta = {
|
|
||||||
"foo" => "bar",
|
|
||||||
"baz" => "qux",
|
|
||||||
}
|
|
||||||
expect(resource_meta_string(meta)).to eq(%' meta baz="qux" foo="bar"')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#resource_op_string" do
|
|
||||||
it "should return empty string with nil op" do
|
|
||||||
expect(resource_op_string(nil)).to eq("")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return empty string with empty op" do
|
|
||||||
expect(resource_op_string({})).to eq("")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return a resource op string" do
|
|
||||||
op = {
|
|
||||||
"monitor" => {
|
|
||||||
"foo" => "bar",
|
|
||||||
"baz" => "qux",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect(resource_op_string(op)).to eq(%' op monitor baz="qux" foo="bar"')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#extract_hash" do
|
|
||||||
include_context "keystone config"
|
|
||||||
|
|
||||||
it "should extract a hash from config" do
|
|
||||||
expect(extract_hash("keystone", ra[:config], "params")).to eq(Hash[ra[:params]])
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
108
spec/libraries/primitive_spec.rb
Normal file
108
spec/libraries/primitive_spec.rb
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
require_relative File.join(%w(.. .. libraries pacemaker resource primitive))
|
||||||
|
require_relative File.join(%w(.. helpers keystone_primitive))
|
||||||
|
|
||||||
|
describe Pacemaker::Resource::Primitive do
|
||||||
|
before(:each) do
|
||||||
|
@primitive = Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE.dup
|
||||||
|
Mixlib::ShellOut.any_instance.stub(:run_command)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should barf if the loaded definition's type is not primitive" do
|
||||||
|
Mixlib::ShellOut.any_instance.stub(:error!)
|
||||||
|
expect_any_instance_of(Mixlib::ShellOut) \
|
||||||
|
.to receive(:stdout) \
|
||||||
|
.and_return("clone foo blah blah")
|
||||||
|
expect { @primitive.load_definition }.to \
|
||||||
|
raise_error(Pacemaker::ObjectTypeMismatch,
|
||||||
|
"Expected primitive type but loaded definition was type clone")
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#params_string" do
|
||||||
|
it "should return empty string with nil params" do
|
||||||
|
@primitive.params = nil
|
||||||
|
expect(@primitive.params_string).to eq("")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return empty string with empty params" do
|
||||||
|
@primitive.params = {}
|
||||||
|
expect(@primitive.params_string).to eq("")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return a resource params string" do
|
||||||
|
@primitive.params = {
|
||||||
|
"foo" => "bar",
|
||||||
|
"baz" => "qux",
|
||||||
|
}
|
||||||
|
expect(@primitive.params_string).to eq(%' params baz="qux" foo="bar"')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#meta_string" do
|
||||||
|
it "should return empty string with nil meta" do
|
||||||
|
@primitive.meta = nil
|
||||||
|
expect(@primitive.meta_string).to eq("")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return empty string with empty meta" do
|
||||||
|
@primitive.meta = {}
|
||||||
|
expect(@primitive.meta_string).to eq("")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return a resource meta string" do
|
||||||
|
@primitive.meta = {
|
||||||
|
"foo" => "bar",
|
||||||
|
"baz" => "qux",
|
||||||
|
}
|
||||||
|
expect(@primitive.meta_string).to eq(%' meta baz="qux" foo="bar"')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#op_string" do
|
||||||
|
it "should return empty string with nil op" do
|
||||||
|
@primitive.op = nil
|
||||||
|
expect(@primitive.op_string).to eq("")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return empty string with empty op" do
|
||||||
|
@primitive.op = {}
|
||||||
|
expect(@primitive.op_string).to eq("")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return a resource op string" do
|
||||||
|
@primitive.op = {
|
||||||
|
"monitor" => {
|
||||||
|
"foo" => "bar",
|
||||||
|
"baz" => "qux",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(@primitive.op_string).to eq(%' op monitor baz="qux" foo="bar"')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "::extract_hash" do
|
||||||
|
it "should extract a hash from config" do
|
||||||
|
expect(@primitive.class.extract_hash(@primitive.definition_string, "params")).to \
|
||||||
|
eq(Hash[@primitive.params])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#definition_string" do
|
||||||
|
it "should return the definition string" do
|
||||||
|
expect(@primitive.definition_string).to \
|
||||||
|
eq(Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE_DEFINITION)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#parse_definition" do
|
||||||
|
before(:each) do
|
||||||
|
@parsed = Pacemaker::Resource::Primitive.new(@primitive.name)
|
||||||
|
@parsed.definition = Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE_DEFINITION
|
||||||
|
@parsed.parse_definition
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should parse the agent" do
|
||||||
|
expect(@parsed.agent).to eq(@primitive.agent)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
27
spec/libraries/resource_spec.rb
Normal file
27
spec/libraries/resource_spec.rb
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
require_relative File.join(%w(.. .. libraries pacemaker resource))
|
||||||
|
|
||||||
|
describe Pacemaker::Resource do
|
||||||
|
describe "#running?" do
|
||||||
|
let(:rsc) { Pacemaker::Resource.new('keystone') }
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
@cmd = double(Mixlib::ShellOut)
|
||||||
|
expect(rsc).to receive(:shell_out!) \
|
||||||
|
.with(*%w(crm resource status keystone)) \
|
||||||
|
.and_return(@cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return true" do
|
||||||
|
expect(@cmd).to receive(:stdout).at_least(:once) \
|
||||||
|
.and_return("resource #{rsc.name} is running on: d52-54-00-e5-6b-a0")
|
||||||
|
expect(rsc.running?).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return false" do
|
||||||
|
expect(@cmd).to receive(:stdout).at_least(:once) \
|
||||||
|
.and_return("resource #{rsc.name} is NOT running")
|
||||||
|
expect(rsc.running?).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@@ -1,12 +1,12 @@
|
|||||||
require 'chef/application'
|
require 'chef/application'
|
||||||
require_relative File.join(%w(.. spec_helper))
|
require_relative File.join(%w(.. spec_helper))
|
||||||
require_relative File.join(%w(.. helpers keystone_config))
|
require_relative File.join(%w(.. helpers keystone_primitive))
|
||||||
|
|
||||||
describe "Chef::Provider::PacemakerPrimitive" do
|
describe "Chef::Provider::PacemakerPrimitive" do
|
||||||
# for use inside examples:
|
# for use inside examples:
|
||||||
let(:ra) { Chef::RSpec::Pacemaker::Config::RA }
|
let(:rsc) { Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE }
|
||||||
# for use outside examples (e.g. when invoking shared_examples)
|
# for use outside examples (e.g. when invoking shared_examples)
|
||||||
ra = Chef::RSpec::Pacemaker::Config::RA
|
rsc = Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
runner_opts = {
|
runner_opts = {
|
||||||
@@ -17,27 +17,48 @@ describe "Chef::Provider::PacemakerPrimitive" do
|
|||||||
@node = @chef_run.node
|
@node = @chef_run.node
|
||||||
@run_context = @chef_run.run_context
|
@run_context = @chef_run.run_context
|
||||||
|
|
||||||
@resource = Chef::Resource::PacemakerPrimitive.new(ra[:name], @run_context)
|
@resource = Chef::Resource::PacemakerPrimitive.new(rsc.name, @run_context)
|
||||||
@resource.agent ra[:agent]
|
@resource.agent rsc.agent
|
||||||
@resource.params Hash[ra[:params]]
|
@resource.params Hash[rsc.params]
|
||||||
@resource.meta Hash[ra[:meta]]
|
@resource.meta Hash[rsc.meta]
|
||||||
@resource.op Hash[ra[:op]]
|
@resource.op Hash[rsc.op]
|
||||||
end
|
end
|
||||||
|
|
||||||
let (:provider) { Chef::Provider::PacemakerPrimitive.new(@resource, @run_context) }
|
let (:provider) { Chef::Provider::PacemakerPrimitive.new(@resource, @run_context) }
|
||||||
|
|
||||||
|
# "crm configure show" is executed by load_current_resource, and
|
||||||
|
# again later on for the :create action, to see whether to create or
|
||||||
|
# modify.
|
||||||
|
def expect_definition(definition)
|
||||||
|
Mixlib::ShellOut.any_instance.stub(:run_command)
|
||||||
|
Mixlib::ShellOut.any_instance.stub(:error!)
|
||||||
|
expect_any_instance_of(Mixlib::ShellOut) \
|
||||||
|
.to receive(:stdout) \
|
||||||
|
.and_return(definition)
|
||||||
|
end
|
||||||
|
|
||||||
|
def expect_exists(exists)
|
||||||
|
expect_any_instance_of(Pacemaker::Resource::Primitive) \
|
||||||
|
.to receive(:exists?) \
|
||||||
|
.and_return(exists)
|
||||||
|
end
|
||||||
|
|
||||||
|
def expect_running(running)
|
||||||
|
expect_any_instance_of(Pacemaker::Resource::Primitive) \
|
||||||
|
.to receive(:running?) \
|
||||||
|
.and_return(running)
|
||||||
|
end
|
||||||
|
|
||||||
describe ":create action" do
|
describe ":create action" do
|
||||||
it "should modify the primitive if it already exists" do
|
it "should modify the primitive if it already exists" do
|
||||||
new_params = Hash[ra[:params]].merge("os_password" => "newpasswd")
|
new_params = Hash[rsc.params].merge("os_password" => "newpasswd")
|
||||||
new_params.delete("os_tenant_name")
|
new_params.delete("os_tenant_name")
|
||||||
@resource.params new_params
|
@resource.params new_params
|
||||||
@resource.meta Hash[ra[:meta]].merge("target-role" => "Stopped")
|
@resource.meta Hash[rsc.meta].merge("target-role" => "Stopped")
|
||||||
|
|
||||||
# get_cib_object_definition is invoked by load_current_resource
|
expect_definition(rsc.definition_string)
|
||||||
# and later used to see whether to create or modify.
|
|
||||||
expect(provider).to receive(:get_cib_object_definition).and_return(ra[:config])
|
|
||||||
|
|
||||||
configure_cmd_prefix = "crm_resource --resource #{ra[:name]}"
|
configure_cmd_prefix = "crm_resource --resource #{rsc.name}"
|
||||||
expected_configure_cmd_args = [
|
expected_configure_cmd_args = [
|
||||||
%'--set-parameter "os_password" --parameter-value "newpasswd"',
|
%'--set-parameter "os_password" --parameter-value "newpasswd"',
|
||||||
%'--delete-parameter "os_tenant_name"',
|
%'--delete-parameter "os_tenant_name"',
|
||||||
@@ -45,6 +66,7 @@ describe "Chef::Provider::PacemakerPrimitive" do
|
|||||||
]
|
]
|
||||||
|
|
||||||
provider.run_action :create
|
provider.run_action :create
|
||||||
|
|
||||||
expected_configure_cmd_args.each do |args|
|
expected_configure_cmd_args.each do |args|
|
||||||
cmd = configure_cmd_prefix + " " + args
|
cmd = configure_cmd_prefix + " " + args
|
||||||
expect(@chef_run).to run_execute(cmd)
|
expect(@chef_run).to run_execute(cmd)
|
||||||
@@ -53,33 +75,24 @@ describe "Chef::Provider::PacemakerPrimitive" do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "should create a primitive if it doesn't already exist" do
|
it "should create a primitive if it doesn't already exist" do
|
||||||
# get_cib_object_definition is invoked by load_current_resource
|
expect_definition("")
|
||||||
# and later used to see whether to create or modify.
|
# Later, the :create action calls Pacemaker::Resource::Primitive#exists? to check
|
||||||
expect(provider).to receive(:get_cib_object_definition).and_return("")
|
|
||||||
|
|
||||||
# Later, the :create action calls cib_object_exists? to check
|
|
||||||
# that creation succeeded.
|
# that creation succeeded.
|
||||||
expect(provider).to receive(:cib_object_exists?).and_return(true)
|
expect_exists(true)
|
||||||
|
|
||||||
provider.run_action :create
|
provider.run_action :create
|
||||||
|
|
||||||
cmd = "crm configure primitive #{ra[:name]} #{ra[:agent]}" + \
|
expect(@chef_run).to run_execute(rsc.crm_configure_command)
|
||||||
ra[:params_string] + ra[:meta_string] + ra[:op_string]
|
|
||||||
expect(@chef_run).to run_execute(cmd)
|
|
||||||
|
|
||||||
expect(@resource).to be_updated
|
expect(@resource).to be_updated
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should barf if the primitive has the wrong agent" do
|
it "should barf if the primitive is already defined with the wrong agent" do
|
||||||
existing_agent = "ocf:openstack:something-else"
|
existing_agent = "ocf:openstack:something-else"
|
||||||
config = ra[:config].sub(ra[:agent], existing_agent)
|
definition = rsc.definition_string.sub(rsc.agent, existing_agent)
|
||||||
|
expect_definition(definition)
|
||||||
# get_cib_object_definition is invoked by load_current_resource
|
|
||||||
# and later used to see whether to create or modify.
|
|
||||||
expect(provider).to receive(:get_cib_object_definition).and_return(config)
|
|
||||||
|
|
||||||
expected_error = \
|
expected_error = \
|
||||||
"Existing resource primitive '#{ra[:name]}' has agent '#{existing_agent}' " \
|
"Existing resource primitive '#{rsc.name}' has agent '#{existing_agent}' " \
|
||||||
"but recipe wanted '#{@resource.agent}'"
|
"but recipe wanted '#{@resource.agent}'"
|
||||||
expect { provider.run_action :create }.to \
|
expect { provider.run_action :create }.to \
|
||||||
raise_error(RuntimeError, expected_error)
|
raise_error(RuntimeError, expected_error)
|
||||||
@@ -90,8 +103,7 @@ describe "Chef::Provider::PacemakerPrimitive" do
|
|||||||
|
|
||||||
shared_examples "action on non-existent resource" do |action, cmd, expected_error|
|
shared_examples "action on non-existent resource" do |action, cmd, expected_error|
|
||||||
it "should not attempt to #{action.to_s} a non-existent resource" do
|
it "should not attempt to #{action.to_s} a non-existent resource" do
|
||||||
# get_cib_object_definition is invoked by load_current_resource
|
expect_definition("")
|
||||||
expect(provider).to receive(:get_cib_object_definition).once.and_return("")
|
|
||||||
|
|
||||||
if expected_error
|
if expected_error
|
||||||
expect { provider.run_action action }.to \
|
expect { provider.run_action action }.to \
|
||||||
@@ -107,28 +119,28 @@ describe "Chef::Provider::PacemakerPrimitive" do
|
|||||||
|
|
||||||
describe ":delete action" do
|
describe ":delete action" do
|
||||||
it_should_behave_like "action on non-existent resource", \
|
it_should_behave_like "action on non-existent resource", \
|
||||||
:delete, "crm configure delete #{ra[:name]}", nil
|
:delete, "crm configure delete #{rsc.name}", nil
|
||||||
|
|
||||||
it "should not delete a running resource" do
|
it "should not delete a running resource" do
|
||||||
# get_cib_object_definition is invoked by load_current_resource
|
expect_definition(rsc.definition_string)
|
||||||
expect(provider).to receive(:get_cib_object_definition).once.and_return(ra[:config])
|
expect_running(true)
|
||||||
|
|
||||||
expect(provider).to receive(:pacemaker_resource_running?).once.and_return(true)
|
expected_error = "Cannot delete running resource primitive #{rsc.name}"
|
||||||
cmd = "crm configure delete #{ra[:name]}"
|
|
||||||
expected_error = "Cannot delete running resource primitive #{ra[:name]}"
|
|
||||||
expect { provider.run_action :delete }.to \
|
expect { provider.run_action :delete }.to \
|
||||||
raise_error(RuntimeError, expected_error)
|
raise_error(RuntimeError, expected_error)
|
||||||
|
|
||||||
|
cmd = "crm configure delete '#{rsc.name}'"
|
||||||
expect(@chef_run).not_to run_execute(cmd)
|
expect(@chef_run).not_to run_execute(cmd)
|
||||||
expect(@resource).not_to be_updated
|
expect(@resource).not_to be_updated
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should delete a non-running resource" do
|
it "should delete a non-running resource" do
|
||||||
# get_cib_object_definition is invoked by load_current_resource
|
expect_definition(rsc.definition_string)
|
||||||
expect(provider).to receive(:get_cib_object_definition).once.and_return(ra[:config])
|
expect_running(false)
|
||||||
|
|
||||||
expect(provider).to receive(:pacemaker_resource_running?).once.and_return(false)
|
|
||||||
cmd = "crm configure delete #{ra[:name]}"
|
|
||||||
provider.run_action :delete
|
provider.run_action :delete
|
||||||
|
|
||||||
|
cmd = "crm configure delete '#{rsc.name}'"
|
||||||
expect(@chef_run).to run_execute(cmd)
|
expect(@chef_run).to run_execute(cmd)
|
||||||
expect(@resource).to be_updated
|
expect(@resource).to be_updated
|
||||||
end
|
end
|
||||||
@@ -137,30 +149,28 @@ describe "Chef::Provider::PacemakerPrimitive" do
|
|||||||
describe ":start action" do
|
describe ":start action" do
|
||||||
it_should_behave_like "action on non-existent resource", \
|
it_should_behave_like "action on non-existent resource", \
|
||||||
:start,
|
:start,
|
||||||
"crm resource start #{ra[:name]}", \
|
"crm resource start #{rsc.name}", \
|
||||||
"Cannot start non-existent resource primitive '#{ra[:name]}'"
|
"Cannot start non-existent resource primitive '#{rsc.name}'"
|
||||||
|
|
||||||
it "should do nothing to a started resource" do
|
it "should do nothing to a started resource" do
|
||||||
# get_cib_object_definition is invoked by load_current_resource
|
expect_definition(rsc.definition_string)
|
||||||
expect(provider).to receive(:get_cib_object_definition).and_return(ra[:config])
|
expect_running(true)
|
||||||
|
|
||||||
expect(provider).to receive(:pacemaker_resource_running?).once.and_return(true)
|
|
||||||
|
|
||||||
cmd = "crm resource start #{ra[:name]}"
|
|
||||||
provider.run_action :start
|
provider.run_action :start
|
||||||
|
|
||||||
|
cmd = "crm resource start #{rsc.name}"
|
||||||
expect(@chef_run).not_to run_execute(cmd)
|
expect(@chef_run).not_to run_execute(cmd)
|
||||||
expect(@resource).not_to be_updated
|
expect(@resource).not_to be_updated
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should start a stopped resource" do
|
it "should start a stopped resource" do
|
||||||
config = ra[:config].sub("Started", "Stopped")
|
config = rsc.definition_string.sub("Started", "Stopped")
|
||||||
# get_cib_object_definition is invoked by load_current_resource
|
expect_definition(config)
|
||||||
expect(provider).to receive(:get_cib_object_definition).and_return(config)
|
expect_running(false)
|
||||||
|
|
||||||
expect(provider).to receive(:pacemaker_resource_running?).once.and_return(false)
|
|
||||||
|
|
||||||
cmd = "crm resource start #{ra[:name]}"
|
|
||||||
provider.run_action :start
|
provider.run_action :start
|
||||||
|
|
||||||
|
cmd = "crm resource start '#{rsc.name}'"
|
||||||
expect(@chef_run).to run_execute(cmd)
|
expect(@chef_run).to run_execute(cmd)
|
||||||
expect(@resource).to be_updated
|
expect(@resource).to be_updated
|
||||||
end
|
end
|
||||||
@@ -169,29 +179,27 @@ describe "Chef::Provider::PacemakerPrimitive" do
|
|||||||
describe ":stop action" do
|
describe ":stop action" do
|
||||||
it_should_behave_like "action on non-existent resource", \
|
it_should_behave_like "action on non-existent resource", \
|
||||||
:stop,
|
:stop,
|
||||||
"crm resource stop #{ra[:name]}", \
|
"crm resource stop #{rsc.name}", \
|
||||||
"Cannot stop non-existent resource primitive '#{ra[:name]}'"
|
"Cannot stop non-existent resource primitive '#{rsc.name}'"
|
||||||
|
|
||||||
it "should do nothing to a stopped resource" do
|
it "should do nothing to a stopped resource" do
|
||||||
# get_cib_object_definition is invoked by load_current_resource
|
expect_definition(rsc.definition_string)
|
||||||
expect(provider).to receive(:get_cib_object_definition).and_return(ra[:config])
|
expect_running(false)
|
||||||
|
|
||||||
expect(provider).to receive(:pacemaker_resource_running?).once.and_return(false)
|
|
||||||
|
|
||||||
cmd = "crm resource start #{ra[:name]}"
|
|
||||||
provider.run_action :stop
|
provider.run_action :stop
|
||||||
|
|
||||||
|
cmd = "crm resource start #{rsc.name}"
|
||||||
expect(@chef_run).not_to run_execute(cmd)
|
expect(@chef_run).not_to run_execute(cmd)
|
||||||
expect(@resource).not_to be_updated
|
expect(@resource).not_to be_updated
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should stop a started resource" do
|
it "should stop a started resource" do
|
||||||
# get_cib_object_definition is invoked by load_current_resource
|
expect_definition(rsc.definition_string)
|
||||||
expect(provider).to receive(:get_cib_object_definition).and_return(ra[:config])
|
expect_running(true)
|
||||||
|
|
||||||
expect(provider).to receive(:pacemaker_resource_running?).once.and_return(true)
|
|
||||||
|
|
||||||
cmd = "crm resource stop #{ra[:name]}"
|
|
||||||
provider.run_action :stop
|
provider.run_action :stop
|
||||||
|
|
||||||
|
cmd = "crm resource stop '#{rsc.name}'"
|
||||||
expect(@chef_run).to run_execute(cmd)
|
expect(@chef_run).to run_execute(cmd)
|
||||||
expect(@resource).to be_updated
|
expect(@resource).to be_updated
|
||||||
end
|
end
|
||||||
|
@@ -17,7 +17,7 @@ RSpec.configure do |config|
|
|||||||
#config.role_path = '/var/roles'
|
#config.role_path = '/var/roles'
|
||||||
|
|
||||||
# Specify the Chef log_level (default: :warn)
|
# Specify the Chef log_level (default: :warn)
|
||||||
#config.log_level = :debug
|
config.log_level = ENV['CHEF_LOG_LEVEL'].to_sym if ENV['CHEF_LOG_LEVEL']
|
||||||
|
|
||||||
# Specify the path to a local JSON file with Ohai data (default: nil)
|
# Specify the path to a local JSON file with Ohai data (default: nil)
|
||||||
#config.path = 'ohai.json'
|
#config.path = 'ohai.json'
|
||||||
|
Reference in New Issue
Block a user