Skip to content

Commit

Permalink
First pass on heartbeat LWRP.
Browse files Browse the repository at this point in the history
  • Loading branch information
coderanger committed May 22, 2012
1 parent 301c4b3 commit 9be87eb
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 0 deletions.
76 changes: 76 additions & 0 deletions libraries/default.rb
@@ -0,0 +1,76 @@
#
# Cookbook Name:: heartbeat
# Library:: default
#
# Copyright 2009-2012, Opscode, 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.
#

class Chef
class Resource
class HeartbeatResourceGroup
include Chef::Mixin::ParamsValidate
include Chef::Mixin::RecipeDefinitionDSLCore

attr_reader :run_context, :cookbook_name, :recipe_name, :sub_resources

def initialize(run_context, cookbook_name, recipe_name)
@run_context = run_context
@cookbook_name = cookbook_name
@recipe_name = recipe_name
@sub_resources = []
end

def default_node(default_node=nil)
set_or_return(:default_node, default_node, :kind_of => String)
end

def method_missing(name, *args, &block)
# Build the set of names to check for a valid resource
lookup_path = ["heartbeat_#{name}"]
run_context.cookbook_collection.each do |cookbook_name, cookbook_ver|
lookup_path << "#{cookbook_name}_heartbeat_#{name}"
end
resource = nil
# Try to find our resource
lookup_path.each do |resource_name|
begin
Chef::Log.debug "Trying to load heartbeat resource #{resource_name} for #{name}"
resource = super(resource_name.to_sym, args.first || name, &block)
break
rescue NameError => e
# Works on any MRI ruby
if e.name == resource_name.to_sym || e.inspect =~ /\b#{resource_name}\b/
next
else
raise e
end
end
end
raise NameError, "No resource found for #{name}. Tried #{lookup_path.join(', ')}" unless resource
raise ArgumentError, "Resource instance #{resource} is not a valid heartbeat resource" unless resource.respond_to?(:to_resource)
provider = resource.provider || begin
Chef::Platform.provider_for_resource(resource)
rescue ArgumentError => e
nil
end
# As a default this has #action_nothing on it
resource.provider Chef::Provider::HeartbeatNull unless provider
@sub_resources << resource
resource
end

end
end
end
57 changes: 57 additions & 0 deletions providers/default.rb
@@ -0,0 +1,57 @@
#
# Cookbook Name:: heartbeat
# Provider:: default
#
# Copyright 2009-2012, Opscode, 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.
#

action :create do
query = new_resource.search || "recipes:#{new_resource.cookbook_name}\\:\\:#{new_resource.recipe_name}"
nodes = search(:node, query)
nodes << node if nodes.select{|n| n['macaddress'] == node['macaddress']}.empty?
interface = new_resource.interface.is_a?(Array) ? new_resource.interface : [new_resource.interface]
authkeys = new_resource.authkeys.is_a?(Array) ? new_resource.authkeys : [new_resource.authkeys]

template "/etc/ha.d/ha.cf" do
cookbook "heartbeat"
source "ha.cf.erb"
mode "644"
owner "root"
group "root"
notifies :restart, "service[heartbeat]"
variables :heartbeat => new_resource, :nodes => nodes, :interface => interface
end

template "/etc/ha.d/authkeys" do
cookbook "heartbeat"
source "authkeys.erb"
owner "root"
group "root"
mode "600"
notifies :restart, "service[heartbeat]"
variables :active => new_resource.active_key || authkeys.length, :keys => authkeys
end

template "/etc/ha.d/haresources" do
cookbook "heartbeat"
source "haresources.erb"
owner "root"
group "root"
mode "644"
notifies :restart, "service[heartbeat]"
variables :heartbeat => new_resource, :default => nodes.sort_by{|n| n['macaddress']}.first['hostname']
end

end
20 changes: 20 additions & 0 deletions providers/null.rb
@@ -0,0 +1,20 @@
#
# Cookbook Name:: heartbeat
# Provider:: null
#
# Copyright 2009-2012, Opscode, 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.
#

# This space left intentionally blank
55 changes: 55 additions & 0 deletions resources/default.rb
@@ -0,0 +1,55 @@
#
# Cookbook Name:: heartbeat
# Resource:: default
#
# Copyright 2009-2012, Opscode, 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.
#

