Retire stackforge/cookbook-pacemaker

This commit is contained in:
Monty Taylor 2015-10-17 16:02:55 -04:00
parent 5b4bb9262d
commit 5f8b586576
78 changed files with 7 additions and 3891 deletions

2
.gitignore vendored
View File

@ -1,2 +0,0 @@
Gemfile.lock
tmp/

21
Gemfile
View File

@ -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

View File

@ -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

View File

@ -1,77 +0,0 @@
[![Build Status](https://travis-ci.org/crowbar/barclamp-pacemaker.png?branch=release/roxy/master)](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
View 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".

View File

@ -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

View File

@ -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] = ""

View File

@ -1,3 +0,0 @@
# managed by Chef
# start corosync at boot [yes|no]
START=yes

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 => {}

View File

@ -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

View File

@ -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 => {}

View File

@ -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

View File

@ -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 => {}

View File

@ -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

View File

@ -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 => {}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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"