Implement ringbuilder

This commit adds a class, defined resources and
native types that can be used together to manage
the creation, configuration, and rebalancing of
rings.

Creates the following native types:

  ring_account_device
  ring_container_device
  ring_object_device

The implementation of these types is provided
by swift-ring-builder and most of the functionality
is provided by the parent provider (SwiftRingBuilder)

Each of the providers is hard-coded to find the
relevent ring database in /etc/swift. This directory
location can currently not be configured.

Each provider implements self.instances so the
current state can of the ring can be queried from
puppet.

These providers do not handle either the creation
or the rebalancing of the rings, they are intended
to be used in combination with defined resource types
that perform that functionality:

  swift::ringbuilder::create
  swift::ringbuilder::rebalance

The swift::ringbuilder class is also created as
a part of this commit. This class does the following:

  - creates object, account, and container rings
(it uses the same parameters to create these rings)
  - creates the rebalance defines
  - sets up the following relationships:
    for each ring type, create should be applied before
    the native types which should refresh the rebalance
This commit is contained in:
Dan Bode 2012-01-23 10:47:35 -08:00
parent 50eaa8e66d
commit 917426380e
10 changed files with 300 additions and 9 deletions

@ -0,0 +1,15 @@
require File.join(File.dirname(__FILE__), '..', 'swift_ring_builder')
Puppet::Type.type(:ring_account_device).provide(
:swift_ring_builder,
:parent => Puppet::Provider::SwiftRingBuilder
) do
optional_commands :swift_ring_builder => 'swift-ring-builder'
# TODO maybe this should be a parameter eventually so that
# it can be configurable
def self.builder_file_path
'/etc/swift/account.builder'
end
end

@ -0,0 +1,15 @@
require File.join(File.dirname(__FILE__), '..', 'swift_ring_builder')
Puppet::Type.type(:ring_container_device).provide(
:swift_ring_builder,
:parent => Puppet::Provider::SwiftRingBuilder
) do
optional_commands :swift_ring_builder => 'swift-ring-builder'
# TODO maybe this should be a parameter eventually so that
# it can be configurable
def self.builder_file_path
'/etc/swift/container.builder'
end
end

@ -0,0 +1,15 @@
require File.join(File.dirname(__FILE__), '..', 'swift_ring_builder')
Puppet::Type.type(:ring_object_device).provide(
:swift_ring_builder,
:parent => Puppet::Provider::SwiftRingBuilder
) do
optional_commands :swift_ring_builder => 'swift-ring-builder'
# TODO maybe this should be a parameter eventually so that
# it can be configurable
def self.builder_file_path
'/etc/swift/object.builder'
end
end

@ -0,0 +1,126 @@
class Puppet::Provider::SwiftRingBuilder < Puppet::Provider
def self.instances
# TODO iterate through the databases
# and add the database that we used a property
ring.keys.collect do |name|
new(:name => name)
end
end
def self.ring
@my_ring ||= lookup_ring
end
def self.lookup_ring
object_hash = {}
if File.exists?(builder_file_path)
swift_ring_builder(builder_file_path).split("\n")[4..-1].each do |row|
if row =~ /^\s+(\d+)\s+(\d+)\s+(\S+)\s+(\d+)\s+(\S+)\s+(\d+\.\d+)\s+(\d+)\s+(-?\d+\.\d+)\s+(\S*)$/
object_hash["#{$3}:#{$4}"] = {
:id => $1,
:zone => $2,
:device_name => $5,
:weight => $6,
:partitions => $7,
:balance => $8,
:meta => $9
}
else
Puppet.warning("Unexpected line: #{row}")
end
end
end
object_hash
end
def ring
self.class.ring
end
def builder_file_path
self.class.builder_file_path
end
def exists?
ring[resource[:name]]
end
def create
[:zone, :device_name, :weight].each do |param|
raise(Puppet::Error, "#{param} is required") unless resource[param]
end
swift_ring_builder(
builder_file_path,
'add',
"z#{resource[:zone]}-#{resource[:name]}/#{resource[:device_name]}",
resource[:weight]
)
end
def id
ring[resource[:name]][:id]
end
def id=(id)
raise(Puppet::Error, "Cannot assign id, it is immutable")
end
def zone
ring[resource[:name]][:zone]
end
# TODO - is updating the zone supported?
def zone=(zone)
Puppet.warning('Setting zone is not yet supported, I am not even sure if it is supported')
end
def device_name
ring[resource[:name]][:device_name]
end
def device_name=(name)
Puppet.warning('I think it makes sense to set the name, it is just not yet supported')
end
def weight
ring[resource[:name]][:weight]
# get the weight
end
def weight=(weight)
swift_ring_builder(
builder_file_path,
'set_weight',
"d#{ring[resource[:name]][:id]}",
resource[:weight]
)
# requires a rebalance
end
def partitions
ring[resource[:name]][:partitions]
end
def partitions=(part)
raise(Puppet::Error, "Cannot set partitions, it is set by rebalancing")
end
def balance
ring[resource[:name]][:balance]
end
def balance=(balance)
raise(Puppet::Error, "Cannot set balance, it is set by rebalancing")
end
def meta
ring[resource[:name]][:meta]
end
def meta=(meta)
raise(Puppet::Error, "Cannot set meta, I am not sure if it makes sense or what it is for")
end
end