default_action :create

def after_created
run_context.resource_collection.each do |res|
raise "You may only define a single heartbeat resource per node. Found #{res}: #{res.defined_at}" if res.is_a?(self.class) && res.name != name
end
end

# These map directly to ha.cf directives
attribute :auto_failback, :kind_of => [TrueClass, FalseClass], :default => false
attribute :autojoin, :equal_to => [:none, 'none', :other, 'other', :any, 'any'], :default => :none
attribute :compression, :equal_to => [nil, :zlib, 'zlib', :bz2, 'bz2'], :default => nil
attribute :compression_threshold, :kind_of => Integer, :default => 2
attribute :deadtime, :kind_of => Integer, :default => 2
attribute :initdead, :kind_of => Integer, :default => 15
attribute :keepalive, :kind_of => Integer, :default => 250
attribute :logfacility, :kind_of => String, :default => 'local0'
attribute :udpport, :kind_of => Integer, :default => 694
attribute :warntime, :kind_of => Integer, :default => 1

attribute :search, :kind_of => String
attribute :authkeys, :kind_of => [Array, String], :required => true
attribute :active_key, :kind_of => Integer
attribute :mode, :equal_to => [:bcast, 'bcast', :mcast, 'mcast', :ucast, 'ucast'], :default => :ucast
attribute :interface, :kind_of => [String, Array], :default => node['network']['default_interface']
attribute :mcast_group, :kind_of => String
attribute :mcast_ttl, :kind_of => Integer, :default => 1
attribute :resource_groups, :default => []

def resources(ip=nil, &block)
group = ::Chef::Resource::HeartbeatResourceGroup.new(run_context, cookbook_name, recipe_name)
group.ipaddr ip if ip
group.instance_eval(&block) if block
resource_groups << group
group
end
24 changes: 24 additions & 0 deletions resources/ipaddr.rb
@@ -0,0 +1,24 @@
#
# Cookbook Name:: heartbeat
# Resource:: ipaddr
#
# Copyright 2009-2012, Opscode, 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.
#

attribute :ip, :kind_of => String, :name_attribute => true

def to_resource
ip
end
4 changes: 4 additions & 0 deletions templates/default/authkeys.erb
@@ -0,0 +1,4 @@
auth <%= @active %>
<% @keys.each_with_index do |secret, i| -%>
<%= i+1 %> sha1 <%= secret %>
<% end -%>
28 changes: 28 additions & 0 deletions templates/default/ha.cf.erb
@@ -0,0 +1,28 @@
udpport <%= @heartbeat.udpport %>
autojoin <%= @heartbeat.autojoin %>
<% if @heartbeat.compression -%>
compression <%= @heartbeat.compression %>
<% end -%>
compression_threshold <%= @heartbeat.compression_threshold %>
deadtime <%= @heartbeat.deadtime %>
initdead <%= @heartbeat.initdead %>
keepalive <%= @heartbeat.keepalive %>ms
logfacility <%= @heartbeat.logfacility %>
warntime <%= @heartbeat.warntime %>
<% case @heartbeat.mode.to_sym -%>
<% when :ucast -%>
<% @nodes.select{|n| n['macaddress'] != node['macaddress']}.each do |n| -%>
ucast <%=@interface.first %> <%= n['ipaddress'] %>
<% end -%>
<% when :bcast -%>
bcast <%= @interface.join(' ') %>
<% when :mcast -%>
mcast <%= @interface.first %> <%= @heartbeat.mcast_group %> <%= @heartbeat.udpport %> <%= @heartbeat.mcast_ttl %>
<% end -%>

auto_failback <%= @heartbeat.auto_failback ? "on" : "off" %>
<% @nodes.each do |n| -%>
node <%= n['hostname'] %>
<% end -%>
3 changes: 3 additions & 0 deletions templates/default/haresources.erb
@@ -0,0 +1,3 @@
<% @heartbeat.resource_groups.each do |group| -%>
<%= group.default_node || @default %> <%= group.sub_resources.map{|r| r.to_resource}.join(' ') %>
<% end -%>

0 comments on commit 9be87eb

Please sign in to comment.