Retire stackforge/cookbook-pacemaker
This commit is contained in:
parent
5b4bb9262d
commit
5f8b586576
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
Gemfile.lock
|
||||
tmp/
|
21
Gemfile
21
Gemfile
@ -1,21 +0,0 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
#gem 'berkshelf', '~> 2.0'
|
||||
|
||||
group :test, :development do
|
||||
gem 'chefspec', '~> 3.0'
|
||||
gem 'rspec-expectations', '~> 2.14.0'
|
||||
gem 'rubydeps'
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem 'foodcritic', '~> 3.0'
|
||||
gem 'rubocop'
|
||||
gem 'jazz_hands'
|
||||
gem 'guard-rspec'
|
||||
gem 'guard-bundler'
|
||||
# Prevent "Error: can't modify string; temporarily locked"
|
||||
# http://stackoverflow.com/a/19505033/179332
|
||||
gem "rb-readline", "~> 0.5.0"
|
||||
end
|
||||
|
49
Guardfile
49
Guardfile
@ -1,49 +0,0 @@
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# More info at https://github.com/guard/guard#readme
|
||||
|
||||
guard_opts = {
|
||||
all_on_start: true,
|
||||
all_after_pass: true,
|
||||
}
|
||||
|
||||
DEBUG = false
|
||||
|
||||
def reload(target)
|
||||
puts "-> #{target}" if DEBUG
|
||||
target
|
||||
end
|
||||
|
||||
def all_specs; reload 'all_specs'; 'spec' end
|
||||
def library_specs; reload 'library_specs'; 'spec/libraries' end
|
||||
def provider_specs; reload 'provider_specs'; 'spec/providers' end
|
||||
|
||||
group :rspec do
|
||||
guard 'rspec', guard_opts do
|
||||
watch(%r{^Gemfile$}) { all_specs }
|
||||
watch(%r{^Gemfile.lock$}) { all_specs }
|
||||
watch(%r{^spec/spec_helper\.rb$}) { all_specs }
|
||||
watch(%r{^spec/helpers/.+\.rb$}) { all_specs }
|
||||
watch(%r{^spec/fixtures/.+\.rb$}) { all_specs }
|
||||
watch(%r{^libraries/pacemaker\.rb$}) { all_specs }
|
||||
watch(%r{^libraries/pacemaker/[^/]+\.rb$}) \
|
||||
{ all_specs }
|
||||
watch(%r{^libraries/(.*mixin.*)\.rb$}) { all_specs }
|
||||
watch(%r{^(spec/.+_spec\.rb)$}) { |m| reload m[1] }
|
||||
watch(%r{^libraries/(.+)\.rb$}) { |m|
|
||||
reload "spec/libraries/#{m[1]}_spec.rb"
|
||||
}
|
||||
watch(%r{^recipes/.+\.rb$}) { provider_specs }
|
||||
watch(%r{^providers/common\.rb$}) { provider_specs }
|
||||
watch(%r{^providers/(.*mixin.*)\.rb$}) { provider_specs }
|
||||
watch(%r{^(?:resources|providers)/(.+)\.rb$}) { |m|
|
||||
reload "spec/providers/#{m[1]}_spec.rb"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
group :bundler do
|
||||
guard 'bundler' do
|
||||
watch('Gemfile')
|
||||
end
|
||||
end
|
77
README.md
77
README.md
@ -1,77 +0,0 @@
|
||||
[](https://travis-ci.org/crowbar/barclamp-pacemaker)
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
This is a cookbook for installing and configuring pacemaker.
|
||||
|
||||
Recipes
|
||||
=======
|
||||
default
|
||||
-------
|
||||
Installs and start `pacemaker`.
|
||||
|
||||
Resources/Providers
|
||||
===================
|
||||
There are 7 LWRPs for interacting with pacemaker.
|
||||
|
||||
primitive
|
||||
----------
|
||||
Configure and delete primitive resource.
|
||||
|
||||
- `:create` configures a `primitive`
|
||||
- `:delete` deletes a `primitive`
|
||||
|
||||
### Examples
|
||||
``` ruby
|
||||
pacemaker_primitive drbd do
|
||||
agent "ocf:linbit:drbd"
|
||||
params {'drbd_resource' => 'r0'}
|
||||
op {'monitor' => { 'interval' => '5s', 'role' => 'Master' } }
|
||||
action :create
|
||||
end
|
||||
```
|
||||
|
||||
clone
|
||||
-----
|
||||
TBU
|
||||
|
||||
ms
|
||||
--
|
||||
TBU
|
||||
|
||||
location
|
||||
--------
|
||||
TBU
|
||||
|
||||
colocation
|
||||
----------
|
||||
TBU
|
||||
|
||||
order
|
||||
-----
|
||||
TBU
|
||||
|
||||
node
|
||||
----
|
||||
TBU
|
||||
|
||||
|
||||
License and Author
|
||||
==================
|
||||
|
||||
Author:: Robert Choi <taeilchoi1@gmail.com>
|
||||
|
||||
Copyright:: 2013 Robert Choi
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
7
README.rst
Normal file
7
README.rst
Normal file
@ -0,0 +1,7 @@
|
||||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git source code
|
||||
management system. To see the contents of this repository before it reached
|
||||
its end of life, please check out the previous commit with
|
||||
"git checkout HEAD^1".
|
||||
|
47
Rakefile
47
Rakefile
@ -1,47 +0,0 @@
|
||||
IGNORED_CLASSES = ['RSpec::Core::ExampleGroup']
|
||||
DUMP_FILE = 'rubydeps.dump'
|
||||
DOT_FILE = 'rubydeps.dot'
|
||||
SVG_FILE = 'rubydeps.svg'
|
||||
|
||||
task :default => "rubydeps:svg"
|
||||
|
||||
file DUMP_FILE do
|
||||
sh 'RUBYDEPS=y rspec'
|
||||
end
|
||||
|
||||
file DOT_FILE => DUMP_FILE do
|
||||
ignore_regexp = IGNORED_CLASSES.join "|"
|
||||
sh "rubydeps --class-name-filter='^(?!#{ignore_regexp})'"
|
||||
dot = File.read(DOT_FILE)
|
||||
dot.gsub!('rankdir=LR', 'rankdir=TB')
|
||||
# Unfortunately due to https://github.com/dcadenas/rubydeps/issues/4
|
||||
# we need to manually exclude some superfluous dependencies which
|
||||
# go in the wrong direction.
|
||||
dot.gsub!(/\\\n/, '')
|
||||
dot.gsub!(/^(?=\s+Object )/, '#')
|
||||
dot.gsub!(/^(?=\s+"Pacemaker::Resource::Meta" ->)/, '#')
|
||||
dot.gsub!(/^(?=\s+"Pacemaker::CIBObject" ->)/, '#')
|
||||
dot.gsub!(/^(?=\s+"Chef::Mixin::Pacemaker::StandardCIBObject" -> "(?!Pacemaker::CIBObject))/, '#')
|
||||
dot.gsub!(/^(?=\s+"Chef::Mixin::Pacemaker::RunnableResource" -> "(?!Pacemaker::CIBObject))/, '#')
|
||||
File.open(DOT_FILE, 'w') { |f| f.write(dot) }
|
||||
end
|
||||
|
||||
file SVG_FILE => DOT_FILE do
|
||||
sh "dot -Tsvg #{DOT_FILE} > #{SVG_FILE}"
|
||||
end
|
||||
|
||||
namespace :rubydeps do
|
||||
desc "Clean rubydeps dump"
|
||||
task :clean do
|
||||
FileUtils.rm_f([DUMP_FILE])
|
||||
end
|
||||
|
||||
desc "Regenerate #{DUMP_FILE}"
|
||||
task :dump => DUMP_FILE
|
||||
|
||||
desc "Regenerate #{DOT_FILE}"
|
||||
task :dot => DOT_FILE
|
||||
|
||||
desc "Regenerate #{SVG_FILE}"
|
||||
task :svg => SVG_FILE
|
||||
end
|
@ -1,72 +0,0 @@
|
||||
# Copyright 2011, Dell, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
case node.platform
|
||||
when 'suse'
|
||||
default[:pacemaker][:platform][:packages] = %w(pacemaker crmsh)
|
||||
|
||||
# pacemaker-mgmt-client provides hb_gui, which it's useful
|
||||
# to run over ssh. Note that pacemaker-mgmt needs to be installed
|
||||
# *before* the openais service is started, otherwise the mgmtd
|
||||
# plugin won't be forked as a child process.
|
||||
default[:pacemaker][:platform][:graphical_packages] = %w(
|
||||
pacemaker-mgmt pacemaker-mgmt-client
|
||||
xorg-x11-xauth xorg-x11-fonts
|
||||
)
|
||||
else
|
||||
Chef::Application.fatal! "FIXME: #{node.platform} platform not supported yet"
|
||||
return
|
||||
end
|
||||
|
||||
default[:pacemaker][:founder] = false
|
||||
default[:pacemaker][:crm][:initial_config_file] = "/etc/corosync/crm-initial.conf"
|
||||
default[:pacemaker][:crm][:no_quorum_policy] = "ignore"
|
||||
default[:pacemaker][:crm][:op_default_timeout] = 60
|
||||
|
||||
# Values can be "disabled", "manual", "sbd", "shared", "per_node"
|
||||
default[:pacemaker][:stonith][:mode] = "disabled"
|
||||
|
||||
# This hash will contain devices for each node.
|
||||
# For instance:
|
||||
# default[:pacemaker][:stonith][:sbd][:nodes][$node][:devices] = ['/dev/disk/by-id/foo-part1', '/dev/disk/by-id/bar-part1']
|
||||
default[:pacemaker][:stonith][:sbd][:nodes] = {}
|
||||
|
||||
default[:pacemaker][:stonith][:shared][:agent] = ""
|
||||
# This can be either a string (containing a list of parameters) or a hash.
|
||||
# For instance:
|
||||
# default[:pacemaker][:stonith][:shared][:params] = 'hostname="foo" password="bar"'
|
||||
# will give the same result as:
|
||||
# default[:pacemaker][:stonith][:shared][:params] = {"hostname" => "foo", "password" => "bar"}
|
||||
default[:pacemaker][:stonith][:shared][:params] = {}
|
||||
|
||||
default[:pacemaker][:stonith][:per_node][:agent] = ""
|
||||
# This can be "all" or "self":
|
||||
# - if set to "all", then every node will configure the stonith resources for
|
||||
# all nodes in the cluster
|
||||
# - if set to "self", then every node will configure the stonith resource for
|
||||
# itself only
|
||||
default[:pacemaker][:stonith][:per_node][:mode] = "all"
|
||||
# This hash will contain parameters for each node. See documentation for
|
||||
# default[:pacemaker][:stonith][:shared][:params] about the format.
|
||||
# For instance:
|
||||
# default[:pacemaker][:stonith][:per_node][:nodes][$node][:params] = 'hostname="foo" password="bar"'
|
||||
default[:pacemaker][:stonith][:per_node][:nodes] = {}
|
||||
|
||||
default[:pacemaker][:notifications][:agent] = "ocf:heartbeat:ClusterMon"
|
||||
default[:pacemaker][:notifications][:smtp][:enabled] = false
|
||||
default[:pacemaker][:notifications][:smtp][:to] = ""
|
||||
default[:pacemaker][:notifications][:smtp][:from] = ""
|
||||
default[:pacemaker][:notifications][:smtp][:server] = ""
|
||||
default[:pacemaker][:notifications][:smtp][:prefix] = ""
|
@ -1,3 +0,0 @@
|
||||
# managed by Chef
|
||||
# start corosync at boot [yes|no]
|
||||
START=yes
|
@ -1,3 +0,0 @@
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('pacemaker/standard_cib_object.rb', this_dir)
|
||||
require File.expand_path('pacemaker/runnable_resource.rb', this_dir)
|
@ -1,46 +0,0 @@
|
||||
require ::File.expand_path('standard_cib_object', File.dirname(__FILE__))
|
||||
|
||||
# Common code used by Pacemaker LWRP providers for resources supporting
|
||||
# the :run action.
|
||||
|
||||
class Chef
|
||||
module Mixin::Pacemaker
|
||||
module RunnableResource
|
||||
include StandardCIBObject
|
||||
|
||||
def start_runnable_resource
|
||||
name = new_resource.name
|
||||
unless @current_resource
|
||||
raise "Cannot start non-existent #{cib_object_class.description} '#{name}'"
|
||||
end
|
||||
return if @current_cib_object.running?
|
||||
execute @current_cib_object.crm_start_command do
|
||||
action :nothing
|
||||
end.run_action(:run)
|
||||
new_resource.updated_by_last_action(true)
|
||||
Chef::Log.info "Successfully started #{@current_cib_object}"
|
||||
end
|
||||
|
||||
def stop_runnable_resource
|
||||
name = new_resource.name
|
||||
unless @current_resource
|
||||
raise "Cannot stop non-existent #{cib_object_class.description} '#{name}'"
|
||||
end
|
||||
return unless @current_cib_object.running?
|
||||
execute @current_cib_object.crm_stop_command do
|
||||
action :nothing
|
||||
end.run_action(:run)
|
||||
new_resource.updated_by_last_action(true)
|
||||
Chef::Log.info "Successfully stopped #{@current_cib_object}"
|
||||
end
|
||||
|
||||
def delete_runnable_resource
|
||||
return unless @current_resource
|
||||
if @current_cib_object.running?
|
||||
raise "Cannot delete running #{@current_cib_object}"
|
||||
end
|
||||
standard_delete_resource
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,116 +0,0 @@
|
||||
require ::File.expand_path('../../../pacemaker/cib_object',
|
||||
File.dirname(__FILE__))
|
||||
|
||||
# Common code used by Pacemaker LWRP providers
|
||||
|
||||
class Chef
|
||||
module Mixin::Pacemaker
|
||||
module StandardCIBObject
|
||||
def standard_create_action
|
||||
name = new_resource.name
|
||||
|
||||
if @current_resource_definition.nil?
|
||||
create_resource(name)
|
||||
else
|
||||
maybe_modify_resource(name)
|
||||
end
|
||||
end
|
||||
|
||||
# Instantiate @current_resource and read details about the existing
|
||||
# primitive (if any) via "crm configure show" into it, so that we
|
||||
# can compare it against the resource requested by the recipe, and
|
||||
# create / delete / modify as necessary.
|
||||
#
|
||||
# http://docs.opscode.com/lwrp_custom_provider_ruby.html#load-current-resource
|
||||
def standard_load_current_resource
|
||||
name = @new_resource.name
|
||||
|
||||
cib_object = ::Pacemaker::CIBObject.from_name(name)
|
||||
unless cib_object
|
||||
::Chef::Log.debug "CIB object definition nil or empty"
|
||||
return
|
||||
end
|
||||
|
||||
unless cib_object.is_a? cib_object_class
|
||||
expected_type = cib_object_class.description
|
||||
::Chef::Log.warn "CIB object '#{name}' was a #{cib_object.type} not a #{expected_type}"
|
||||
return
|
||||
end
|
||||
|
||||
::Chef::Log.debug "CIB object definition #{cib_object.definition}"
|
||||
@current_resource_definition = cib_object.definition
|
||||
cib_object.parse_definition
|
||||
|
||||
@current_cib_object = cib_object
|
||||
init_current_resource
|
||||
end
|
||||
|
||||
# In Pacemaker, target-role defaults to 'Started', but we want
|
||||
# to allow consumers of the LWRPs the choice whether their
|
||||
# newly created resource gets started or not, and we also want
|
||||
# to adhere to the Principle of Least Surprise. Therefore we
|
||||
# stick to the intuitive semantics that
|
||||
#
|
||||
# action :create
|
||||
#
|
||||
# creates the resource with target-role="Stopped" in order to
|
||||
# prevent it from starting immediately, whereas
|
||||
#
|
||||
# action [:create, :start]
|
||||
#
|
||||
# creates the resource and then starts it.
|
||||
#
|
||||
# Consequently we deprecate setting target-role values directly
|
||||
# via the meta attribute.
|
||||
def deprecate_target_role
|
||||
if new_resource.respond_to? :meta
|
||||
meta = new_resource.meta
|
||||
if meta && meta['target-role']
|
||||
::Chef::Log.warn "#{new_resource} used deprecated target-role " +
|
||||
"#{meta['target-role']}; use action :start / :stop instead"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def standard_create_resource
|
||||
deprecate_target_role
|
||||
|
||||
cib_object = cib_object_class.from_chef_resource(new_resource)
|
||||
|
||||
# We don't want resources to automatically start on creation;
|
||||
# only when the :create action is invoked. However Pacemaker
|
||||
# defaults target-role to "Started", so we need to override it.
|
||||
if cib_object.respond_to? :meta # might be a constraint
|
||||
cib_object.meta['target-role'] = 'Stopped'
|
||||
end
|
||||
|
||||
cmd = cib_object.configure_command
|
||||
|
||||
::Chef::Log.info "Creating new #{cib_object}"
|
||||
|
||||
execute cmd do
|
||||
action :nothing
|
||||
end.run_action(:run)
|
||||
|
||||
created_cib_object = ::Pacemaker::CIBObject.from_name(new_resource.name)
|
||||
|
||||
raise "Failed to create #{cib_object}" if created_cib_object.nil?
|
||||
unless created_cib_object.exists?
|
||||
# This case seems pretty unlikely
|
||||
raise "Definition missing for #{created_cib_object} after creation"
|
||||
end
|
||||
|
||||
new_resource.updated_by_last_action(true)
|
||||
::Chef::Log.info "Successfully configured #{created_cib_object}"
|
||||
end
|
||||
|
||||
def standard_delete_resource
|
||||
execute @current_cib_object.delete_command do
|
||||
action :nothing
|
||||
end.run_action(:run)
|
||||
new_resource.updated_by_last_action(true)
|
||||
Chef::Log.info "Deleted #{@current_cib_object}'."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,7 +0,0 @@
|
||||
this_dir = File.dirname(__FILE__)
|
||||
|
||||
require File.expand_path('pacemaker/resource/primitive', this_dir)
|
||||
require File.expand_path('pacemaker/resource/clone', this_dir)
|
||||
require File.expand_path('pacemaker/resource/ms', this_dir)
|
||||
require File.expand_path('pacemaker/resource/group', this_dir)
|
||||
require File.expand_path('pacemaker/constraint/colocation', this_dir)
|
@ -1,165 +0,0 @@
|
||||
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 definition_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 = definition_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
|
||||
|
||||
def from_chef_resource(resource)
|
||||
new(resource.name).copy_attrs_from_chef_resource(resource,
|
||||
*attrs_to_copy_from_chef)
|
||||
end
|
||||
|
||||
def attrs_to_copy_from_chef
|
||||
raise NotImplementedError, "#{self.class} didn't implement attrs_to_copy_from_chef"
|
||||
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.definition_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 configure_command
|
||||
"crm configure " + definition_string
|
||||
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
|
@ -1,10 +0,0 @@
|
||||
require File.expand_path('cib_object', File.dirname(__FILE__))
|
||||
|
||||
module Pacemaker
|
||||
class Constraint < Pacemaker::CIBObject
|
||||
def self.description
|
||||
type = self.to_s.split('::').last
|
||||
"#{type} constraint"
|
||||
end
|
||||
end
|
||||
end
|
@ -1,30 +0,0 @@
|
||||
require File.expand_path('../constraint', File.dirname(__FILE__))
|
||||
|
||||
class Pacemaker::Constraint::Colocation < Pacemaker::Constraint
|
||||
TYPE = 'colocation'
|
||||
register_type TYPE
|
||||
|
||||
attr_accessor :score, :resources
|
||||
|
||||
def self.attrs_to_copy_from_chef
|
||||
%w(score resources)
|
||||
end
|
||||
|
||||
def parse_definition
|
||||
# FIXME: this is incomplete. It probably doesn't handle resource
|
||||
# sets correctly, and certainly doesn't handle node attributes.
|
||||
# See the crm(8) man page for the official BNF grammar.
|
||||
unless definition =~ /^#{self.class::TYPE} (\S+) (\d+|[-+]?inf): (.+?)\s*$/
|
||||
raise Pacemaker::CIBObject::DefinitionParseError, \
|
||||
"Couldn't parse definition '#{definition}'"
|
||||
end
|
||||
self.name = $1
|
||||
self.score = $2
|
||||
self.resources = $3.split
|
||||
end
|
||||
|
||||
def definition_string
|
||||
"#{self.class::TYPE} #{name} #{score}: " + resources.join(' ')
|
||||
end
|
||||
|
||||
end
|
@ -1,31 +0,0 @@
|
||||
require File.expand_path('../constraint', File.dirname(__FILE__))
|
||||
|
||||
class Pacemaker::Constraint::Location < Pacemaker::Constraint
|
||||
TYPE = 'location'
|
||||
register_type TYPE
|
||||
|
||||
attr_accessor :rsc, :score, :node
|
||||
|
||||
def self.attrs_to_copy_from_chef
|
||||
%w(rsc score node)
|
||||
end
|
||||
|
||||
def parse_definition
|
||||
# FIXME: this is woefully incomplete, and doesn't cope with any of
|
||||
# the rules syntax. See the crm(8) man page for the official BNF
|
||||
# grammar.
|
||||
unless definition =~ /^#{self.class::TYPE} (\S+) (\S+) (\d+|[-+]?inf): (\S+)\s*$/
|
||||
raise Pacemaker::CIBObject::DefinitionParseError, \
|
||||
"Couldn't parse definition '#{definition}'"
|
||||
end
|
||||
self.name = $1
|
||||
self.rsc = $2
|
||||
self.score = $3
|
||||
self.node = $4
|
||||
end
|
||||
|
||||
def definition_string
|
||||
"#{self.class::TYPE} #{name} #{rsc} #{score}: #{node}"
|
||||
end
|
||||
|
||||
end
|
@ -1,31 +0,0 @@
|
||||
require File.expand_path('../constraint', File.dirname(__FILE__))
|
||||
|
||||
class Pacemaker::Constraint::Order < Pacemaker::Constraint
|
||||
TYPE = 'order'
|
||||
register_type TYPE
|
||||
|
||||
attr_accessor :score, :ordering
|
||||
|
||||
def self.attrs_to_copy_from_chef
|
||||
%w(score ordering)
|
||||
end
|
||||
|
||||
def parse_definition
|
||||
# FIXME: add support for symmetrical=<bool>
|
||||
# Currently we take the easy way out and don't bother parsing the ordering.
|
||||
# See the crm(8) man page for the official BNF grammar.
|
||||
score_regexp = %r{\d+|[-+]?inf|Mandatory|Optional|Serialize}
|
||||
unless definition =~ /^#{self.class::TYPE} (\S+) (#{score_regexp}): (.+?)\s*$/
|
||||
raise Pacemaker::CIBObject::DefinitionParseError, \
|
||||
"Couldn't parse definition '#{definition}'"
|
||||
end
|
||||
self.name = $1
|
||||
self.score = $2
|
||||
self.ordering = $3
|
||||
end
|
||||
|
||||
def definition_string
|
||||
"#{self.class::TYPE} #{name} #{score}: #{ordering}"
|
||||
end
|
||||
|
||||
end
|
@ -1,30 +0,0 @@
|
||||
# A mixin for Pacemaker::Resource subclasses which support meta attributes
|
||||
# (priority, target-role, is-managed, etc.)
|
||||
|
||||
module Pacemaker
|
||||
module Mixins
|
||||
module Resource
|
||||
module Meta
|
||||
def self.included(base)
|
||||
base.extend ClassMethods
|
||||
end
|
||||
|
||||
attr_accessor :meta
|
||||
|
||||
def meta_string
|
||||
self.class.meta_string(meta)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def meta_string(meta)
|
||||
return "" if ! meta or meta.empty?
|
||||
"meta " +
|
||||
meta.sort.map do |key, value|
|
||||
%'#{key}="#{value}"'
|
||||
end.join(' ')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,53 +0,0 @@
|
||||
require 'chef/mixin/shell_out'
|
||||
require File.expand_path('cib_object', File.dirname(__FILE__))
|
||||
|
||||
module Pacemaker
|
||||
class Resource < Pacemaker::CIBObject
|
||||
include Chef::Mixin::ShellOut
|
||||
|
||||
def self.description
|
||||
type = self.to_s.split('::').last.downcase
|
||||
"#{type} resource"
|
||||
end
|
||||
|
||||
def running?
|
||||
cmd = shell_out! "crm", "resource", "status", name
|
||||
Chef::Log.info cmd.stdout
|
||||
!! cmd.stdout.include?("resource #{name} is running")
|
||||
end
|
||||
|
||||
def crm_start_command
|
||||
"crm --force --wait resource start '#{name}'"
|
||||
end
|
||||
|
||||
def crm_stop_command
|
||||
"crm --force --wait resource stop '#{name}'"
|
||||
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*\\?$/
|
||||
return {}
|
||||
end
|
||||
|
||||
h = {}
|
||||
Shellwords.split($1).each do |kvpair|
|
||||
break if kvpair == 'op'
|
||||
unless kvpair =~ /^(.+?)=(.*)$/
|
||||
raise "Couldn't understand '#{kvpair}' for '#{data_type}' section "\
|
||||
"of #{name} primitive (definition was [#{obj_definition}])"
|
||||
end
|
||||
h[$1] = $2.sub(/^"(.*)"$/, "\1")
|
||||
end
|
||||
h
|
||||
end
|
||||
end
|
||||
end
|
@ -1,37 +0,0 @@
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../resource', this_dir)
|
||||
require File.expand_path('../mixins/resource_meta', this_dir)
|
||||
|
||||
class Pacemaker::Resource::Clone < Pacemaker::Resource
|
||||
TYPE = 'clone'
|
||||
register_type TYPE
|
||||
|
||||
include Pacemaker::Mixins::Resource::Meta
|
||||
|
||||
# FIXME: need to handle params as well as meta
|
||||
|
||||
attr_accessor :rsc
|
||||
|
||||
def self.attrs_to_copy_from_chef
|
||||
%w(rsc meta)
|
||||
end
|
||||
|
||||
def definition_string
|
||||
str = "#{self.class::TYPE} #{name} #{rsc}"
|
||||
unless meta.empty?
|
||||
str << continuation_line(meta_string)
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
def parse_definition
|
||||
unless definition =~ /^#{self.class::TYPE} (\S+) (\S+)/
|
||||
raise Pacemaker::CIBObject::DefinitionParseError, \
|
||||
"Couldn't parse definition '#{definition}'"
|
||||
end
|
||||
self.name = $1
|
||||
self.rsc = $2
|
||||
self.meta = self.class.extract_hash(definition, 'meta')
|
||||
end
|
||||
|
||||
end
|
@ -1,40 +0,0 @@
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../resource', this_dir)
|
||||
require File.expand_path('../mixins/resource_meta', this_dir)
|
||||
|
||||
class Pacemaker::Resource::Group < Pacemaker::Resource
|
||||
TYPE = 'group'
|
||||
register_type TYPE
|
||||
|
||||
include Pacemaker::Mixins::Resource::Meta
|
||||
|
||||
# FIXME: need to handle params as well as meta
|
||||
|
||||
attr_accessor :members
|
||||
|
||||
def self.attrs_to_copy_from_chef
|
||||
%w(members meta)
|
||||
end
|
||||
|
||||
def parse_definition
|
||||
unless definition =~ /^#{self.class::TYPE} (\S+) (.+?)(\s+\\)?$/
|
||||
raise Pacemaker::CIBObject::DefinitionParseError, \
|
||||
"Couldn't parse definition '#{definition}'"
|
||||
end
|
||||
self.name = $1
|
||||
members = $2.split
|
||||
trim_from = members.find_index('meta')
|
||||
members = members[0..trim_from-1] if trim_from
|
||||
self.members = members
|
||||
self.meta = self.class.extract_hash(definition, 'meta')
|
||||
end
|
||||
|
||||
def definition_string
|
||||
str = "#{self.class::TYPE} #{name} " + members.join(' ')
|
||||
unless meta.empty?
|
||||
str << continuation_line(meta_string)
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
end
|
@ -1,10 +0,0 @@
|
||||
require File.expand_path('clone', File.dirname(__FILE__))
|
||||
|
||||
class Pacemaker::Resource::MasterSlave < Pacemaker::Resource::Clone
|
||||
TYPE = 'ms'
|
||||
register_type TYPE
|
||||
|
||||
#include Pacemaker::Mixins::Resource::Meta
|
||||
|
||||
attr_accessor :rsc
|
||||
end
|
@ -1,89 +0,0 @@
|
||||
require 'shellwords'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../resource', this_dir)
|
||||
require File.expand_path('../mixins/resource_meta', this_dir)
|
||||
|
||||
class Pacemaker::Resource::Primitive < Pacemaker::Resource
|
||||
TYPE = 'primitive'
|
||||
register_type TYPE
|
||||
|
||||
include Pacemaker::Mixins::Resource::Meta
|
||||
|
||||
attr_accessor :agent, :params, :op
|
||||
|
||||
def initialize(*args)
|
||||
super(*args)
|
||||
|
||||
@agent = nil
|
||||
end
|
||||
|
||||
def self.attrs_to_copy_from_chef
|
||||
%w(agent params meta op)
|
||||
end
|
||||
|
||||
def parse_definition
|
||||
unless definition =~ /\A#{self.class::TYPE} (\S+) (\S+)/
|
||||
raise Pacemaker::CIBObject::DefinitionParseError, \
|
||||
"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
|
||||
|
||||
self.op = {}
|
||||
%w(start stop monitor).each do |op|
|
||||
h = self.class.extract_hash(definition, "op #{op}")
|
||||
self.op[op] = h unless h.empty?
|
||||
end
|
||||
end
|
||||
|
||||
def params_string
|
||||
self.class.params_string(params)
|
||||
end
|
||||
|
||||
def op_string
|
||||
self.class.op_string(op)
|
||||
end
|
||||
|
||||
def definition_string
|
||||
str = "#{self.class::TYPE} #{name} #{agent}"
|
||||
%w(params meta op).each do |data_type|
|
||||
unless send(data_type).empty?
|
||||
data_string = send("#{data_type}_string")
|
||||
str << continuation_line(data_string)
|
||||
end
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
def 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?
|
||||
"params " +
|
||||
params.sort.map do |key, value|
|
||||
%'#{key}="#{value}"'
|
||||
end.join(' ')
|
||||
end
|
||||
|
||||
def self.op_string(ops)
|
||||
return "" if ! ops or ops.empty?
|
||||
ops.sort.map do |op, attrs|
|
||||
attrs.empty? ? nil : "op #{op} " + \
|
||||
attrs.sort.map do |key, value|
|
||||
%'#{key}="#{value}"'
|
||||
end.join(' ')
|
||||
end.compact.join(' ')
|
||||
end
|
||||
|
||||
end
|
@ -1,24 +0,0 @@
|
||||
module PacemakerStonithHelper
|
||||
@@stonith_agents = nil
|
||||
|
||||
def self.stonith_agent_valid?(agent)
|
||||
if agent.nil? || agent.empty?
|
||||
false
|
||||
else
|
||||
if @@stonith_agents.nil?
|
||||
out = %x{stonith -L}
|
||||
if $?.success?
|
||||
@@stonith_agents = out.split("\n")
|
||||
end
|
||||
end
|
||||
|
||||
!@@stonith_agents.nil? && @@stonith_agents.include?(agent)
|
||||
end
|
||||
end
|
||||
|
||||
def self.assert_stonith_agent_valid(agent)
|
||||
unless stonith_agent_valid? agent
|
||||
raise "STONITH fencing agent #{agent} is not available!"
|
||||
end
|
||||
end
|
||||
end
|
@ -1,9 +0,0 @@
|
||||
name "pacemaker"
|
||||
maintainer "Crowbar Project"
|
||||
maintainer_email "crowbar@dell.com"
|
||||
license "Apache 2.0"
|
||||
description "Installs/configures Pacemaker"
|
||||
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
|
||||
version "0.1"
|
||||
|
||||
depends "corosync"
|
@ -1,72 +0,0 @@
|
||||
# Author:: Robert Choi
|
||||
# Cookbook Name:: pacemaker
|
||||
# Provider:: clone
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
this_dir = ::File.dirname(__FILE__)
|
||||
require ::File.expand_path('../libraries/pacemaker', this_dir)
|
||||
require ::File.expand_path('../libraries/chef/mixin/pacemaker', this_dir)
|
||||
|
||||
include Chef::Mixin::Pacemaker::RunnableResource
|
||||
|
||||
action :create do
|
||||
standard_create_action
|
||||
end
|
||||
|
||||
action :delete do
|
||||
delete_runnable_resource
|
||||
end
|
||||
|
||||
action :start do
|
||||
start_runnable_resource
|
||||
end
|
||||
|
||||
action :stop do
|
||||
stop_runnable_resource
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
::Pacemaker::Resource::Clone
|
||||
end
|
||||
|
||||
def load_current_resource
|
||||
standard_load_current_resource
|
||||
end
|
||||
|
||||
def init_current_resource
|
||||
name = @new_resource.name
|
||||
@current_resource = Chef::Resource::PacemakerClone.new(name)
|
||||
@current_cib_object.copy_attrs_to_chef_resource(@current_resource, :rsc)
|
||||
end
|
||||
|
||||
def create_resource(name)
|
||||
standard_create_resource
|
||||
end
|
||||
|
||||
def maybe_modify_resource(name)
|
||||
Chef::Log.info "Checking existing #{@current_cib_object} for modifications"
|
||||
|
||||
desired_clone = cib_object_class.from_chef_resource(new_resource)
|
||||
if desired_clone.definition_string != @current_cib_object.definition_string
|
||||
Chef::Log.debug "changed from [#{@current_cib_object.definition_string}] to [#{desired_clone.definition_string}]"
|
||||
cmd = desired_clone.reconfigure_command
|
||||
execute cmd do
|
||||
action :nothing
|
||||
end.run_action(:run)
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
@ -1,72 +0,0 @@
|
||||
# Author:: Robert Choi
|
||||
# Cookbook Name:: pacemaker
|
||||
# Provider:: colocation
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
this_dir = ::File.dirname(__FILE__)
|
||||
require ::File.expand_path('../libraries/pacemaker', this_dir)
|
||||
require ::File.expand_path('../libraries/chef/mixin/pacemaker', this_dir)
|
||||
|
||||
include Chef::Mixin::Pacemaker::StandardCIBObject
|
||||
|
||||
action :create do
|
||||
name = new_resource.name
|
||||
|
||||
if @current_resource_definition.nil?
|
||||
create_resource(name)
|
||||
else
|
||||
maybe_modify_resource(name)
|
||||
end
|
||||
end
|
||||
|
||||
action :delete do
|
||||
next unless @current_resource
|
||||
standard_delete_resource
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
::Pacemaker::Constraint::Colocation
|
||||
end
|
||||
|
||||
def load_current_resource
|
||||
standard_load_current_resource
|
||||
end
|
||||
|
||||
def init_current_resource
|
||||
name = @new_resource.name
|
||||
@current_resource = Chef::Resource::PacemakerColocation.new(name)
|
||||
attrs = [:score, :resources]
|
||||
@current_cib_object.copy_attrs_to_chef_resource(@current_resource, *attrs)
|
||||
end
|
||||
|
||||
def create_resource(name)
|
||||
standard_create_resource
|
||||
end
|
||||
|
||||
def maybe_modify_resource(name)
|
||||
Chef::Log.info "Checking existing #{@current_cib_object} for modifications"
|
||||
|
||||
desired_colocation = cib_object_class.from_chef_resource(new_resource)
|
||||
if desired_colocation.definition_string != @current_cib_object.definition_string
|
||||
Chef::Log.debug "changed from [#{@current_cib_object.definition_string}] to [#{desired_colocation.definition_string}]"
|
||||
cmd = desired_colocation.reconfigure_command
|
||||
execute cmd do
|
||||
action :nothing
|
||||
end.run_action(:run)
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
@ -1,71 +0,0 @@
|
||||
# Cookbook Name:: pacemaker
|
||||
# Provider:: group
|
||||
#
|
||||
# Copyright:: 2014, SUSE
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
this_dir = ::File.dirname(__FILE__)
|
||||
require ::File.expand_path('../libraries/pacemaker', this_dir)
|
||||
require ::File.expand_path('../libraries/chef/mixin/pacemaker', this_dir)
|
||||
|
||||
include Chef::Mixin::Pacemaker::RunnableResource
|
||||
|
||||
action :create do
|
||||
standard_create_action
|
||||
end
|
||||
|
||||
action :delete do
|
||||
delete_runnable_resource
|
||||
end
|
||||
|
||||
action :start do
|
||||
start_runnable_resource
|
||||
end
|
||||
|
||||
action :stop do
|
||||
stop_runnable_resource
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
::Pacemaker::Resource::Group
|
||||
end
|
||||
|
||||
def load_current_resource
|
||||
standard_load_current_resource
|
||||
end
|
||||
|
||||
def init_current_resource
|
||||
name = @new_resource.name
|
||||
@current_resource = Chef::Resource::PacemakerGroup.new(name)
|
||||
@current_cib_object.copy_attrs_to_chef_resource(@current_resource, :members)
|
||||
end
|
||||
|
||||
def create_resource(name)
|
||||
standard_create_resource
|
||||
end
|
||||
|
||||
def maybe_modify_resource(name)
|
||||
Chef::Log.info "Checking existing #{@current_cib_object} for modifications"
|
||||
|
||||
desired_group = cib_object_class.from_chef_resource(new_resource)
|
||||
if desired_group.definition_string != @current_cib_object.definition_string
|
||||
Chef::Log.debug "changed from [#{@current_cib_object.definition_string}] to [#{desired_group.definition_string}]"
|
||||
cmd = desired_group.reconfigure_command
|
||||
execute cmd do
|
||||
action :nothing
|
||||
end.run_action(:run)
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
@ -1,72 +0,0 @@
|
||||
# Author:: Robert Choi
|
||||
# Cookbook Name:: pacemaker
|
||||
# Provider:: location
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
this_dir = ::File.dirname(__FILE__)
|
||||
require ::File.expand_path('../libraries/pacemaker', this_dir)
|
||||
require ::File.expand_path('../libraries/chef/mixin/pacemaker', this_dir)
|
||||
|
||||
include Chef::Mixin::Pacemaker::StandardCIBObject
|
||||
|
||||
action :create do
|
||||
name = new_resource.name
|
||||
|
||||
if @current_resource_definition.nil?
|
||||
create_resource(name)
|
||||
else
|
||||
maybe_modify_resource(name)
|
||||
end
|
||||
end
|
||||
|
||||
action :delete do
|
||||
next unless @current_resource
|
||||
standard_delete_resource
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
::Pacemaker::Constraint::Location
|
||||
end
|
||||
|
||||
def load_current_resource
|
||||
standard_load_current_resource
|
||||
end
|
||||
|
||||
def init_current_resource
|
||||
name = @new_resource.name
|
||||
@current_resource = Chef::Resource::PacemakerLocation.new(name)
|
||||
attrs = [:rsc, :score, :node]
|
||||
@current_cib_object.copy_attrs_to_chef_resource(@current_resource, *attrs)
|
||||
end
|
||||
|
||||
def create_resource(name)
|
||||
standard_create_resource
|
||||
end
|
||||
|
||||
def maybe_modify_resource(name)
|
||||
Chef::Log.info "Checking existing #{@current_cib_object} for modifications"
|
||||
|
||||
desired_location = cib_object_class.from_chef_resource(new_resource)
|
||||
if desired_location.definition_string != @current_cib_object.definition_string
|
||||
Chef::Log.debug "changed from [#{@current_cib_object.definition_string}] to [#{desired_location.definition_string}]"
|
||||
cmd = desired_location.reconfigure_command
|
||||
execute cmd do
|
||||
action :nothing
|
||||
end.run_action(:run)
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
@ -1,72 +0,0 @@
|
||||
# Cookbook Name:: pacemaker
|
||||
# Provider:: ms
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi
|
||||
# Copyright:: 2014, SUSE
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
this_dir = ::File.dirname(__FILE__)
|
||||
require ::File.expand_path('../libraries/pacemaker', this_dir)
|
||||
require ::File.expand_path('../libraries/chef/mixin/pacemaker', this_dir)
|
||||
|
||||
include Chef::Mixin::Pacemaker::RunnableResource
|
||||
|
||||
action :create do
|
||||
standard_create_action
|
||||
end
|
||||
|
||||
action :delete do
|
||||
delete_runnable_resource
|
||||
end
|
||||
|
||||
action :start do
|
||||
start_runnable_resource
|
||||
end
|
||||
|
||||
action :stop do
|
||||
stop_runnable_resource
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
::Pacemaker::Resource::MasterSlave
|
||||
end
|
||||
|
||||
def load_current_resource
|
||||
standard_load_current_resource
|
||||
end
|
||||
|
||||
def init_current_resource
|
||||
name = @new_resource.name
|
||||
@current_resource = Chef::Resource::PacemakerMs.new(name)
|
||||
@current_cib_object.copy_attrs_to_chef_resource(@current_resource, :rsc)
|
||||
end
|
||||
|
||||
def create_resource(name)
|
||||
standard_create_resource
|
||||
end
|
||||
|
||||
def maybe_modify_resource(name)
|
||||
Chef::Log.info "Checking existing #{@current_cib_object} for modifications"
|
||||
|
||||
desired_clone = cib_object_class.from_chef_resource(new_resource)
|
||||
if desired_clone.definition_string != @current_cib_object.definition_string
|
||||
Chef::Log.debug "changed from [#{@current_cib_object.definition_string}] to [#{desired_clone.definition_string}]"
|
||||
cmd = desired_clone.reconfigure_command
|
||||
execute cmd do
|
||||
action :nothing
|
||||
end.run_action(:run)
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
@ -1,73 +0,0 @@
|
||||
# Cookbook Name:: pacemaker
|
||||
# Provider:: order
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi
|
||||
# Copyright:: 2014, SUSE
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
this_dir = ::File.dirname(__FILE__)
|
||||
require ::File.expand_path('../libraries/pacemaker/cib_object', this_dir)
|
||||
require ::File.expand_path('../libraries/pacemaker', this_dir)
|
||||
require ::File.expand_path('../libraries/chef/mixin/pacemaker', this_dir)
|
||||
|
||||
include Chef::Mixin::Pacemaker::StandardCIBObject
|
||||
|
||||
action :create do
|
||||
name = new_resource.name
|
||||
|
||||
if @current_resource_definition.nil?
|
||||
create_resource(name)
|
||||
else
|
||||
maybe_modify_resource(name)
|
||||
end
|
||||
end
|
||||
|
||||
action :delete do
|
||||
next unless @current_resource
|
||||
standard_delete_resource
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
::Pacemaker::Constraint::Order
|
||||
end
|
||||
|
||||
def load_current_resource
|
||||
standard_load_current_resource
|
||||
end
|
||||
|
||||
def init_current_resource
|
||||
name = @new_resource.name
|
||||
@current_resource = Chef::Resource::PacemakerOrder.new(name)
|
||||
attrs = [:score, :ordering]
|
||||
@current_cib_object.copy_attrs_to_chef_resource(@current_resource, *attrs)
|
||||
end
|
||||
|
||||
def create_resource(name)
|
||||
standard_create_resource
|
||||
end
|
||||
|
||||
def maybe_modify_resource(name)
|
||||
Chef::Log.info "Checking existing #{@current_cib_object} for modifications"
|
||||
|
||||
desired_order = cib_object_class.from_chef_resource(new_resource)
|
||||
if desired_order.definition_string != @current_cib_object.definition_string
|
||||
Chef::Log.debug "changed from [#{@current_cib_object.definition_string}] to [#{desired_order.definition_string}]"
|
||||
cmd = desired_order.reconfigure_command
|
||||
execute cmd do
|
||||
action :nothing
|
||||
end.run_action(:run)
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
@ -1,146 +0,0 @@
|
||||
# Author:: Robert Choi
|
||||
# Cookbook Name:: pacemaker
|
||||
# Provider:: primitive
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
this_dir = ::File.dirname(__FILE__)
|
||||
require ::File.expand_path('../libraries/pacemaker', this_dir)
|
||||
require ::File.expand_path('../libraries/chef/mixin/pacemaker', this_dir)
|
||||
|
||||
include Chef::Mixin::Pacemaker::RunnableResource
|
||||
|
||||
action :create do
|
||||
name = new_resource.name
|
||||
|
||||
if @current_resource_definition.nil?
|
||||
create_resource(name)
|
||||
else
|
||||
if @current_resource.agent != new_resource.agent
|
||||
raise "Existing %s has agent '%s' " \
|
||||
"but recipe wanted '%s'" % \
|
||||
[ @current_cib_object, @current_resource.agent, new_resource.agent ]
|
||||
end
|
||||
|
||||
maybe_modify_resource(name)
|
||||
end
|
||||
end
|
||||
|
||||
action :delete do
|
||||
delete_runnable_resource
|
||||
end
|
||||
|
||||
action :start do
|
||||
start_runnable_resource
|
||||
end
|
||||
|
||||
action :stop do
|
||||
stop_runnable_resource
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
::Pacemaker::Resource::Primitive
|
||||
end
|
||||
|
||||
def load_current_resource
|
||||
standard_load_current_resource
|
||||
end
|
||||
|
||||
def init_current_resource
|
||||
name = @new_resource.name
|
||||
@current_resource = Chef::Resource::PacemakerPrimitive.new(name)
|
||||
@current_cib_object.copy_attrs_to_chef_resource(@current_resource,
|
||||
:agent, :params, :meta)
|
||||
end
|
||||
|
||||
def create_resource(name)
|
||||
standard_create_resource
|
||||
end
|
||||
|
||||
def maybe_modify_resource(name)
|
||||
deprecate_target_role
|
||||
|
||||
Chef::Log.info "Checking existing #{@current_cib_object} for modifications"
|
||||
|
||||
cmds = []
|
||||
|
||||
desired_primitive = cib_object_class.from_chef_resource(new_resource)
|
||||
|
||||
# We deprecated setting target-role values via the meta attribute, in favor
|
||||
# of :start/:stop actions on the resource. So this should not be relied upon
|
||||
# anymore, and it's safe to drop this if we want to.
|
||||
#
|
||||
# There is one racy case where this matters:
|
||||
# - node1 and node2 try to create a primitive with the chef resource;
|
||||
# on initial creation, we set target-role='Stopped' because we do not
|
||||
# want to autostart primitives.
|
||||
# - because they can't create it at the same time, node2 will fail on
|
||||
# creation. If the chef resource is configured to retry, then node2 will
|
||||
# then try to update the primitive (since it now exists); but the chef
|
||||
# resource is not reloaded so still has target-role='Stopped'.
|
||||
# - if node1 had also started the primitive before node2 retries the
|
||||
# :create, then the target-role will be changed from 'Started' to
|
||||
# 'Stopped' with the update.
|
||||
#
|
||||
# This can result in a primitive not being started with [:create, :start].
|
||||
# Therefore, we just delete this deprecated bit from meta to avoid any issue.
|
||||
desired_primitive.meta.delete('target-role')
|
||||
|
||||
if desired_primitive.op_string != @current_cib_object.op_string
|
||||
Chef::Log.debug "op changed from [#{@current_cib_object.op_string}] to [#{desired_primitive.op_string}]"
|
||||
cmds = [desired_primitive.reconfigure_command]
|
||||
else
|
||||
maybe_configure_params(name, cmds, :params)
|
||||
maybe_configure_params(name, cmds, :meta)
|
||||
end
|
||||
|
||||
cmds.each do |cmd|
|
||||
execute cmd do
|
||||
action :nothing
|
||||
end.run_action(:run)
|
||||
end
|
||||
|
||||
new_resource.updated_by_last_action(true) unless cmds.empty?
|
||||
end
|
||||
|
||||
def maybe_configure_params(name, cmds, data_type)
|
||||
configure_cmd_prefix = "crm_resource --resource #{name}"
|
||||
|
||||
new_resource.send(data_type).each do |param, new_value|
|
||||
current_value = @current_resource.send(data_type)[param]
|
||||
# Value from recipe might be a TrueClass instance, but the same
|
||||
# value would be retrieved from the cluster resource as the String
|
||||
# "true". So we force a string-wise comparison to adhere to
|
||||
# Postel's Law whilst minimising activity on the Chef client node.
|
||||
if current_value.to_s == new_value.to_s
|
||||
Chef::Log.info("#{name}'s #{param} #{data_type} didn't change")
|
||||
else
|
||||
Chef::Log.info("#{name}'s #{param} #{data_type} changed from #{current_value} to #{new_value}")
|
||||
cmd = configure_cmd_prefix + %' --set-parameter "#{param}" --parameter-value "#{new_value}"'
|
||||
cmd += " --meta" if data_type == :meta
|
||||
cmds << cmd
|
||||
end
|
||||
end
|
||||
|
||||
@current_resource.send(data_type).each do |param, value|
|
||||
unless new_resource.send(data_type).has_key? param
|
||||
Chef::Log.info("#{name}'s #{param} #{data_type} was removed")
|
||||
cmd = configure_cmd_prefix + %' --delete-parameter "#{param}"'
|
||||
cmd += " --meta" if data_type == :meta
|
||||
cmds << cmd
|
||||
end
|
||||
end
|
||||
end
|
@ -1,45 +0,0 @@
|
||||
# Author:: Robert Choi
|
||||
# Cookbook Name:: pacemaker
|
||||
# Provider:: property
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
this_dir = ::File.dirname(__FILE__)
|
||||
require ::File.expand_path('../libraries/pacemaker/cib_object', this_dir)
|
||||
|
||||
action :create do
|
||||
name = new_resource.name
|
||||
val = new_resource.value
|
||||
|
||||
unless resource_exists?(name)
|
||||
cmd = "crm configure property #{name}=#{val}"
|
||||
|
||||
cmd_ = Mixlib::ShellOut.new(cmd)
|
||||
cmd_.environment['HOME'] = ENV.fetch('HOME', '/root')
|
||||
cmd_.run_command
|
||||
begin
|
||||
cmd_.error!
|
||||
if resource_exists?(name)
|
||||
new_resource.updated_by_last_action(true)
|
||||
Chef::Log.info "Successfully configured property '#{name}'."
|
||||
else
|
||||
Chef::Log.error "Failed to configure property #{name}."
|
||||
end
|
||||
rescue
|
||||
Chef::Log.error "Failed to configure property #{name}."
|
||||
end
|
||||
end
|
||||
end
|
@ -1,84 +0,0 @@
|
||||
#
|
||||
# Author:: Robert Choi
|
||||
# Cookbook Name:: pacemaker
|
||||
# Recipe:: default
|
||||
#
|
||||
# Copyright 2013, Robert Choi
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
node[:pacemaker][:platform][:packages].each do |pkg|
|
||||
package pkg
|
||||
end
|
||||
|
||||
if node[:pacemaker][:setup_hb_gui]
|
||||
node[:pacemaker][:platform][:graphical_packages].each do |pkg|
|
||||
package pkg
|
||||
end
|
||||
|
||||
# required to run hb_gui
|
||||
if platform_family? "suse"
|
||||
cmd = "SuSEconfig --module gtk2"
|
||||
execute cmd do
|
||||
user "root"
|
||||
command cmd
|
||||
not_if { File.exists? "/etc/gtk-2.0/gdk-pixbuf64.loaders" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Chef::Config[:solo]
|
||||
unless ENV['RSPEC_RUNNING']
|
||||
Chef::Application.fatal! \
|
||||
"pacemaker::default needs corosync::default which uses search, " \
|
||||
"but Chef Solo does not support search."
|
||||
return
|
||||
end
|
||||
else
|
||||
include_recipe "corosync::default"
|
||||
end
|
||||
|
||||
ruby_block "wait for cluster to be online" do
|
||||
block do
|
||||
require 'timeout'
|
||||
begin
|
||||
Timeout.timeout(60) do
|
||||
cmd = "crm_mon -1 | grep -qi online"
|
||||
while ! ::Kernel.system(cmd)
|
||||
Chef::Log.debug("cluster not online yet")
|
||||
sleep(5)
|
||||
end
|
||||
end
|
||||
rescue Timeout::Error
|
||||
message = "Pacemaker cluster not online yet; our first configuration changes might get lost (but will be reapplied on next chef run)."
|
||||
Chef::Log.warn(message)
|
||||
end
|
||||
end # block
|
||||
end # ruby_block
|
||||
|
||||
if node[:pacemaker][:founder]
|
||||
include_recipe "pacemaker::setup"
|
||||
end
|
||||
|
||||
if platform_family? "rhel"
|
||||
execute "sleep 2"
|
||||
|
||||
service "pacemaker" do
|
||||
action [ :enable, :start ]
|
||||
notifies :restart, "service[clvm]", :immediately
|
||||
end
|
||||
end
|
||||
|
||||
include_recipe "pacemaker::stonith"
|
||||
include_recipe "pacemaker::notifications"
|
@ -1,66 +0,0 @@
|
||||
#
|
||||
# Author:: Vincent Untz
|
||||
# Cookbook Name:: pacemaker
|
||||
# Recipe:: notifications
|
||||
#
|
||||
# Copyright 2014, SUSE
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
smtp_resource = "smtp-notifications"
|
||||
clone_smtp_resource = "cl-#{smtp_resource}"
|
||||
|
||||
if node[:pacemaker][:notifications][:smtp][:enabled]
|
||||
raise "No SMTP server for mail notifications!" if node[:pacemaker][:notifications][:smtp][:server].empty?
|
||||
raise "No sender address for mail notifications!" if node[:pacemaker][:notifications][:smtp][:to].empty?
|
||||
raise "No recipient address for mail notifications!" if node[:pacemaker][:notifications][:smtp][:from].empty?
|
||||
|
||||
require 'shellwords'
|
||||
|
||||
server = Shellwords.shellescape(node[:pacemaker][:notifications][:smtp][:server])
|
||||
to = Shellwords.shellescape(node[:pacemaker][:notifications][:smtp][:to])
|
||||
from = Shellwords.shellescape(node[:pacemaker][:notifications][:smtp][:from])
|
||||
|
||||
options = "-H #{server}"
|
||||
options += " -T #{to}"
|
||||
options += " -F #{from}"
|
||||
|
||||
unless node[:pacemaker][:notifications][:smtp][:prefix].nil? || node[:pacemaker][:notifications][:smtp][:prefix].empty?
|
||||
prefix = Shellwords.shellescape(node[:pacemaker][:notifications][:smtp][:prefix])
|
||||
options += " -P #{prefix}"
|
||||
end
|
||||
|
||||
pacemaker_primitive smtp_resource do
|
||||
agent node[:pacemaker][:notifications][:agent]
|
||||
params ({ "extra_options" => options })
|
||||
action :create
|
||||
end
|
||||
|
||||
pacemaker_clone clone_smtp_resource do
|
||||
rsc smtp_resource
|
||||
action [:create, :start]
|
||||
end
|
||||
else
|
||||
pacemaker_clone clone_smtp_resource do
|
||||
rsc smtp_resource
|
||||
action [:stop, :delete]
|
||||
only_if "crm configure show #{clone_smtp_resource}"
|
||||
end
|
||||
|
||||
pacemaker_primitive smtp_resource do
|
||||
agent node[:pacemaker][:notifications][:agent]
|
||||
action [:stop, :delete]
|
||||
only_if "crm configure show #{smtp_resource}"
|
||||
end
|
||||
end
|
@ -1,40 +0,0 @@
|
||||
#
|
||||
# Author:: Robert Choi
|
||||
# Cookbook Name:: pacemaker
|
||||
# Recipe:: setup
|
||||
#
|
||||
# Copyright 2013, Robert Choi
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
crm_conf = node[:pacemaker][:crm][:initial_config_file]
|
||||
|
||||
template crm_conf do
|
||||
source "crm-initial.conf.erb"
|
||||
owner "root"
|
||||
group "root"
|
||||
mode 0600
|
||||
variables(
|
||||
:stonith_enabled => (node[:pacemaker][:stonith][:mode] != "disabled"),
|
||||
:no_quorum_policy => node[:pacemaker][:crm][:no_quorum_policy],
|
||||
:op_default_timeout => node[:pacemaker][:crm][:op_default_timeout]
|
||||
)
|
||||
end
|
||||
|
||||
execute "crm initial configuration" do
|
||||
user "root"
|
||||
command "crm configure load replace #{crm_conf}"
|
||||
subscribes :run, resources(:template => crm_conf), :immediately
|
||||
action :nothing
|
||||
end
|
@ -1,169 +0,0 @@
|
||||
#
|
||||
# Author:: Vincent Untz
|
||||
# Cookbook Name:: pacemaker
|
||||
# Recipe:: stonith
|
||||
#
|
||||
# Copyright 2014, SUSE
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# FIXME: delete old resources when switching mode (or agent!)
|
||||
|
||||
case node[:pacemaker][:stonith][:mode]
|
||||
when "disabled"
|
||||
when "manual"
|
||||
# nothing!
|
||||
|
||||
when "sbd"
|
||||
require 'shellwords'
|
||||
|
||||
sbd_devices = nil
|
||||
sbd_devices ||= (node[:pacemaker][:stonith][:sbd][:nodes][node[:fqdn]][:devices] rescue nil)
|
||||
sbd_devices ||= (node[:pacemaker][:stonith][:sbd][:nodes][node[:hostname]][:devices] rescue nil)
|
||||
raise "No SBD devices defined!" if sbd_devices.nil? || sbd_devices.empty?
|
||||
|
||||
sbd_cmd = "sbd"
|
||||
sbd_devices.each do |sbd_device|
|
||||
sbd_cmd += " -d #{Shellwords.shellescape(sbd_device)}"
|
||||
end
|
||||
|
||||
execute "Check if watchdog is present" do
|
||||
command "test -c /dev/watchdog"
|
||||
end
|
||||
|
||||
execute "Check that SBD was initialized using '#{sbd_cmd} create'." do
|
||||
command "#{sbd_cmd} dump &> /dev/null"
|
||||
end
|
||||
|
||||
if node.platform == 'suse'
|
||||
# We will want to explicitly allocate a slot the first time we come here
|
||||
# (hence the use of a notification to trigger this execute).
|
||||
# According to the man page, it should not be required, but apparently,
|
||||
# I've hit bugs where I had to do that. So better be safe.
|
||||
execute "Allocate SBD slot" do
|
||||
command "#{sbd_cmd} allocate #{node[:hostname]}"
|
||||
not_if "#{sbd_cmd} list | grep -q \" #{node[:hostname]} \""
|
||||
action :nothing
|
||||
end
|
||||
|
||||
template "/etc/sysconfig/sbd" do
|
||||
source "sysconfig_sbd.erb"
|
||||
owner "root"
|
||||
group "root"
|
||||
mode 0644
|
||||
variables(
|
||||
:sbd_devices => sbd_devices
|
||||
)
|
||||
# We want to allocate slots before restarting corosync
|
||||
notifies :run, "execute[Allocate SBD slot]", :immediately
|
||||
notifies :restart, "service[#{node[:corosync][:platform][:service_name]}]", :immediately
|
||||
# After restarting corosync, we need to wait for the cluster to be online again
|
||||
notifies :create, "ruby_block[wait for cluster to be online]", :immediately
|
||||
end
|
||||
end
|
||||
|
||||
pacemaker_primitive "stonith-sbd" do
|
||||
agent "stonith:external/sbd"
|
||||
action [:create, :start]
|
||||
end
|
||||
|
||||
when "shared"
|
||||
agent = node[:pacemaker][:stonith][:shared][:agent]
|
||||
params = node[:pacemaker][:stonith][:shared][:params]
|
||||
|
||||
# This needs to be done in the second phase of chef, because we need
|
||||
# cluster-glue to be installed first; hence ruby_block
|
||||
ruby_block "Check if STONITH fencing agent #{agent} is available" do
|
||||
block do
|
||||
PacemakerStonithHelper.assert_stonith_agent_valid agent
|
||||
end
|
||||
end
|
||||
|
||||
if params.respond_to?('to_hash')
|
||||
primitive_params = params.to_hash
|
||||
elsif params.is_a?(String)
|
||||
primitive_params = ::Pacemaker::Resource.extract_hash(" params #{params}", "params")
|
||||
else
|
||||
message = "Unknown format for shared fencing agent parameters: #{params.inspect}."
|
||||
Chef::Log.fatal(message)
|
||||
raise message
|
||||
end
|
||||
|
||||
unless primitive_params.has_key?("hostlist")
|
||||
message = "Missing hostlist parameter for shared fencing agent!"
|
||||
Chef::Log.fatal(message)
|
||||
raise message
|
||||
end
|
||||
|
||||
pacemaker_primitive "stonith-shared" do
|
||||
agent "stonith:#{agent}"
|
||||
params primitive_params
|
||||
action [:create, :start]
|
||||
end
|
||||
|
||||
when "per_node"
|
||||
agent = node[:pacemaker][:stonith][:per_node][:agent]
|
||||
|
||||
# This needs to be done in the second phase of chef, because we need
|
||||
# cluster-glue to be installed first; hence ruby_block
|
||||
ruby_block "Check if STONITH fencing agent #{agent} is available" do
|
||||
block do
|
||||
PacemakerStonithHelper.assert_stonith_agent_valid agent
|
||||
end
|
||||
end
|
||||
|
||||
node[:pacemaker][:stonith][:per_node][:nodes].keys.each do |node_name|
|
||||
if node[:pacemaker][:stonith][:per_node][:mode] == "self"
|
||||
next unless node_name == node[:hostname]
|
||||
end
|
||||
|
||||
stonith_resource = "stonith-#{node_name}"
|
||||
params = node[:pacemaker][:stonith][:per_node][:nodes][node_name][:params]
|
||||
|
||||
if params.respond_to?('to_hash')
|
||||
primitive_params = params.to_hash
|
||||
elsif params.is_a?(String)
|
||||
primitive_params = ::Pacemaker::Resource.extract_hash(" params #{params}", "params")
|
||||
else
|
||||
message = "Unknown format for per-node fencing agent parameters of #{node_name}: #{params.inspect}."
|
||||
Chef::Log.fatal(message)
|
||||
raise message
|
||||
end
|
||||
|
||||
# Only set one of hostname / hostlist param if none of them are present; we
|
||||
# do not overwrite it as the user might have passed more information than
|
||||
# just the hostname (some agents accept hostname:data in hostlist)
|
||||
unless primitive_params.has_key?("hostname") || primitive_params.has_key?("hostlist")
|
||||
primitive_params["hostname"] = node_name
|
||||
end
|
||||
|
||||
pacemaker_primitive stonith_resource do
|
||||
agent "stonith:#{agent}"
|
||||
params primitive_params
|
||||
action [:create, :start]
|
||||
end
|
||||
|
||||
pacemaker_location "l-#{stonith_resource}" do
|
||||
rsc stonith_resource
|
||||
score "-inf"
|
||||
node node_name
|
||||
action :create
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
message = "Unknown STONITH mode: #{node[:pacemaker][:stonith][:mode]}."
|
||||
Chef::Log.fatal(message)
|
||||
raise message
|
||||
end
|
@ -1,25 +0,0 @@
|
||||
# Cookbook Name:: pacemaker
|
||||
# Resource:: clone
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi, SUSE
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
actions :create, :delete, :start, :stop
|
||||
|
||||
default_action :create
|
||||
|
||||
attribute :name, :kind_of => String, :name_attribute => true
|
||||
attribute :rsc, :kind_of => String
|
||||
attribute :meta, :kind_of => Hash, :default => {}
|
@ -1,29 +0,0 @@
|
||||
# Author:: Robert Choi
|
||||
# Cookbook Name:: pacemaker
|
||||
# Resource:: colocation
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
actions :create, :delete
|
||||
|
||||
default_action :create
|
||||
|
||||
attribute :name, :kind_of => String, :name_attribute => true
|
||||
attribute :score, :kind_of => String
|
||||
|
||||
# If more than two resources are given, Pacemaker will treat this
|
||||
# as a resource set.
|
||||
attribute :resources, :kind_of => Array
|
@ -1,25 +0,0 @@
|
||||
# Cookbook Name:: pacemaker
|
||||
# Resource:: group
|
||||
#
|
||||
# Copyright:: 2014, SUSE
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
actions :create, :delete, :start, :stop
|
||||
|
||||
default_action :create
|
||||
|
||||
attribute :name, :kind_of => String, :name_attribute => true
|
||||
attribute :members, :kind_of => Array
|
||||
attribute :meta, :kind_of => Hash, :default => {}
|
@ -1,27 +0,0 @@
|
||||
# Author:: Robert Choi
|
||||
# Cookbook Name:: pacemaker
|
||||
# Resource:: location
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
actions :create, :delete
|
||||
|
||||
default_action :create
|
||||
|
||||
attribute :name, :kind_of => String, :name_attribute => true
|
||||
attribute :rsc, :kind_of => String
|
||||
attribute :score, :kind_of => String
|
||||
attribute :node, :kind_of => String
|
@ -1,25 +0,0 @@
|
||||
# Cookbook Name:: pacemaker
|
||||
# Resource:: ms
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi, SUSE
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
actions :create, :delete, :start, :stop
|
||||
|
||||
default_action :create
|
||||
|
||||
attribute :name, :kind_of => String, :name_attribute => true
|
||||
attribute :rsc, :kind_of => String
|
||||
attribute :meta, :kind_of => Hash, :default => {}
|
@ -1,26 +0,0 @@
|
||||
# Author:: Robert Choi
|
||||
# Cookbook Name:: pacemaker
|
||||
# Resource:: order
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
actions :create, :delete
|
||||
|
||||
default_action :create
|
||||
|
||||
attribute :name, :kind_of => String, :name_attribute => true
|
||||
attribute :score, :kind_of => String
|
||||
attribute :ordering, :kind_of => String
|
@ -1,28 +0,0 @@
|
||||
# Author:: Robert Choi
|
||||
# Cookbook Name:: pacemaker
|
||||
# Resource:: primitive
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
actions :create, :delete, :start, :stop
|
||||
|
||||
default_action :create
|
||||
|
||||
attribute :name, :kind_of => String, :name_attribute => true
|
||||
attribute :agent, :kind_of => String
|
||||
attribute :params, :kind_of => Hash, :default => {}
|
||||
attribute :meta, :kind_of => Hash, :default => {}
|
||||
attribute :op, :kind_of => Hash, :default => {}
|
@ -1,25 +0,0 @@
|
||||
# Author:: Robert Choi
|
||||
# Cookbook Name:: pacemaker
|
||||
# Resource:: property
|
||||
#
|
||||
# Copyright:: 2013, Robert Choi
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
actions :create
|
||||
|
||||
default_action :create
|
||||
|
||||
attribute :name, :kind_of => String, :name_attribute => true
|
||||
attribute :value, :kind_of => String
|
20
spec/fixtures/clone_resource.rb
vendored
20
spec/fixtures/clone_resource.rb
vendored
@ -1,20 +0,0 @@
|
||||
require File.expand_path('../../libraries/pacemaker/resource/clone',
|
||||
File.dirname(__FILE__))
|
||||
|
||||
module Chef::RSpec
|
||||
module Pacemaker
|
||||
module Config
|
||||
CLONE_RESOURCE = ::Pacemaker::Resource::Clone.new('clone1')
|
||||
CLONE_RESOURCE.rsc = 'primitive1'
|
||||
CLONE_RESOURCE.meta = [
|
||||
[ "globally-unique", "true" ],
|
||||
[ "clone-max", "2" ],
|
||||
[ "clone-node-max", "2" ]
|
||||
]
|
||||
CLONE_RESOURCE_DEFINITION = <<'EOF'.chomp
|
||||
clone clone1 primitive1 \
|
||||
meta clone-max="2" clone-node-max="2" globally-unique="true"
|
||||
EOF
|
||||
end
|
||||
end
|
||||
end
|
14
spec/fixtures/colocation_constraint.rb
vendored
14
spec/fixtures/colocation_constraint.rb
vendored
@ -1,14 +0,0 @@
|
||||
require ::File.expand_path('../../libraries/pacemaker/constraint/colocation',
|
||||
File.dirname(__FILE__))
|
||||
|
||||
module Chef::RSpec
|
||||
module Pacemaker
|
||||
module Config
|
||||
COLOCATION_CONSTRAINT = \
|
||||
::Pacemaker::Constraint::Colocation.new('colocation1')
|
||||
COLOCATION_CONSTRAINT.score = 'inf'
|
||||
COLOCATION_CONSTRAINT.resources = ['foo']
|
||||
COLOCATION_CONSTRAINT_DEFINITION = 'colocation colocation1 inf: foo'
|
||||
end
|
||||
end
|
||||
end
|
31
spec/fixtures/keystone_primitive.rb
vendored
31
spec/fixtures/keystone_primitive.rb
vendored
@ -1,31 +0,0 @@
|
||||
require ::File.expand_path('../../libraries/pacemaker/resource/primitive',
|
||||
File.dirname(__FILE__))
|
||||
|
||||
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 = [
|
||||
[ "is-managed", "true" ]
|
||||
]
|
||||
KEYSTONE_PRIMITIVE.op = [
|
||||
[ "monitor", { "timeout" => "60", "interval" => "10s" } ],
|
||||
[ "start", { "timeout" => "240", "interval" => "10s" } ]
|
||||
]
|
||||
KEYSTONE_PRIMITIVE_DEFINITION = <<'EOF'.chomp
|
||||
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" \
|
||||
op monitor interval="10s" timeout="60" op start interval="10s" timeout="240"
|
||||
EOF
|
||||
end
|
||||
end
|
||||
end
|
15
spec/fixtures/location_constraint.rb
vendored
15
spec/fixtures/location_constraint.rb
vendored
@ -1,15 +0,0 @@
|
||||
require ::File.expand_path('../../libraries/pacemaker/constraint/location',
|
||||
File.dirname(__FILE__))
|
||||
|
||||
module Chef::RSpec
|
||||
module Pacemaker
|
||||
module Config
|
||||
LOCATION_CONSTRAINT = \
|
||||
::Pacemaker::Constraint::Location.new('location1')
|
||||
LOCATION_CONSTRAINT.rsc = 'primitive1'
|
||||
LOCATION_CONSTRAINT.score = '-inf'
|
||||
LOCATION_CONSTRAINT.node = 'node1'
|
||||
LOCATION_CONSTRAINT_DEFINITION = 'location location1 primitive1 -inf: node1'
|
||||
end
|
||||
end
|
||||
end
|
22
spec/fixtures/ms_resource.rb
vendored
22
spec/fixtures/ms_resource.rb
vendored
@ -1,22 +0,0 @@
|
||||
require File.expand_path('../../libraries/pacemaker/resource/ms',
|
||||
File.dirname(__FILE__))
|
||||
|
||||
module Chef::RSpec
|
||||
module Pacemaker
|
||||
module Config
|
||||
MS_RESOURCE = ::Pacemaker::Resource::MasterSlave.new('ms1')
|
||||
MS_RESOURCE.rsc = 'primitive1'
|
||||
MS_RESOURCE.meta = [
|
||||
[ "globally-unique", "true" ],
|
||||
[ "clone-max", "2" ],
|
||||
[ "clone-node-max", "2" ],
|
||||
[ "master-max", "1" ],
|
||||
[ "master-node-max", "1" ]
|
||||
]
|
||||
MS_RESOURCE_DEFINITION = <<'EOF'.chomp
|
||||
ms ms1 primitive1 \
|
||||
meta clone-max="2" clone-node-max="2" globally-unique="true" master-max="1" master-node-max="1"
|
||||
EOF
|
||||
end
|
||||
end
|
||||
end
|
14
spec/fixtures/order_constraint.rb
vendored
14
spec/fixtures/order_constraint.rb
vendored
@ -1,14 +0,0 @@
|
||||
require ::File.expand_path('../../libraries/pacemaker/constraint/order',
|
||||
File.dirname(__FILE__))
|
||||
|
||||
module Chef::RSpec
|
||||
module Pacemaker
|
||||
module Config
|
||||
ORDER_CONSTRAINT = \
|
||||
::Pacemaker::Constraint::Order.new('order1')
|
||||
ORDER_CONSTRAINT.score = 'Mandatory'
|
||||
ORDER_CONSTRAINT.ordering = 'primitive1 clone1'
|
||||
ORDER_CONSTRAINT_DEFINITION = 'order order1 Mandatory: primitive1 clone1'
|
||||
end
|
||||
end
|
||||
end
|
19
spec/fixtures/resource_group.rb
vendored
19
spec/fixtures/resource_group.rb
vendored
@ -1,19 +0,0 @@
|
||||
require File.expand_path('../../libraries/pacemaker/resource/group',
|
||||
File.dirname(__FILE__))
|
||||
|
||||
module Chef::RSpec
|
||||
module Pacemaker
|
||||
module Config
|
||||
RESOURCE_GROUP = \
|
||||
::Pacemaker::Resource::Group.new('group1')
|
||||
RESOURCE_GROUP.members = ['resource1', 'resource2']
|
||||
RESOURCE_GROUP.meta = [
|
||||
[ "is-managed", "true" ]
|
||||
]
|
||||
RESOURCE_GROUP_DEFINITION = <<'EOF'.chomp
|
||||
group group1 resource1 resource2 \
|
||||
meta is-managed="true"
|
||||
EOF
|
||||
end
|
||||
end
|
||||
end
|
@ -1,37 +0,0 @@
|
||||
# Shared code used to test subclasses of Pacemaker::CIBObject
|
||||
|
||||
require 'mixlib/shellout'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../../libraries/pacemaker/cib_object', this_dir)
|
||||
require File.expand_path('shellout', this_dir)
|
||||
|
||||
shared_examples "a CIB object" do
|
||||
include Chef::RSpec::Mixlib::ShellOut
|
||||
|
||||
def expect_to_match_fixture(obj)
|
||||
expect(obj.class).to eq(pacemaker_object_class)
|
||||
fields.each do |field|
|
||||
method = field.to_sym
|
||||
expect(obj.send(method)).to eq(fixture.send(method))
|
||||
end
|
||||
end
|
||||
|
||||
it "should be instantiated via Pacemaker::CIBObject.from_name" do
|
||||
stub_shellout(fixture.definition_string)
|
||||
obj = Pacemaker::CIBObject.from_name(fixture.name)
|
||||
expect_to_match_fixture(obj)
|
||||
end
|
||||
|
||||
it "should instantiate by parsing a definition" do
|
||||
obj = Pacemaker::CIBObject.from_definition(fixture.definition_string)
|
||||
expect_to_match_fixture(obj)
|
||||
end
|
||||
|
||||
it "should barf if the loaded definition's type is not right" do
|
||||
stub_shellout("sometype foo blah blah")
|
||||
expect { fixture.load_definition }.to \
|
||||
raise_error(Pacemaker::CIBObject::TypeMismatch,
|
||||
"Expected #{object_type} type but loaded definition was type sometype")
|
||||
end
|
||||
end
|
@ -1,21 +0,0 @@
|
||||
shared_examples "with meta attributes" do
|
||||
describe "#meta_string" do
|
||||
it "should return empty string with nil meta" do
|
||||
fixture.meta = nil
|
||||
expect(fixture.meta_string).to eq("")
|
||||
end
|
||||
|
||||
it "should return empty string with empty meta" do
|
||||
fixture.meta = {}
|
||||
expect(fixture.meta_string).to eq("")
|
||||
end
|
||||
|
||||
it "should return a resource meta string" do
|
||||
fixture.meta = {
|
||||
"foo" => "bar",
|
||||
"baz" => "qux",
|
||||
}
|
||||
expect(fixture.meta_string).to eq(%'meta baz="qux" foo="bar"')
|
||||
end
|
||||
end
|
||||
end
|
@ -1,26 +0,0 @@
|
||||
# Shared code used to test providers of non-runnable Chef resources
|
||||
# representing Pacemaker CIB objects. For example the provider for
|
||||
# primitives is runnable (since primitives can be started and stopped)
|
||||
# but constraints cannot.
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('provider', this_dir)
|
||||
require File.expand_path('shellout', this_dir)
|
||||
|
||||
shared_examples "a non-runnable resource" do |fixture|
|
||||
include Chef::RSpec::Mixlib::ShellOut
|
||||
|
||||
it_should_behave_like "all Pacemaker LWRPs", fixture
|
||||
|
||||
describe ":delete action" do
|
||||
it "should delete a resource" do
|
||||
stub_shellout(fixture.definition_string)
|
||||
|
||||
provider.run_action :delete
|
||||
|
||||
cmd = "crm configure delete '#{fixture.name}'"
|
||||
expect(@chef_run).to run_execute(cmd)
|
||||
expect(@resource).to be_updated
|
||||
end
|
||||
end
|
||||
end
|
@ -1,74 +0,0 @@
|
||||
# Shared code used to test providers of CIB objects
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('shellout', this_dir)
|
||||
require File.expand_path('cib_object', this_dir)
|
||||
|
||||
shared_context "a Pacemaker LWRP" do
|
||||
before(:each) do
|
||||
stub_command("crm configure show smtp-notifications")
|
||||
stub_command("crm configure show cl-smtp-notifications")
|
||||
|
||||
runner_opts = {
|
||||
:step_into => [lwrp_name]
|
||||
}
|
||||
@chef_run = ::ChefSpec::Runner.new(runner_opts)
|
||||
@chef_run.converge "pacemaker::default"
|
||||
@node = @chef_run.node
|
||||
@run_context = @chef_run.run_context
|
||||
|
||||
camelized_subclass_name = "Pacemaker" + lwrp_name.capitalize
|
||||
@resource_class = ::Chef::Resource.const_get(camelized_subclass_name)
|
||||
@provider_class = ::Chef::Provider.const_get(camelized_subclass_name)
|
||||
|
||||
@resource = @resource_class.new(fixture.name, @run_context)
|
||||
end
|
||||
|
||||
let (:provider) { @provider_class.new(@resource, @run_context) }
|
||||
end
|
||||
|
||||
module Chef::RSpec
|
||||
module Pacemaker
|
||||
module CIBObject
|
||||
include Chef::RSpec::Mixlib::ShellOut
|
||||
|
||||
def test_modify(expected_cmds)
|
||||
yield
|
||||
|
||||
stub_shellout(fixture.definition_string)
|
||||
|
||||
provider.run_action :create
|
||||
|
||||
expected_cmds.each do |cmd|
|
||||
expect(@chef_run).to run_execute(cmd)
|
||||
end
|
||||
expect(@resource).to be_updated
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples "action on non-existent resource" do |action, cmd, expected_error|
|
||||
include Chef::RSpec::Mixlib::ShellOut
|
||||
|
||||
it "should not attempt to #{action.to_s} a non-existent resource" do
|
||||
stub_shellout("")
|
||||
|
||||
if expected_error
|
||||
expect { provider.run_action action }.to \
|
||||
raise_error(RuntimeError, expected_error)
|
||||
else
|
||||
provider.run_action action
|
||||
end
|
||||
|
||||
expect(@chef_run).not_to run_execute(cmd)
|
||||
expect(@resource).not_to be_updated
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples "all Pacemaker LWRPs" do |fixture|
|
||||
describe ":delete action" do
|
||||
it_should_behave_like "action on non-existent resource", \
|
||||
:delete, "crm configure delete #{fixture.name}", nil
|
||||
end
|
||||
end
|
@ -1,128 +0,0 @@
|
||||
# Shared code used to test providers of runnable Chef resources
|
||||
# representing Pacemaker CIB objects. For example the provider
|
||||
# for primitives is runnable (since primitives can be started
|
||||
# and stopped) but constraints cannot.
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('provider', this_dir)
|
||||
require File.expand_path('shellout', this_dir)
|
||||
|
||||
shared_context "stopped resource" do
|
||||
def stopped_fixture
|
||||
new_fixture = fixture.dup
|
||||
new_fixture.meta = fixture.meta.dup
|
||||
new_fixture.meta << ['target-role', 'Stopped']
|
||||
new_fixture
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples "a runnable resource" do |fixture|
|
||||
def expect_running(running)
|
||||
expect_any_instance_of(cib_object_class) \
|
||||
.to receive(:running?) \
|
||||
.and_return(running)
|
||||
end
|
||||
|
||||
it_should_behave_like "all Pacemaker LWRPs", fixture
|
||||
|
||||
include Chef::RSpec::Mixlib::ShellOut
|
||||
|
||||
describe ":create action" do
|
||||
include_context "stopped resource"
|
||||
|
||||
it "should not start a newly-created resource" do
|
||||
stub_shellout("", fixture.definition_string)
|
||||
|
||||
provider.run_action :create
|
||||
|
||||
expect(@chef_run).to run_execute(stopped_fixture.configure_command)
|
||||
expect(@resource).to be_updated
|
||||
end
|
||||
end
|
||||
|
||||
describe ":delete action" do
|
||||
it "should not delete a running resource" do
|
||||
stub_shellout(fixture.definition_string)
|
||||
expect_running(true)
|
||||
|
||||
expected_error = "Cannot delete running #{fixture}"
|
||||
expect { provider.run_action :delete }.to \
|
||||
raise_error(RuntimeError, expected_error)
|
||||
|
||||
cmd = "crm configure delete '#{fixture.name}'"
|
||||
expect(@chef_run).not_to run_execute(cmd)
|
||||
expect(@resource).not_to be_updated
|
||||
end
|
||||
|
||||
it "should delete a non-running resource" do
|
||||
stub_shellout(fixture.definition_string)
|
||||
expect_running(false)
|
||||
|
||||
provider.run_action :delete
|
||||
|
||||
cmd = "crm configure delete '#{fixture.name}'"
|
||||
expect(@chef_run).to run_execute(cmd)
|
||||
expect(@resource).to be_updated
|
||||
end
|
||||
end
|
||||
|
||||
describe ":start action" do
|
||||
it_should_behave_like "action on non-existent resource", \
|
||||
:start,
|
||||
"crm --force --wait resource start #{fixture.name}", \
|
||||
"Cannot start non-existent #{fixture}"
|
||||
|
||||
it "should do nothing to a started resource" do
|
||||
stub_shellout(fixture.definition_string)
|
||||
expect_running(true)
|
||||
|
||||
provider.run_action :start
|
||||
|
||||
cmd = "crm --force --wait resource start #{fixture.name}"
|
||||
expect(@chef_run).not_to run_execute(cmd)
|
||||
expect(@resource).not_to be_updated
|
||||
end
|
||||
|
||||
it "should start a stopped resource" do
|
||||
config = fixture.definition_string.sub("Started", "Stopped")
|
||||
stub_shellout(config)
|
||||
expect_running(false)
|
||||
|
||||
provider.run_action :start
|
||||
|
||||
cmd = "crm --force --wait resource start '#{fixture.name}'"
|
||||
expect(@chef_run).to run_execute(cmd)
|
||||
expect(@resource).to be_updated
|
||||
end
|
||||
end
|
||||
|
||||
describe ":stop action" do
|
||||
it_should_behave_like "action on non-existent resource", \
|
||||
:stop,
|
||||
"crm --force --wait resource stop #{fixture.name}", \
|
||||
"Cannot stop non-existent #{fixture}"
|
||||
|
||||
it "should do nothing to a stopped resource" do
|
||||
stub_shellout(fixture.definition_string)
|
||||
expect_running(false)
|
||||
|
||||
provider.run_action :stop
|
||||
|
||||
cmd = "crm --force --wait resource start #{fixture.name}"
|
||||
expect(@chef_run).not_to run_execute(cmd)
|
||||
expect(@resource).not_to be_updated
|
||||
end
|
||||
|
||||
it "should stop a started resource" do
|
||||
stub_shellout(fixture.definition_string)
|
||||
expect_running(true)
|
||||
|
||||
provider.run_action :stop
|
||||
|
||||
cmd = "crm --force --wait resource stop '#{fixture.name}'"
|
||||
expect(@chef_run).to run_execute(cmd)
|
||||
expect(@resource).to be_updated
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,68 +0,0 @@
|
||||
require 'mixlib/shellout'
|
||||
|
||||
module Chef::RSpec
|
||||
module Mixlib
|
||||
module ShellOut
|
||||
# Return a Mixlib::ShellOut double which mimics successful
|
||||
# execution of a command, returning the given string on STDOUT.
|
||||
def succeeding_shellout_double(string)
|
||||
shellout = double(Mixlib::ShellOut)
|
||||
shellout.stub(:environment).and_return({})
|
||||
shellout.stub(:run_command)
|
||||
shellout.stub(:error!)
|
||||
expect(shellout).to receive(:stdout).and_return(string)
|
||||
shellout
|
||||
end
|
||||
|
||||
# Return a Mixlib::ShellOut double which mimics failed
|
||||
# execution of a command, raising an exception when #error! is
|
||||
# called. We expect #error! to be called, because if it isn't,
|
||||
# that probably indicates the code isn't robust enough. This
|
||||
# may need to be relaxed in the future.
|
||||
def failing_shellout_double(stdout='', stderr='', exitstatus=1)
|
||||
shellout = double(Mixlib::ShellOut)
|
||||
shellout.stub(:environment).and_return({})
|
||||
shellout.stub(:run_command)
|
||||
shellout.stub(:stdout).and_return(stdout)
|
||||
shellout.stub(:stderr).and_return(stderr)
|
||||
shellout.stub(:exitstatus).and_return(exitstatus)
|
||||
exception = ::Mixlib::ShellOut::ShellCommandFailed.new(
|
||||
"Expected process to exit with 0, " +
|
||||
"but received '#{exitstatus}'"
|
||||
)
|
||||
expect(shellout).to receive(:error!).and_raise(exception)
|
||||
shellout
|
||||
end
|
||||
|
||||
# This stubs Mixlib::ShellOut.new with a sequence of doubles
|
||||
# with a corresponding sequence of behaviours. This allows us
|
||||
# to simulate the output of a series of shell commands being run
|
||||
# via Mixlib::ShellOut. Each double either mimics a successful
|
||||
# command execution whose #stdout method returns the given
|
||||
# string, or a failed execution with the given exit code and
|
||||
# STDOUT/STDERR.
|
||||
#
|
||||
# results is an Array describing the sequence of behaviours;
|
||||
# each element is either a string mimicking STDOUT from
|
||||
# successful command execution, or a [stdout, stderr, exitcode]
|
||||
# status mimicking command execution failure.
|
||||
#
|
||||
# For example, "crm configure show" is executed by
|
||||
# #load_current_resource, and again later on for the :create
|
||||
# action, to see whether to create or modify. So the first
|
||||
# double in the sequence would return an empty definition if we
|
||||
# wanted to test creation of a new CIB object, or an existing
|
||||
# definition if we wanted to test modification of an existing
|
||||
# one. If the test needs subsequent doubles to return different
|
||||
# values then stdout_strings can have more than one element.
|
||||
def stub_shellout(*results)
|
||||
doubles = results.map { |result|
|
||||
result.is_a?(String) ?
|
||||
succeeding_shellout_double(result)
|
||||
: failing_shellout_double(*result)
|
||||
}
|
||||
::Mixlib::ShellOut.stub(:new).and_return(*doubles)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,107 +0,0 @@
|
||||
require 'mixlib/shellout'
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../../../libraries/pacemaker', this_dir)
|
||||
require File.expand_path('../../fixtures/keystone_primitive', this_dir)
|
||||
|
||||
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
|
||||
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,61 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../../../../libraries/pacemaker/constraint/colocation',
|
||||
this_dir)
|
||||
require File.expand_path('../../../fixtures/colocation_constraint', this_dir)
|
||||
require File.expand_path('../../../helpers/cib_object', this_dir)
|
||||
|
||||
describe Pacemaker::Constraint::Colocation do
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::COLOCATION_CONSTRAINT.dup }
|
||||
let(:fixture_definition) {
|
||||
Chef::RSpec::Pacemaker::Config::COLOCATION_CONSTRAINT_DEFINITION
|
||||
}
|
||||
|
||||
def object_type
|
||||
'colocation'
|
||||
end
|
||||
|
||||
def pacemaker_object_class
|
||||
Pacemaker::Constraint::Colocation
|
||||
end
|
||||
|
||||
def fields
|
||||
%w(name score resources)
|
||||
end
|
||||
|
||||
it_should_behave_like "a CIB object"
|
||||
|
||||
describe "#definition_string" do
|
||||
it "should return the definition string" do
|
||||
expect(fixture.definition_string).to eq(fixture_definition)
|
||||
end
|
||||
|
||||
it "should return a short definition string" do
|
||||
colocation = pacemaker_object_class.new('foo')
|
||||
colocation.definition = \
|
||||
%!colocation colocation1 -inf: rsc1 rsc2!
|
||||
colocation.parse_definition
|
||||
expect(colocation.definition_string).to eq(<<'EOF'.chomp)
|
||||
colocation colocation1 -inf: rsc1 rsc2
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
||||
describe "#parse_definition" do
|
||||
before(:each) do
|
||||
@parsed = pacemaker_object_class.new(fixture.name)
|
||||
@parsed.definition = fixture_definition
|
||||
@parsed.parse_definition
|
||||
end
|
||||
|
||||
it "should parse the score" do
|
||||
expect(@parsed.score).to eq(fixture.score)
|
||||
end
|
||||
|
||||
it "should parse the resources" do
|
||||
expect(@parsed.resources).to eq(fixture.resources)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,64 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../../../../libraries/pacemaker/constraint/location',
|
||||
this_dir)
|
||||
require File.expand_path('../../../fixtures/location_constraint', this_dir)
|
||||
require File.expand_path('../../../helpers/cib_object', this_dir)
|
||||
|
||||
describe Pacemaker::Constraint::Location do
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::LOCATION_CONSTRAINT.dup }
|
||||
let(:fixture_definition) {
|
||||
Chef::RSpec::Pacemaker::Config::LOCATION_CONSTRAINT_DEFINITION
|
||||
}
|
||||
|
||||
def object_type
|
||||
'location'
|
||||
end
|
||||
|
||||
def pacemaker_object_class
|
||||
Pacemaker::Constraint::Location
|
||||
end
|
||||
|
||||
def fields
|
||||
%w(name rsc score node)
|
||||
end
|
||||
|
||||
it_should_behave_like "a CIB object"
|
||||
|
||||
describe "#definition_string" do
|
||||
it "should return the definition string" do
|
||||
expect(fixture.definition_string).to eq(fixture_definition)
|
||||
end
|
||||
|
||||
it "should return a short definition string" do
|
||||
location = pacemaker_object_class.new('foo')
|
||||
location.definition = \
|
||||
%!location location1 primitive1 -inf: node1!
|
||||
location.parse_definition
|
||||
expect(location.definition_string).to eq(<<'EOF'.chomp)
|
||||
location location1 primitive1 -inf: node1
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
||||
describe "#parse_definition" do
|
||||
before(:each) do
|
||||
@parsed = pacemaker_object_class.new(fixture.name)
|
||||
@parsed.definition = fixture_definition
|
||||
@parsed.parse_definition
|
||||
end
|
||||
|
||||
it "should parse the rsc" do
|
||||
expect(@parsed.rsc).to eq(fixture.rsc)
|
||||
end
|
||||
|
||||
it "should parse the score" do
|
||||
expect(@parsed.score).to eq(fixture.score)
|
||||
end
|
||||
|
||||
it "should parse the node" do
|
||||
expect(@parsed.node).to eq(fixture.node)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,61 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../../../../libraries/pacemaker/constraint/order',
|
||||
this_dir)
|
||||
require File.expand_path('../../../fixtures/order_constraint', this_dir)
|
||||
require File.expand_path('../../../helpers/cib_object', this_dir)
|
||||
|
||||
describe Pacemaker::Constraint::Order do
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::ORDER_CONSTRAINT.dup }
|
||||
let(:fixture_definition) {
|
||||
Chef::RSpec::Pacemaker::Config::ORDER_CONSTRAINT_DEFINITION
|
||||
}
|
||||
|
||||
def object_type
|
||||
'order'
|
||||
end
|
||||
|
||||
def pacemaker_object_class
|
||||
Pacemaker::Constraint::Order
|
||||
end
|
||||
|
||||
def fields
|
||||
%w(name score ordering)
|
||||
end
|
||||
|
||||
it_should_behave_like "a CIB object"
|
||||
|
||||
describe "#definition_string" do
|
||||
it "should return the definition string" do
|
||||
expect(fixture.definition_string).to eq(fixture_definition)
|
||||
end
|
||||
|
||||
it "should return a short definition string" do
|
||||
order = pacemaker_object_class.new('foo')
|
||||
order.definition = \
|
||||
%!order order1 Mandatory: rsc1 rsc2!
|
||||
order.parse_definition
|
||||
expect(order.definition_string).to eq(<<'EOF'.chomp)
|
||||
order order1 Mandatory: rsc1 rsc2
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
||||
describe "#parse_definition" do
|
||||
before(:each) do
|
||||
@parsed = pacemaker_object_class.new(fixture.name)
|
||||
@parsed.definition = fixture_definition
|
||||
@parsed.parse_definition
|
||||
end
|
||||
|
||||
it "should parse the score" do
|
||||
expect(@parsed.score).to eq(fixture.score)
|
||||
end
|
||||
|
||||
it "should parse the ordering" do
|
||||
expect(@parsed.ordering).to eq(fixture.ordering)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,59 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../../../../libraries/pacemaker/resource/clone', this_dir)
|
||||
require File.expand_path('../../../fixtures/clone_resource', this_dir)
|
||||
require File.expand_path('../../../helpers/cib_object', this_dir)
|
||||
require File.expand_path('../../../helpers/meta_examples', this_dir)
|
||||
|
||||
describe Pacemaker::Resource::Clone do
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::CLONE_RESOURCE.dup }
|
||||
let(:fixture_definition) {
|
||||
Chef::RSpec::Pacemaker::Config::CLONE_RESOURCE_DEFINITION
|
||||
}
|
||||
|
||||
def object_type
|
||||
'clone'
|
||||
end
|
||||
|
||||
def pacemaker_object_class
|
||||
Pacemaker::Resource::Clone
|
||||
end
|
||||
|
||||
def fields
|
||||
%w(name rsc)
|
||||
end
|
||||
|
||||
it_should_behave_like "a CIB object"
|
||||
|
||||
it_should_behave_like "with meta attributes"
|
||||
|
||||
describe "#definition_string" do
|
||||
it "should return the definition string" do
|
||||
expect(fixture.definition_string).to eq(fixture_definition)
|
||||
end
|
||||
|
||||
it "should return a short definition string" do
|
||||
clone = pacemaker_object_class.new('foo')
|
||||
clone.definition = \
|
||||
%!clone clone1 primitive1 meta globally-unique="true"!
|
||||
clone.parse_definition
|
||||
expect(clone.definition_string).to eq(<<'EOF'.chomp)
|
||||
clone clone1 primitive1 \
|
||||
meta globally-unique="true"
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
||||
describe "#parse_definition" do
|
||||
before(:each) do
|
||||
@parsed = pacemaker_object_class.new(fixture.name)
|
||||
@parsed.definition = fixture_definition
|
||||
@parsed.parse_definition
|
||||
end
|
||||
|
||||
it "should parse the rsc" do
|
||||
expect(@parsed.rsc).to eq(fixture.rsc)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,60 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
require File.expand_path('../../../../libraries/pacemaker/resource/group',
|
||||
File.dirname(__FILE__))
|
||||
require File.expand_path('../../../fixtures/resource_group', File.dirname(__FILE__))
|
||||
require File.expand_path('../../../helpers/cib_object', File.dirname(__FILE__))
|
||||
require File.expand_path('../../../helpers/meta_examples',
|
||||
File.dirname(__FILE__))
|
||||
|
||||
describe Pacemaker::Resource::Group do
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::RESOURCE_GROUP.dup }
|
||||
let(:fixture_definition) {
|
||||
Chef::RSpec::Pacemaker::Config::RESOURCE_GROUP_DEFINITION
|
||||
}
|
||||
|
||||
def object_type
|
||||
'group'
|
||||
end
|
||||
|
||||
def pacemaker_object_class
|
||||
Pacemaker::Resource::Group
|
||||
end
|
||||
|
||||
def fields
|
||||
%w(name members)
|
||||
end
|
||||
|
||||
it_should_behave_like "a CIB object"
|
||||
|
||||
it_should_behave_like "with meta attributes"
|
||||
|
||||
describe "#definition_string" do
|
||||
it "should return the definition string" do
|
||||
expect(fixture.definition_string).to eq(fixture_definition)
|
||||
end
|
||||
|
||||
it "should return a short definition string" do
|
||||
group = pacemaker_object_class.new('foo')
|
||||
group.definition = \
|
||||
%!group foo member1 member2 meta target-role="Started"!
|
||||
group.parse_definition
|
||||
expect(group.definition_string).to eq(<<'EOF'.chomp)
|
||||
group foo member1 member2 \
|
||||
meta target-role="Started"
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
||||
describe "#parse_definition" do
|
||||
before(:each) do
|
||||
@parsed = pacemaker_object_class.new(fixture.name)
|
||||
@parsed.definition = fixture_definition
|
||||
@parsed.parse_definition
|
||||
end
|
||||
|
||||
it "should parse the members" do
|
||||
expect(@parsed.members).to eq(fixture.members)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,59 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../../../../libraries/pacemaker/resource/ms', this_dir)
|
||||
require File.expand_path('../../../fixtures/ms_resource', this_dir)
|
||||
require File.expand_path('../../../helpers/cib_object', this_dir)
|
||||
require File.expand_path('../../../helpers/meta_examples', this_dir)
|
||||
|
||||
describe Pacemaker::Resource::MasterSlave do
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::MS_RESOURCE.dup }
|
||||
let(:fixture_definition) {
|
||||
Chef::RSpec::Pacemaker::Config::MS_RESOURCE_DEFINITION
|
||||
}
|
||||
|
||||
def object_type
|
||||
'ms'
|
||||
end
|
||||
|
||||
def pacemaker_object_class
|
||||
Pacemaker::Resource::MasterSlave
|
||||
end
|
||||
|
||||
def fields
|
||||
%w(name rsc)
|
||||
end
|
||||
|
||||
it_should_behave_like "a CIB object"
|
||||
|
||||
it_should_behave_like "with meta attributes"
|
||||
|
||||
describe "#definition_string" do
|
||||
it "should return the definition string" do
|
||||
expect(fixture.definition_string).to eq(fixture_definition)
|
||||
end
|
||||
|
||||
it "should return a short definition string" do
|
||||
ms = pacemaker_object_class.new('foo')
|
||||
ms.definition = \
|
||||
%!ms ms1 primitive1 meta globally-unique="true"!
|
||||
ms.parse_definition
|
||||
expect(ms.definition_string).to eq(<<'EOF'.chomp)
|
||||
ms ms1 primitive1 \
|
||||
meta globally-unique="true"
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
||||
describe "#parse_definition" do
|
||||
before(:each) do
|
||||
@parsed = pacemaker_object_class.new(fixture.name)
|
||||
@parsed.definition = fixture_definition
|
||||
@parsed.parse_definition
|
||||
end
|
||||
|
||||
it "should parse the rsc" do
|
||||
expect(@parsed.rsc).to eq(fixture.rsc)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,117 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../../../../libraries/pacemaker/resource/primitive',
|
||||
this_dir)
|
||||
require File.expand_path('../../../fixtures/keystone_primitive', this_dir)
|
||||
require File.expand_path('../../../helpers/cib_object', this_dir)
|
||||
require File.expand_path('../../../helpers/meta_examples', this_dir)
|
||||
|
||||
describe Pacemaker::Resource::Primitive do
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE.dup }
|
||||
let(:fixture_definition) {
|
||||
Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE_DEFINITION
|
||||
}
|
||||
|
||||
def object_type
|
||||
'primitive'
|
||||
end
|
||||
|
||||
def pacemaker_object_class
|
||||
Pacemaker::Resource::Primitive
|
||||
end
|
||||
|
||||
def fields
|
||||
%w(name agent params_string meta_string op_string)
|
||||
end
|
||||
|
||||
it_should_behave_like "a CIB object"
|
||||
|
||||
describe "#params_string" do
|
||||
it "should return empty string with nil params" do
|
||||
fixture.params = nil
|
||||
expect(fixture.params_string).to eq("")
|
||||
end
|
||||
|
||||
it "should return empty string with empty params" do
|
||||
fixture.params = {}
|
||||
expect(fixture.params_string).to eq("")
|
||||
end
|
||||
|
||||
it "should return a resource params string" do
|
||||
fixture.params = {
|
||||
"foo" => "bar",
|
||||
"baz" => "qux",
|
||||
}
|
||||
expect(fixture.params_string).to eq(%'params baz="qux" foo="bar"')
|
||||
end
|
||||
end
|
||||
|
||||
describe "#op_string" do
|
||||
it "should return empty string with nil op" do
|
||||
fixture.op = nil
|
||||
expect(fixture.op_string).to eq("")
|
||||
end
|
||||
|
||||
it "should return empty string with empty op" do
|
||||
fixture.op = {}
|
||||
expect(fixture.op_string).to eq("")
|
||||
end
|
||||
|
||||
it "should return a resource op string" do
|
||||
fixture.op = {
|
||||
"monitor" => {
|
||||
"foo" => "bar",
|
||||
"baz" => "qux",
|
||||
}
|
||||
}
|
||||
expect(fixture.op_string).to eq(%'op monitor baz="qux" foo="bar"')
|
||||
end
|
||||
end
|
||||
|
||||
it_should_behave_like "with meta attributes"
|
||||
|
||||
describe "#definition_string" do
|
||||
it "should return the definition string" do
|
||||
expect(fixture.definition_string).to eq(fixture_definition)
|
||||
end
|
||||
|
||||
it "should return a short definition string" do
|
||||
primitive = pacemaker_object_class.new('foo')
|
||||
primitive.definition = \
|
||||
%!primitive foo ocf:heartbeat:IPaddr2 params foo="bar"!
|
||||
primitive.parse_definition
|
||||
expect(primitive.definition_string).to eq(<<'EOF'.chomp)
|
||||
primitive foo ocf:heartbeat:IPaddr2 \
|
||||
params foo="bar"
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
||||
describe "#quoted_definition_string" do
|
||||
it "should return the quoted definition string" do
|
||||
primitive = pacemaker_object_class.new('foo')
|
||||
primitive.definition = <<'EOF'.chomp
|
||||
primitive foo ocf:openstack:keystone \
|
||||
params bar="baz\\qux" bar2="baz'qux"
|
||||
EOF
|
||||
primitive.parse_definition
|
||||
expect(primitive.quoted_definition_string).to eq(<<'EOF'.chomp)
|
||||
'primitive foo ocf:openstack:keystone \\
|
||||
params bar="baz\\qux" bar2="baz\'qux"'
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
||||
describe "#parse_definition" do
|
||||
before(:each) do
|
||||
@parsed = pacemaker_object_class.new(fixture.name)
|
||||
@parsed.definition = fixture_definition
|
||||
@parsed.parse_definition
|
||||
end
|
||||
|
||||
it "should parse the agent" do
|
||||
expect(@parsed.agent).to eq(fixture.agent)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,51 +0,0 @@
|
||||
require 'mixlib/shellout'
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../../../libraries/pacemaker/resource', this_dir)
|
||||
require File.expand_path('../../fixtures/keystone_primitive', this_dir)
|
||||
|
||||
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
|
||||
|
||||
describe "::extract_hash" do
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE.dup }
|
||||
|
||||
it "should extract a params hash from config" do
|
||||
expect(fixture.class.extract_hash(fixture.definition_string, "params")).to \
|
||||
eq(Hash[fixture.params])
|
||||
end
|
||||
|
||||
it "should extract an op start hash from config" do
|
||||
expect(fixture.class.extract_hash(fixture.definition_string, 'op start')).to \
|
||||
eq(Hash[fixture.op]['start'])
|
||||
end
|
||||
|
||||
it "should extract an op monitor hash from config" do
|
||||
expect(fixture.class.extract_hash(fixture.definition_string, 'op monitor')).to \
|
||||
eq(Hash[fixture.op]['monitor'])
|
||||
end
|
||||
end
|
||||
end
|
@ -1,46 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../helpers/runnable_resource', this_dir)
|
||||
require File.expand_path('../fixtures/clone_resource', this_dir)
|
||||
|
||||
describe "Chef::Provider::PacemakerClone" do
|
||||
# for use inside examples:
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::CLONE_RESOURCE.dup }
|
||||
# for use outside examples (e.g. when invoking shared_examples)
|
||||
fixture = Chef::RSpec::Pacemaker::Config::CLONE_RESOURCE.dup
|
||||
|
||||
def lwrp_name
|
||||
'clone'
|
||||
end
|
||||
|
||||
include_context "a Pacemaker LWRP"
|
||||
|
||||
before(:each) do
|
||||
@resource.rsc fixture.rsc.dup
|
||||
@resource.meta Hash[fixture.meta.dup]
|
||||
|
||||
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
Pacemaker::Resource::Clone
|
||||
end
|
||||
|
||||
describe ":create action" do
|
||||
include Chef::RSpec::Pacemaker::CIBObject
|
||||
|
||||
it "should modify the clone if the resource is changed" do
|
||||
expected = fixture.dup
|
||||
expected.rsc = 'primitive2'
|
||||
expected_configure_cmd_args = [expected.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.rsc expected.rsc
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it_should_behave_like "a runnable resource", fixture
|
||||
|
||||
end
|
@ -1,66 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../helpers/provider', this_dir)
|
||||
require File.expand_path('../helpers/non_runnable_resource', this_dir)
|
||||
require File.expand_path('../fixtures/colocation_constraint', this_dir)
|
||||
|
||||
describe "Chef::Provider::PacemakerColocation" do
|
||||
# for use inside examples:
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::COLOCATION_CONSTRAINT.dup }
|
||||
# for use outside examples (e.g. when invoking shared_examples)
|
||||
fixture = Chef::RSpec::Pacemaker::Config::COLOCATION_CONSTRAINT.dup
|
||||
|
||||
def lwrp_name
|
||||
'colocation'
|
||||
end
|
||||
|
||||
include_context "a Pacemaker LWRP"
|
||||
|
||||
before(:each) do
|
||||
@resource.score fixture.score
|
||||
@resource.resources fixture.resources.dup
|
||||
|
||||
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
Pacemaker::Constraint::Colocation
|
||||
end
|
||||
|
||||
describe ":create action" do
|
||||
include Chef::RSpec::Pacemaker::CIBObject
|
||||
|
||||
it "should modify the constraint if it has a different score" do
|
||||
new_score = '100'
|
||||
fixture.score = new_score
|
||||
expected_configure_cmd_args = [fixture.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.score new_score
|
||||
end
|
||||
end
|
||||
|
||||
it "should modify the constraint if it has a resource added" do
|
||||
new_resource = 'bar:Stopped'
|
||||
expected = fixture.dup
|
||||
expected.resources = expected.resources.dup + [new_resource]
|
||||
expected_configure_cmd_args = [expected.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.resources expected.resources
|
||||
end
|
||||
end
|
||||
|
||||
it "should modify the constraint if it has a different resource" do
|
||||
new_resources = ['bar:Started']
|
||||
fixture.resources = new_resources
|
||||
expected_configure_cmd_args = [fixture.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.resources new_resources
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it_should_behave_like "a non-runnable resource", fixture
|
||||
|
||||
end
|
@ -1,55 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../helpers/runnable_resource', this_dir)
|
||||
require File.expand_path('../fixtures/resource_group', this_dir)
|
||||
|
||||
describe "Chef::Provider::PacemakerGroup" do
|
||||
# for use inside examples:
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::RESOURCE_GROUP.dup }
|
||||
# for use outside examples (e.g. when invoking shared_examples)
|
||||
fixture = Chef::RSpec::Pacemaker::Config::RESOURCE_GROUP.dup
|
||||
|
||||
def lwrp_name
|
||||
'group'
|
||||
end
|
||||
|
||||
include_context "a Pacemaker LWRP"
|
||||
|
||||
before(:each) do
|
||||
@resource.members fixture.members.dup
|
||||
@resource.meta Hash[fixture.meta.dup]
|
||||
|
||||
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
Pacemaker::Resource::Group
|
||||
end
|
||||
|
||||
describe ":create action" do
|
||||
include Chef::RSpec::Pacemaker::CIBObject
|
||||
|
||||
it "should modify the group if it has a member resource added" do
|
||||
expected = fixture.dup
|
||||
expected.members = expected.members.dup + %w(resource3)
|
||||
expected_configure_cmd_args = [expected.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.members expected.members
|
||||
end
|
||||
end
|
||||
|
||||
it "should modify the group if it has different member resources" do
|
||||
expected = fixture.dup
|
||||
expected.members = %w(resource1 resource3)
|
||||
expected_configure_cmd_args = [expected.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.members expected.members
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it_should_behave_like "a runnable resource", fixture
|
||||
|
||||
end
|
@ -1,65 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../helpers/provider', this_dir)
|
||||
require File.expand_path('../helpers/non_runnable_resource', this_dir)
|
||||
require File.expand_path('../fixtures/location_constraint', this_dir)
|
||||
|
||||
describe "Chef::Provider::PacemakerLocation" do
|
||||
# for use inside examples:
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::LOCATION_CONSTRAINT.dup }
|
||||
# for use outside examples (e.g. when invoking shared_examples)
|
||||
fixture = Chef::RSpec::Pacemaker::Config::LOCATION_CONSTRAINT.dup
|
||||
|
||||
def lwrp_name
|
||||
'location'
|
||||
end
|
||||
|
||||
include_context "a Pacemaker LWRP"
|
||||
|
||||
before(:each) do
|
||||
@resource.rsc fixture.rsc
|
||||
@resource.score fixture.score
|
||||
@resource.node fixture.node.dup
|
||||
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
Pacemaker::Constraint::Location
|
||||
end
|
||||
|
||||
describe ":create action" do
|
||||
include Chef::RSpec::Pacemaker::CIBObject
|
||||
|
||||
it "should modify the constraint if it has a different resource" do
|
||||
new_resource = 'group2'
|
||||
fixture.rsc = new_resource
|
||||
expected_configure_cmd_args = [fixture.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.rsc new_resource
|
||||
end
|
||||
end
|
||||
|
||||
it "should modify the constraint if it has a different score" do
|
||||
new_score = '100'
|
||||
fixture.score = new_score
|
||||
expected_configure_cmd_args = [fixture.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.score new_score
|
||||
end
|
||||
end
|
||||
|
||||
it "should modify the constraint if it has a different node" do
|
||||
new_node = 'node2'
|
||||
fixture.node = new_node
|
||||
expected_configure_cmd_args = [fixture.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.node new_node
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it_should_behave_like "a non-runnable resource", fixture
|
||||
|
||||
end
|
@ -1,46 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../helpers/runnable_resource', this_dir)
|
||||
require File.expand_path('../fixtures/ms_resource', this_dir)
|
||||
|
||||
describe "Chef::Provider::PacemakerMs" do
|
||||
# for use inside examples:
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::MS_RESOURCE.dup }
|
||||
# for use outside examples (e.g. when invoking shared_examples)
|
||||
fixture = Chef::RSpec::Pacemaker::Config::MS_RESOURCE.dup
|
||||
|
||||
def lwrp_name
|
||||
'ms'
|
||||
end
|
||||
|
||||
include_context "a Pacemaker LWRP"
|
||||
|
||||
before(:each) do
|
||||
@resource.rsc fixture.rsc.dup
|
||||
@resource.meta Hash[fixture.meta.dup]
|
||||
|
||||
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
Pacemaker::Resource::MasterSlave
|
||||
end
|
||||
|
||||
describe ":create action" do
|
||||
include Chef::RSpec::Pacemaker::CIBObject
|
||||
|
||||
it "should modify the resource if it's changed" do
|
||||
expected = fixture.dup
|
||||
expected.rsc = 'primitive2'
|
||||
expected_configure_cmd_args = [expected.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.rsc expected.rsc
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it_should_behave_like "a runnable resource", fixture
|
||||
|
||||
end
|
@ -1,66 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../helpers/provider', this_dir)
|
||||
require File.expand_path('../helpers/non_runnable_resource', this_dir)
|
||||
require File.expand_path('../fixtures/order_constraint', this_dir)
|
||||
|
||||
describe "Chef::Provider::PacemakerOrder" do
|
||||
# for use inside examples:
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::ORDER_CONSTRAINT.dup }
|
||||
# for use outside examples (e.g. when invoking shared_examples)
|
||||
fixture = Chef::RSpec::Pacemaker::Config::ORDER_CONSTRAINT.dup
|
||||
|
||||
def lwrp_name
|
||||
'order'
|
||||
end
|
||||
|
||||
include_context "a Pacemaker LWRP"
|
||||
|
||||
before(:each) do
|
||||
@resource.score fixture.score
|
||||
@resource.ordering fixture.ordering.dup
|
||||
|
||||
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
Pacemaker::Constraint::Order
|
||||
end
|
||||
|
||||
describe ":create action" do
|
||||
include Chef::RSpec::Pacemaker::CIBObject
|
||||
|
||||
it "should modify the constraint if it has a different score" do
|
||||
new_score = '100'
|
||||
fixture.score = new_score
|
||||
expected_configure_cmd_args = [fixture.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.score new_score
|
||||
end
|
||||
end
|
||||
|
||||
it "should modify the constraint if it has a resource added" do
|
||||
new_resource = 'bar:Stopped'
|
||||
expected = fixture.dup
|
||||
expected.ordering = expected.ordering.dup + ' ' + new_resource
|
||||
expected_configure_cmd_args = [expected.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.ordering expected.ordering
|
||||
end
|
||||
end
|
||||
|
||||
it "should modify the constraint if it has a different ordering" do
|
||||
new_ordering = 'clone1 primitive1'
|
||||
fixture.ordering = new_ordering
|
||||
expected_configure_cmd_args = [fixture.reconfigure_command]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.ordering new_ordering
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it_should_behave_like "a non-runnable resource", fixture
|
||||
|
||||
end
|
@ -1,139 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
require File.expand_path('../helpers/runnable_resource', this_dir)
|
||||
require File.expand_path('../fixtures/keystone_primitive', this_dir)
|
||||
|
||||
describe "Chef::Provider::PacemakerPrimitive" do
|
||||
# for use inside examples:
|
||||
let(:fixture) { Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE }
|
||||
# for use outside examples (e.g. when invoking shared_examples)
|
||||
fixture = Chef::RSpec::Pacemaker::Config::KEYSTONE_PRIMITIVE
|
||||
|
||||
def lwrp_name
|
||||
'primitive'
|
||||
end
|
||||
|
||||
include_context "a Pacemaker LWRP"
|
||||
|
||||
before(:each) do
|
||||
@resource.agent fixture.agent
|
||||
@resource.params Hash[fixture.params]
|
||||
@resource.meta Hash[fixture.meta]
|
||||
@resource.op Hash[fixture.op]
|
||||
end
|
||||
|
||||
def cib_object_class
|
||||
Pacemaker::Resource::Primitive
|
||||
end
|
||||
|
||||
describe ":create action" do
|
||||
include Chef::RSpec::Pacemaker::CIBObject
|
||||
include Chef::RSpec::Mixlib::ShellOut
|
||||
|
||||
it "should modify the primitive if it has different params" do
|
||||
expected_configure_cmd_args = [
|
||||
%'--set-parameter "os_password" --parameter-value "newpasswd"',
|
||||
%'--delete-parameter "os_tenant_name"',
|
||||
].map { |args| "crm_resource --resource #{fixture.name} #{args}" }
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
new_params = Hash[fixture.params].merge("os_password" => "newpasswd")
|
||||
new_params.delete("os_tenant_name")
|
||||
@resource.params new_params
|
||||
@resource.meta Hash[fixture.meta].merge("target-role" => "Stopped")
|
||||
end
|
||||
end
|
||||
|
||||
it "should modify the primitive if it has different meta" do
|
||||
expected_configure_cmd_args = [
|
||||
%'--set-parameter "is-managed" --parameter-value "false" --meta',
|
||||
].map { |args| "crm_resource --resource #{fixture.name} #{args}" }
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
@resource.params Hash[fixture.params]
|
||||
@resource.meta Hash[fixture.meta].merge("is-managed" => "false")
|
||||
end
|
||||
end
|
||||
|
||||
it "should modify the primitive if it has different params and meta" do
|
||||
expected_configure_cmd_args = [
|
||||
%'--set-parameter "os_password" --parameter-value "newpasswd"',
|
||||
%'--delete-parameter "os_tenant_name"',
|
||||
%'--set-parameter "is-managed" --parameter-value "false" --meta',
|
||||
].map { |args| "crm_resource --resource #{fixture.name} #{args}" }
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
new_params = Hash[fixture.params].merge("os_password" => "newpasswd")
|
||||
new_params.delete("os_tenant_name")
|
||||
@resource.params new_params
|
||||
@resource.meta Hash[fixture.meta].merge("is-managed" => "false")
|
||||
end
|
||||
end
|
||||
|
||||
it "should modify the primitive if it has different op values" do
|
||||
expected_configure_cmd_args = [
|
||||
fixture.reconfigure_command.gsub('60', '120')
|
||||
]
|
||||
test_modify(expected_configure_cmd_args) do
|
||||
new_op = Hash[fixture.op]
|
||||
# Ensure we're not modifying our expectation as well as the input
|
||||
new_op['monitor'] = new_op['monitor'].dup
|
||||
new_op['monitor']['timeout'] = '120'
|
||||
@resource.op new_op
|
||||
end
|
||||
end
|
||||
|
||||
context "creation from scratch" do
|
||||
include_context "stopped resource"
|
||||
|
||||
it "should create a primitive if it doesn't already exist" do
|
||||
# The first time, Mixlib::ShellOut needs to return an empty definition.
|
||||
# Then the resource gets created so the second time it needs to return
|
||||
# the definition used for creation.
|
||||
stub_shellout("", fixture.definition_string)
|
||||
|
||||
provider.run_action :create
|
||||
|
||||
expect(@chef_run).to run_execute(stopped_fixture.configure_command)
|
||||
expect(@resource).to be_updated
|
||||
end
|
||||
|
||||
it "should barf if crm fails to create the primitive" do
|
||||
stub_shellout("", ["crm configure failed", "oh noes", 3])
|
||||
|
||||
expect { provider.run_action :create }.to \
|
||||
raise_error(RuntimeError, "Failed to create #{fixture}")
|
||||
|
||||
expect(@chef_run).to run_execute(stopped_fixture.configure_command)
|
||||
expect(@resource).not_to be_updated
|
||||
end
|
||||
|
||||
# This scenario seems rather artificial and unlikely, but it doesn't
|
||||
# do any harm to test it.
|
||||
it "should barf if crm creates a primitive with empty definition" do
|
||||
stub_shellout("", "")
|
||||
|
||||
expect { provider.run_action :create }.to \
|
||||
raise_error(RuntimeError, "Failed to create #{fixture}")
|
||||
|
||||
expect(@chef_run).to run_execute(stopped_fixture.configure_command)
|
||||
expect(@resource).not_to be_updated
|
||||
end
|
||||
end
|
||||
|
||||
it "should barf if the primitive is already defined with the wrong agent" do
|
||||
existing_agent = "ocf:openstack:something-else"
|
||||
definition = fixture.definition_string.sub(fixture.agent, existing_agent)
|
||||
stub_shellout(definition)
|
||||
|
||||
expected_error = \
|
||||
"Existing #{fixture} has agent '#{existing_agent}' " \
|
||||
"but recipe wanted '#{@resource.agent}'"
|
||||
expect { provider.run_action :create }.to \
|
||||
raise_error(RuntimeError, expected_error)
|
||||
|
||||
expect(@resource).not_to be_updated
|
||||
end
|
||||
end
|
||||
|
||||
it_should_behave_like "a runnable resource", fixture
|
||||
|
||||
end
|
@ -1,53 +0,0 @@
|
||||
require 'chefspec'
|
||||
|
||||
ENV['RSPEC_RUNNING'] = 'true'
|
||||
|
||||
RSpec.configure do |config|
|
||||
# config.mock_with :rspec do |mocks|
|
||||
# # This option should be set when all dependencies are being loaded
|
||||
# # before a spec run, as is the case in a typical spec helper. It will
|
||||
# # cause any verifying double instantiation for a class that does not
|
||||
# # exist to raise, protecting against incorrectly spelt names.
|
||||
# mocks.verify_doubled_constant_names = true
|
||||
# end
|
||||
|
||||
# Specify the path for Chef Solo to find cookbooks (default: [inferred from
|
||||
# the location of the calling spec file])
|
||||
#config.cookbook_path = '/var/cookbooks'
|
||||
|
||||
# Specify the path for Chef Solo to find roles (default: [ascending search])
|
||||
#config.role_path = '/var/roles'
|
||||
|
||||
# Specify the Chef log_level (default: :warn)
|
||||
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)
|
||||
#config.path = 'ohai.json'
|
||||
|
||||
# Specify the operating platform to mock Ohai data from (default: nil)
|
||||
config.platform = 'suse'
|
||||
|
||||
# Specify the operating version to mock Ohai data from (default: nil)
|
||||
config.version = '11.03'
|
||||
|
||||
# Disable deprecated "should" syntax
|
||||
# https://github.com/rspec/rspec-expectations/blob/master/Should.md
|
||||
config.expect_with :rspec do |c|
|
||||
c.syntax = :expect
|
||||
end
|
||||
|
||||
config.run_all_when_everything_filtered = true
|
||||
config.filter_run :focus => true
|
||||
end
|
||||
|
||||
# FIXME
|
||||
#running_guard = ENV['GUARD_NOTIFY'] && ! ENV['GUARD_NOTIFY'].empty?
|
||||
|
||||
if ENV['RUBYDEPS']
|
||||
require 'rubydeps'
|
||||
Rubydeps.start
|
||||
end
|
||||
|
||||
if false # ! running_guard
|
||||
at_exit { ChefSpec::Coverage.report! }
|
||||
end
|
@ -1,10 +0,0 @@
|
||||
property $id="cib-bootstrap-options" \
|
||||
stonith-enabled="<%= @stonith_enabled %>" \
|
||||
no-quorum-policy="<%= @no_quorum_policy %>" \
|
||||
placement-strategy="balanced"
|
||||
op_defaults $id="op-options" \
|
||||
timeout="<%= @op_default_timeout %>" \
|
||||
record-pending="true"
|
||||
rsc_defaults $id="rsc-options" \
|
||||
resource-stickiness="1" \
|
||||
migration-threshold="3"
|
@ -1,20 +0,0 @@
|
||||
# Logging
|
||||
debug 1
|
||||
use_logd false
|
||||
logfacility daemon
|
||||
|
||||
# Misc Options
|
||||
traditional_compression off
|
||||
compression bz2
|
||||
coredumps true
|
||||
|
||||
# Communications
|
||||
udpport 691
|
||||
bcast eth0
|
||||
autojoin any
|
||||
|
||||
# Thresholds (in seconds)
|
||||
keepalive 1
|
||||
warntime 6
|
||||
deadtime 10
|
||||
initdead 15
|
@ -1,3 +0,0 @@
|
||||
SBD_DEVICE="<%= @sbd_devices.join("; ") %>"
|
||||
# The next line enables the watchdog support, and makes SBD checks Pacemaker quorum and node health:
|
||||
SBD_OPTS="-W -P"
|
Loading…
x
Reference in New Issue
Block a user