@ -0,0 +1,30 @@
Puppet::Type.newtype(:ring_account_device) do
require 'ipaddr'
ensurable
newparam(:name, :namevar => true) do
validate do |value|
address = value.split(':')
raise(Puppet::Error, "invalid name #{value}") unless address.size == 2
IPAddr.new(address[0])
end
end
newproperty(:zone)
newproperty(:device_name)
newproperty(:weight)
newproperty(:meta)
[:id, :partitions, :balance].each do |param|
newproperty(param) do
validate do |value|
raise(Puppet::Error, "#{param} is a read only property, cannot be assigned")
end
end
end
end

@ -0,0 +1,30 @@
Puppet::Type.newtype(:ring_container_device) do
require 'ipaddr'
ensurable
newparam(:name, :namevar => true) do
validate do |value|
address = value.split(':')
raise(Puppet::Error, "invalid name #{value}") unless address.size == 2
IPAddr.new(address[0])
end
end
newproperty(:zone)
newproperty(:device_name)
newproperty(:weight)
newproperty(:meta)
[:id, :partitions, :balance].each do |param|
newproperty(param) do
validate do |value|
raise(Puppet::Error, "#{param} is a read only property, cannot be assigned")
end
end
end
end

@ -0,0 +1,30 @@
Puppet::Type.newtype(:ring_object_device) do
require 'ipaddr'
ensurable
newparam(:name, :namevar => true) do
validate do |value|
address = value.split(':')
raise(Puppet::Error, "invalid name #{value}") unless address.size == 2
IPAddr.new(address[0])
end
end
newproperty(:zone)
newproperty(:device_name)
newproperty(:weight)
newproperty(:meta)
[:id, :partitions, :balance].each do |param|
newproperty(param) do
validate do |value|
raise(Puppet::Error, "#{param} is a read only property, cannot be assigned")
end
end
end
end

@ -1,13 +1,26 @@
#
# role for deploying
#
class swift::ringbuider(
class swift::ringbuilder(
$part_power = undef,
$replicas = undef,
$min_part_hours = undef
) {
# search for all nodes that have class swift::storage
#
# determine what their swift drives are (how can I determine this?)
#exec {
# command =>
#}
Class['swift'] -> Class['swift::ringbuilder']
swift::ringbuilder::create{ ['object', 'account', 'container']:
part_power => $part_power,
replicas => $replicas,
min_part_hours => $min_part_hours,
}
Swift::Ringbuilder::Create['object'] -> Ring_object_device <| |> ~> Swift::Ringbuilder::Rebalance['object']
Swift::Ringbuilder::Create['container'] -> Ring_container_device <| |> ~> Swift::Ringbuilder::Rebalance['container']
Swift::Ringbuilder::Create['account'] -> Ring_account_device <| |> ~> Swift::Ringbuilder::Rebalance['account']
swift::ringbuilder::rebalance{ ['object', 'account', 'container']: }
}

@ -0,0 +1,15 @@
define ringbuilder::create(
$part_power = 18,
$replicas = 5,
$min_part_hours = 1
) {
validate_re($name, '^object|container|account$')
exec { "create_${name}":
command => "swift-ring-builder /etc/swift/${name}.builder create ${part_power} ${replicas} ${min_part_hours}",
path => ['/usr/bin'],
creates => "/etc/swift/${name}.builder",
}
}

@ -1,7 +1,9 @@
define ringbuilder::rebalance() {
validate_re($name, '^object|contianer|account$')
exec { "rebalance_${name}":
command => "swift-ring-builder ${name}.builder rebalance",
command => "swift-ring-builder /etc/swift/${name}.builder rebalance",
path => ['/usr/bin'],
refreshonly => true,
}