diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7ed52fd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+Berksfile.lock
+validation.pem
+metadata.json
+*~
+.bundle/
+cookbooks/
+.cookbooks
+.vagrant
+*.swp
diff --git a/Berksfile b/Berksfile
new file mode 100644
index 0000000..4817cf5
--- /dev/null
+++ b/Berksfile
@@ -0,0 +1,7 @@
+site :opscode
+
+cookbook 'swift', :path => '.'
+cookbook 'apt', :git => 'git://github.com/opscode-cookbooks/apt.git'
+cookbook 'statsd', :git => 'git://github.com/att-cloud/cookbook-statsd.git'
+cookbook 'memcached'
+cookbook 'sysctl'
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..ecf6173
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,9 @@
+source "https://rubygems.org"
+
+gem "chef", "~> 11.4.4"
+gem "json", "<= 1.7.7" # chef dependency
+gem "berkshelf", "~> 1.4.5"
+gem "chefspec", "~> 1.3.0"
+gem "foodcritic"
+gem "strainer"
+gem "webmock", "~> 1.11.0"
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..2068c68
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,202 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ activesupport (3.2.13)
+ i18n (= 0.6.1)
+ multi_json (~> 1.0)
+ addressable (2.3.4)
+ akami (1.2.0)
+ gyoku (>= 0.4.0)
+ nokogiri (>= 1.4.0)
+ berkshelf (1.4.5)
+ activesupport (>= 3.2.0)
+ addressable
+ celluloid (>= 0.14.0)
+ chozo (>= 0.6.1)
+ faraday (>= 0.8.5)
+ hashie (>= 2.0.2)
+ json (>= 1.5.0)
+ minitar
+ mixlib-config (~> 1.1)
+ mixlib-shellout (~> 1.1)
+ multi_json (~> 1.5)
+ retryable
+ ridley (~> 0.12.4)
+ solve (>= 0.4.2)
+ thor (~> 0.18.0)
+ yajl-ruby
+ builder (3.2.0)
+ celluloid (0.14.0)
+ timers (>= 1.0.0)
+ chef (11.4.4)
+ erubis
+ highline (>= 1.6.9)
+ json (>= 1.4.4, <= 1.7.7)
+ mixlib-authentication (>= 1.3.0)
+ mixlib-cli (~> 1.3.0)
+ mixlib-config (>= 1.1.2)
+ mixlib-log (>= 1.3.0)
+ mixlib-shellout
+ net-ssh (~> 2.6)
+ net-ssh-multi (~> 1.1.0)
+ ohai (>= 0.6.0)
+ rest-client (>= 1.0.4, < 1.7.0)
+ yajl-ruby (~> 1.1)
+ chefspec (1.3.0)
+ chef (>= 10.0)
+ erubis
+ fauxhai (>= 0.1.1, < 2.0)
+ minitest-chef-handler (>= 0.6.0)
+ rspec (~> 2.0)
+ chozo (0.6.1)
+ activesupport (>= 3.2.0)
+ hashie (>= 2.0.2)
+ multi_json (>= 1.3.0)
+ ci_reporter (1.8.4)
+ builder (>= 2.1.2)
+ crack (0.3.2)
+ diff-lcs (1.2.4)
+ erubis (2.7.0)
+ faraday (0.8.7)
+ multipart-post (~> 1.1)
+ fauxhai (1.1.1)
+ httparty
+ net-ssh
+ ohai
+ ffi (1.8.1)
+ foodcritic (2.1.0)
+ erubis
+ gherkin (~> 2.11.7)
+ nokogiri (~> 1.5.4)
+ rak (~> 1.4)
+ treetop (~> 1.4.10)
+ yajl-ruby (~> 1.1.0)
+ gherkin (2.11.8)
+ multi_json (~> 1.3)
+ gssapi (1.0.3)
+ ffi (>= 1.0.1)
+ gyoku (1.0.0)
+ builder (>= 2.1.2)
+ hashie (2.0.5)
+ highline (1.6.19)
+ httparty (0.11.0)
+ multi_json (~> 1.0)
+ multi_xml (>= 0.5.2)
+ httpclient (2.2.0.2)
+ httpi (0.9.7)
+ rack
+ i18n (0.6.1)
+ ipaddress (0.8.0)
+ json (1.7.7)
+ little-plugger (1.1.3)
+ logging (1.6.2)
+ little-plugger (>= 1.1.3)
+ mime-types (1.23)
+ minitar (0.5.4)
+ minitest (4.7.4)
+ minitest-chef-handler (1.0.1)
+ chef
+ ci_reporter
+ minitest (~> 4.7.3)
+ mixlib-authentication (1.3.0)
+ mixlib-log
+ mixlib-cli (1.3.0)
+ mixlib-config (1.1.2)
+ mixlib-log (1.6.0)
+ mixlib-shellout (1.1.0)
+ multi_json (1.7.4)
+ multi_xml (0.5.4)
+ multipart-post (1.2.0)
+ net-http-persistent (2.8)
+ net-ssh (2.6.7)
+ net-ssh-gateway (1.2.0)
+ net-ssh (>= 2.6.5)
+ net-ssh-multi (1.1)
+ net-ssh (>= 2.1.4)
+ net-ssh-gateway (>= 0.99.0)
+ nokogiri (1.5.9)
+ nori (1.1.5)
+ ohai (6.16.0)
+ ipaddress
+ mixlib-cli
+ mixlib-config
+ mixlib-log
+ mixlib-shellout
+ systemu
+ yajl-ruby
+ polyglot (0.3.3)
+ rack (1.5.2)
+ rak (1.4)
+ rest-client (1.6.7)
+ mime-types (>= 1.16)
+ retryable (1.3.3)
+ ridley (0.12.4)
+ addressable
+ celluloid (~> 0.14.0)
+ chozo (>= 0.6.0)
+ erubis
+ faraday (>= 0.8.4)
+ hashie (>= 2.0.2)
+ mixlib-authentication (>= 1.3.0)
+ mixlib-config (>= 1.1.0)
+ mixlib-log (>= 1.3.0)
+ mixlib-shellout (>= 1.1.0)
+ net-http-persistent (>= 2.8)
+ net-ssh
+ retryable
+ solve (>= 0.4.4)
+ winrm (~> 1.1.0)
+ rspec (2.13.0)
+ rspec-core (~> 2.13.0)
+ rspec-expectations (~> 2.13.0)
+ rspec-mocks (~> 2.13.0)
+ rspec-core (2.13.1)
+ rspec-expectations (2.13.0)
+ diff-lcs (>= 1.1.3, < 2.0)
+ rspec-mocks (2.13.1)
+ rubyntlm (0.1.1)
+ savon (0.9.5)
+ akami (~> 1.0)
+ builder (>= 2.1.2)
+ gyoku (>= 0.4.0)
+ httpi (~> 0.9)
+ nokogiri (>= 1.4.0)
+ nori (~> 1.0)
+ wasabi (~> 1.0)
+ solve (0.4.4)
+ json
+ strainer (2.1.0)
+ berkshelf (~> 1.3)
+ systemu (2.5.2)
+ thor (0.18.1)
+ timers (1.1.0)
+ treetop (1.4.12)
+ polyglot
+ polyglot (>= 0.3.1)
+ uuidtools (2.1.4)
+ wasabi (1.0.0)
+ nokogiri (>= 1.4.0)
+ webmock (1.11.0)
+ addressable (>= 2.2.7)
+ crack (>= 0.3.2)
+ winrm (1.1.2)
+ gssapi (~> 1.0.0)
+ httpclient (~> 2.2.0.2)
+ logging (~> 1.6.1)
+ nokogiri (~> 1.5.0)
+ rubyntlm (~> 0.1.1)
+ savon (= 0.9.5)
+ uuidtools (~> 2.1.2)
+ yajl-ruby (1.1.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ berkshelf (~> 1.4.5)
+ chef (~> 11.4.4)
+ chefspec (~> 1.3.0)
+ foodcritic
+ json (<= 1.7.7)
+ strainer
+ webmock (~> 1.11.0)
diff --git a/README.md b/README.md
index ad8ab7b..924e302 100644
--- a/README.md
+++ b/README.md
@@ -1,37 +1,260 @@
Description
===========
-Installs the OpenStack Object Storage service **Swift** as part of the OpenStack reference deployment Chef for OpenStack. The http://github.com/mattray/chef-openstack-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. Swift is currently installed from packages.
+Installs the OpenStack Object Storage service **Swift** as part of the OpenStack reference deployment Chef for OpenStack. The http://github.com/stackforge/chef-openstack-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. Swift is currently installed from packages.
https://wiki.openstack.org/wiki/Swift
Requirements
============
+Clients
+--------
+
+ * CentOS >= 6.3
+ * Ubuntu >= 12.04
+
+Chef
+---------
+
+ * 11.4.4
+
Cookbooks
---------
-Usage
+ * memcached
+ * sysctl
+
+Roles
=====
+ * swift-account-server - storage node for account data
+ * swift-container-server - storage node for container data
+ * swift-object-server - storage node for object server
+ * swift-proxy-server - proxy for swift storge nodes
+ * swift-setup - server responsible for generating initial settings
+ * swift-management-server - responsible for ring generation
+
+The swift-management-server role performs the following functions:
+
+ * proxy node that knows super admin password
+ * ring repository and ring building workstation
+ * generally always has the swift-setup role too
+ * there can only be _one_ swift-management-server
+
+There *must* be node with the the swift-managment-server role to act
+as the ring repository.
+
+In small environments, it is likely that all storage machines will
+have all-in-one roles, with a load balancer ahead of it
+
+In larger environments, where it is cost effective to split the proxy
+and storage layer, storage nodes will carry
+swift-{account,container,object}-server roles, and there will be
+dedicated hosts with the swift-proxy-server role.
+
+In really really huge environments, it's possible that the storage
+node will be split into swift-{container,accout}-server nodes and
+swift-object-server nodes.
+
+
Attributes
==========
+ * ```default[:swift][:authmode]``` - "swauth" or "keystone" (default "swauth"). Right now, only swauth is supported (defaults to swauth)
+
+ * ```default[:swift][:swift_secret_databag_name]``` - this cookbook supports an optional secret databag where we will retrieve the following attributes overriding any default attributes below. (defaults to nil)
+
+```
+ {
+ "id": "swift_dal2",
+ "swift_hash": "1a7c0568fa84"
+ "swift_authkey": "keY4all"
+ "dispersion_auth_user": "ops:dispersion",
+ "dispersion_auth_key": "dispersionpass"
+ }
+```
+
+ * ```default[:swift][:swift_hash]``` - swift_hash_path_suffix in /etc/swift/swift.conf (defaults to 107c0568ea84)
+
+ * ```default[:swift][:audit_hour]``` - Hour to run swift_auditor on storage nodes (defaults to 5)
+
+ * ```default[:swift][:disk_enum_expr]``` - Eval-able expression that lists
+ candidate disk nodes for disk probing. The result shoule be a hash
+ with keys being the device name (without the leading "/dev/") and a
+ hash block of any extra info associated with the device. For
+ example: { "sdc" => { "model": "Hitachi 7K3000" }}. Largely,
+ though, if you are going to make a list of valid devices, you
+ probably know all the valid devices, and don't need to pass any
+ metadata about them, so { "sdc" => {}} is probably enough. Example
+ expression: Hash[('a'..'f').to_a.collect{|x| [ "sd{x}", {} ]}]
+
+ * ```default[:swift][:ring][:part_power]``` - controls the size of the ring (defaults to 18)
+
+ * ```default[:swift][:ring][:min_part_hours]``` - the minimum number of hours before swift is allowed to migrate a partition (defaults to 1)
+
+ * ```default[:swift][:ring][:replicas]``` - how many replicas swift should retain (defaults to 3)
+
+ * ```default[:swift][:disk_test_filter]``` - an array of expressions that must
+ all be true in order a block deviced to be considered for
+ formatting and inclusion in the cluster. Each rule gets evaluated
+ with "candidate" set to the device name (without the leading
+ "/dev/") and info set to the node hash value. Default rules:
+
+ * "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~
+ /vd[^a]/"
+
+ * "File.exists?('/dev/ + candidate)"
+
+ * "not system('/sbin/sfdisk -V /dev/' + candidate + '>/dev/null 2>&2')"
+
+ * "info['removable'] = 0" ])
+
+ * ```default[:swift][:expected_disks]``` - an array of device names that the
+ operator expecs to be identified by the previous two values. This
+ acts as a second-check on discovered disks. If this array doesn't
+ match the found disks, then chef processing will be stopped.
+ Example: ("b".."f").collect{|x| "sd#{x}"}. Default: none.
+
+There are other attributes that must be set depending on authmode.
+For "swauth", the following attributes are used:
+
+ * ```default[:swift][:authkey]``` - swauth super admin key if using swauth (defaults to test)
+
+In addition, because swift is typically deployed as a cluster
+there are some attributes used to find interfaces and ip addresses
+on storage nodes:
+
+ * ```default[:swift][:git_builder_ip]``` - the IP address of the management server which other cluster members will use as their git pull target for ring updates (defaults to 127.0.0.1)
+ * ```default[:swift][:network][:proxy-bind-ip]``` - the IP address to bind to
+ on the proxy servers (defaults to 0.0.0.0 for all addresses)
+ * ```default[:swift][:network][:proxy-bind-port]``` - the port to bind to
+ on the proxy servers (defaults to 8080)
+ * ```default[:swift][:network][:account-bind-ip]``` - the IP address to bind to
+ on the account servers (defaults to 0.0.0.0 for all addresses)
+ * ```default[:swift][:network][:account-bind-port]``` - the port to bind to
+ on the account servers (defaults to 6002)
+ * ```default[:swift][:network][:container-bind-ip]``` - the IP address to bind to
+ on the container servers (defaults to 0.0.0.0 for all addresses)
+ * ```default[:swift][:network][:container-bind-port]``` - the port to bind to
+ on the container servers (defaults to 6002)
+ * ```default[:swift][:network][:object-bind-ip]``` - the IP address to bind to
+ on the object servers (defaults to 0.0.0.0 for all addresses)
+ * ```default[:swift][:network][:object-bind-port]``` - the port to bind to
+ on the container servers (defaults to 6002)
+ * ```default[:swift][:network][:object-cidr]``` - the CIDR network for your object
+ servers in order to build the ring (defaults to 10.0.0.0/24)
+
+Examples
+========
+
+Example environment
+-------------------
+
+```json
+{
+ "default_attributes": {
+ "swift": {
+ "swift_hash": "107c0568ea84",
+ "authmode": "swauth",
+ "authkey": "test"
+ "auto_rebuild_rings": false
+ "git_builder_ip": "10.0.0.10"
+ "swauth": {
+ "url": "http://10.0.0.10:8080/v1/"
+ }
+ },
+ },
+ "name": "swift",
+ "chef_type": "environment",
+ "json_class": "Chef::Environment"
+}
+```
+
+This sets up defaults for a swauth-based cluster with the storage
+network on 10.0.0.0/24.
+
+Example all-in-one
+--------------------------
+
+Example all-in-one storage node config (note there should only ever be
+one node with the swift-setup and swift-management roles)
+
+```json
+{
+ "id": "storage1",
+ "name": "storage1",
+ "json_class": "Chef::Node",
+ "run_list": [
+ "role[swift-setup]",
+ "role[swift-management-server]",
+ "role[swift-account-server]",
+ "role[swift-object-server]",
+ "role[swift-container-server]",
+ "role[swift-proxy-server]"
+ ],
+ "chef_environment": "development",
+ "normal": {
+ "swift": {
+ "zone": "1"
+ }
+ }
+}
+```
+
+Standalone Storage Server
+-------------------------
+
+```json
+{
+ "name": "swift-object-server",
+ "json_class": "Chef::Role",
+ "run_list": [
+ "recipe[swift::object-server]"
+ ],
+ "description": "A storage server role.",
+ "chef_type": "role"
+}
+```
+
+Standalone Proxy Server
+-----------------------
+
+```json
+ "run_list": [
+ "role[swift-proxy-server]"
+ ]
+```
+
Testing
-=====
+=======
This cookbook is using [ChefSpec](https://github.com/acrmp/chefspec) for testing. Run the following before commiting. It will run your tests, and check for lint errors.
$ ./run_tests.bash
+There is also a Vagrant test environment that you can launch in order to integration
+test this cookbook. See the tests/README.md file for more information on launching the environment.
+
+Testing
+=======
+
+ $ bundle install
+ $ bundle exec berks install
+ $ bundle exec strainer test
+
License and Author
==================
| | |
|:---------------------|:---------------------------------------------------|
-| **Author** | Matt Ray () |
+| **Authors** | Alan Meadows () |
+| | Oisin Feely () |
+| | Ron Pedde () |
+| | Will Kelly () |
| | |
-| **Copyright** | Copyright (c) 2013, Opscode, Inc. |
+| **Copyright** | Copyright (c) 2013, AT&T, Inc. |
+| | Copyright (c) 2012, Rackspace US, Inc. |
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -44,3 +267,4 @@ 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.
+
diff --git a/Strainerfile b/Strainerfile
new file mode 100644
index 0000000..f23cd14
--- /dev/null
+++ b/Strainerfile
@@ -0,0 +1,4 @@
+# Strainerfile
+knife test: bundle exec knife cookbook test $COOKBOOK
+foodcritic: bundle exec foodcritic -f any -t ~FC003 -t ~FC023 $SANDBOX/$COOKBOOK
+chefspec: bundle exec rspec $SANDBOX/$COOKBOOK
diff --git a/attributes/default.rb b/attributes/default.rb
new file mode 100644
index 0000000..57492d2
--- /dev/null
+++ b/attributes/default.rb
@@ -0,0 +1,194 @@
+#--------------------
+# node/ring settings
+#--------------------
+
+default["swift"]["state"] = {}
+default["swift"]["swift_hash"] = "107c0568ea84"
+default["swift"]["audit_hour"] = "5"
+default["swift"]["disk_enum_expr"] = "node[:block_device]"
+default["swift"]["auto_rebuild_rings"] = false
+default["swift"]["git_builder_ip"] = "127.0.0.1"
+
+# the release only has any effect on ubuntu, and must be
+# a valid release on http://ubuntu-cloud.archive.canonical.com/ubuntu
+default["swift"]["release"] = "folsom"
+
+# we support an optional secret databag where we will retrieve the
+# following attributes overriding any default attributes here
+#
+# {
+# "id": "swift_dal2",
+# "swift_hash": "107c0568ea84"
+# "swift_authkey": "keW4all"
+# "dispersion_auth_user": "test:test",
+# "dispersion_auth_key": "test"
+# }
+default["swift"]["swift_secret_databag_name"] = nil
+
+#--------------------
+# authentication
+#--------------------
+
+default["swift"]["authmode"] = "swauth"
+default["swift"]["authkey"] = "test"
+default["swift"]["swift_url"] = "http://127.0.0.1:8080/v1/"
+default["swift"]["swauth_url"] = "http://127.0.0.1:8080/v1/"
+default["swift"]["auth_url"] = "http://127.0.0.1:8080/auth/v1.0"
+
+#---------------------
+# dispersion settings
+#---------------------
+
+default["swift"]["dispersion"]["auth_user"] = "test:test"
+default["swift"]["dispersion"]["auth_key"] = "test"
+
+
+# settings for the swift ring - these default settings are
+# a safe setting for testing but part_power should be set to
+# 26 in production to allow a swift cluster with 50,000 spindles
+default["swift"]["ring"]["part_power"] = 18
+default["swift"]["ring"]["min_part_hours"] = 1
+default["swift"]["ring"]["replicas"] = 3
+
+#------------------
+# statistics
+#------------------
+default["swift"]["enable_statistics"] = true
+
+#------------------
+# network settings
+#------------------
+
+# the cidr configuration items are unimportant for a single server
+# configuration, but in a multi-server setup, the cidr should match
+# the interface appropriate to that service as they are used to
+# resolve the appropriate addresses to use for internode
+# communication
+
+# proxy servers
+default["swift"]["network"]["proxy-bind-ip"] = "0.0.0.0"
+default["swift"]["network"]["proxy-bind-port"] = "8080"
+default["swift"]["network"]["proxy-cidr"] = "10.0.0.0/24"
+
+# account servers
+default["swift"]["network"]["account-bind-ip"] = "0.0.0.0"
+default["swift"]["network"]["account-bind-port"] = "6002"
+
+# container servers
+default["swift"]["network"]["container-bind-ip"] = "0.0.0.0"
+default["swift"]["network"]["container-bind-port"] = "6001"
+
+# object servers
+default["swift"]["network"]["object-bind-ip"] = "0.0.0.0"
+default["swift"]["network"]["object-bind-port"] = "6000"
+default["swift"]["network"]["object-cidr"] = "10.0.0.0/24"
+
+#------------------
+# sysctl
+#------------------
+
+# set sysctl properties for time waits
+default['sysctl']['params']['net']['ipv4']['tcp_tw_recycle'] = 1
+default['sysctl']['params']['net']['ipv4']['tcp_tw_reuse'] = 1
+default['sysctl']['params']['net']['ipv4']['tcp_syncookies'] = 0
+
+# N.B. conntrack_max may also need to be adjusted if
+# server is running a stateful firewall
+
+#------------------
+# disk search
+#------------------
+
+# disk_test_filter is an array of predicates to test against disks to
+# determine if a disk should be formatted and configured for swift.
+# Each predicate is evaluated in turn, and a false from the predicate
+# will result in the disk not being considered as a candidate for
+# formatting.
+default["swift"]["disk_test_filter"] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/",
+ "File.exist?('/dev/' + candidate)",
+ "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')",
+ "not info.has_key?('removable') or info['removable'] == 0.to_s" ]
+
+#------------------
+# packages
+#------------------
+
+
+# Leveling between distros
+case platform
+when "redhat"
+ default["swift"]["platform"] = {
+ "disk_format" => "ext4",
+ "proxy_packages" => ["openstack-swift-proxy", "sudo", "cronie", "python-memcached"],
+ "object_packages" => ["openstack-swift-object", "sudo", "cronie"],
+ "container_packages" => ["openstack-swift-container", "sudo", "cronie"],
+ "account_packages" => ["openstack-swift-account", "sudo", "cronie"],
+ "swift_packages" => ["openstack-swift", "sudo", "cronie"],
+ "swauth_packages" => ["openstack-swauth", "sudo", "cronie"],
+ "rsync_packages" => ["rsync"],
+ "git_packages" => ["xinetd", "git", "git-daemon"],
+ "service_prefix" => "openstack-",
+ "service_suffix" => "",
+ "git_dir" => "/var/lib/git",
+ "git_service" => "git",
+ "service_provider" => Chef::Provider::Service::Redhat,
+ "override_options" => ""
+ }
+#
+# python-iso8601 is a missing dependency for swift.
+# https://bugzilla.redhat.com/show_bug.cgi?id=875948
+when "centos"
+ default["swift"]["platform"] = {
+ "disk_format" => "xfs",
+ "proxy_packages" => ["openstack-swift-proxy", "sudo", "cronie", "python-iso8601", "python-memcached" ],
+ "object_packages" => ["openstack-swift-object", "sudo", "cronie", "python-iso8601" ],
+ "container_packages" => ["openstack-swift-container", "sudo", "cronie", "python-iso8601" ],
+ "account_packages" => ["openstack-swift-account", "sudo", "cronie", "python-iso8601" ],
+ "swift_packages" => ["openstack-swift", "sudo", "cronie", "python-iso8601" ],
+ "swauth_packages" => ["openstack-swauth", "sudo", "cronie", "python-iso8601" ],
+ "rsync_packages" => ["rsync"],
+ "git_packages" => ["xinetd", "git", "git-daemon"],
+ "service_prefix" => "openstack-",
+ "service_suffix" => "",
+ "git_dir" => "/var/lib/git",
+ "git_service" => "git",
+ "service_provider" => Chef::Provider::Service::Redhat,
+ "override_options" => ""
+ }
+when "fedora"
+ default["swift"]["platform"] = {
+ "disk_format" => "xfs",
+ "proxy_packages" => ["openstack-swift-proxy", "python-memcached"],
+ "object_packages" => ["openstack-swift-object"],
+ "container_packages" => ["openstack-swift-container"],
+ "account_packages" => ["openstack-swift-account"],
+ "swift_packages" => ["openstack-swift"],
+ "swauth_packages" => ["openstack-swauth"],
+ "rsync_packages" => ["rsync"],
+ "git_packages" => ["git", "git-daemon"],
+ "service_prefix" => "openstack-",
+ "service_suffix" => ".service",
+ "git_dir" => "/var/lib/git",
+ "git_service" => "git",
+ "service_provider" => Chef::Provider::Service::Systemd,
+ "override_options" => ""
+ }
+when "ubuntu"
+ default["swift"]["platform"] = {
+ "disk_format" => "xfs",
+ "proxy_packages" => ["swift-proxy", "python-memcache"],
+ "object_packages" => ["swift-object"],
+ "container_packages" => ["swift-container"],
+ "account_packages" => ["swift-account", "python-swiftclient"],
+ "swift_packages" => ["swift"],
+ "swauth_packages" => ["swauth"],
+ "rsync_packages" => ["rsync"],
+ "git_packages" => ["git-daemon-sysvinit"],
+ "service_prefix" => "",
+ "service_suffix" => "",
+ "git_dir" => "/var/cache/git",
+ "git_service" => "git-daemon",
+ "service_provider" => Chef::Provider::Service::Upstart,
+ "override_options" => "-o Dpkg::Options:='--force-confold' -o Dpkg::Option:='--force-confdef'"
+ }
+end
diff --git a/files/default/5EDB1B62EC4926EA b/files/default/5EDB1B62EC4926EA
new file mode 100644
index 0000000..99f7cac
--- /dev/null
+++ b/files/default/5EDB1B62EC4926EA
@@ -0,0 +1,53 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+mQINBFAqSlgBEADPKwXUwqbgoDYgR20zFypxSZlSbrttOKVPEMb0HSUx9Wj8VvNC
+r+mT4E9wAyq7NTIs5ad2cUhXoyenrjcfGqK6k9R6yRHDbvAxCSWTnJjw7mzsajDN
+ocXC6THKVW8BSjrh0aOBLpht6d5QCO2vyWxw65FKM65GOsbX03ZngUPMuOuiOEHQ
+Zo97VSH2pSB+L+B3d9B0nw3QnU8qZMne+nVWYLYRXhCIxSv1/h39SXzHRgJoRUFH
+vL2aiiVrn88NjqfDW15HFhVJcGOFuACZnRA0/EqTq0qNo3GziQO4mxuZi3bTVL5s
+GABiYW9uIlokPqcS7Fa0FRVIU9R+bBdHZompcYnKAeGag+uRvuTqC3MMRcLUS9Oi
+/P9I8fPARXUPwzYN3fagCGB8ffYVqMunnFs0L6td08BgvWwer+Buu4fPGsQ5OzMc
+lgZ0TJmXyOlIW49lc1UXnORp4sm7HS6okA7P6URbqyGbaplSsNUVTgVbi+vc8/jY
+dfExt/3HxVqgrPlq9htqYgwhYvGIbBAxmeFQD8Ak/ShSiWb1FdQ+f7Lty+4mZLfN
+8x4zPZ//7fD5d/PETPh9P0msF+lLFlP564+1j75wx+skFO4v1gGlBcDaeipkFzeo
+zndAgpegydKSNTF4QK9iTYobTIwsYfGuS8rV21zE2saLM0CE3T90aHYB/wARAQAB
+tD1DYW5vbmljYWwgQ2xvdWQgQXJjaGl2ZSBTaWduaW5nIEtleSA8ZnRwbWFzdGVy
+QGNhbm9uaWNhbC5jb20+iQI3BBMBCAAhBQJQKkpYAhsDBQsJCAcDBRUKCQgLBRYC
+AwEAAh4BAheAAAoJEF7bG2LsSSbqKxkQAIKtgImrk02YCDldg6tLt3b69ZK0kIVI
+3Xso/zCBZbrYFmgGQEFHAa58mIgpv5GcgHHxWjpX3n4tu2RM9EneKvFjFBstTTgo
+yuCgFr7iblvs/aMW4jFJAiIbmjjXWVc0CVB/JlLqzBJ/MlHdR9OWmojN9ZzoIA+i
++tWlypgUot8iIxkR6JENxit5v9dN8i6anmnWybQ6PXFMuNi6GzQ0JgZIVs37n0ks
+2wh0N8hBjAKuUgqu4MPMwvNtz8FxEzyKwLNSMnjLAhzml/oje/Nj1GBB8roj5dmw
+7PSul5pAqQ5KTaXzl6gJN5vMEZzO4tEoGtRpA0/GTSXIlcx/SGkUK5+lqdQIMdyS
+n8bImU6V6rDSoOaI9YWHZtpv5WeUsNTdf68jZsFCRD+2+NEmIqBVm11yhmUoasC6
+dYw5l9P/PBdwmFm6NBUSEwxb+ROfpL1ICaZk9Jy++6akxhY//+cYEPLin02r43Z3
+o5Piqujrs1R2Hs7kX84gL5SlBzTM4Ed+ob7KVtQHTefpbO35bQllkPNqfBsC8AIC
+8xvTP2S8FicYOPATEuiRWs7Kn31TWC2iwswRKEKVRmN0fdpu/UPdMikyoNu9szBZ
+RxvkRAezh3WheJ6MW6Fmg9d+uTFJohZt5qHdpxYa4beuN4me8LF0TYzgfEbFT6b9
+D6IyTFoT0LequQINBFAqSlgBEADmL3TEq5ejBYrA+64zo8FYvCF4gziPa5rCIJGZ
+/gZXQ7pm5zek/lOe9C80mhxNWeLmrWMkMOWKCeaDMFpMBOQhZZmRdakOnH/xxO5x
++fRdOOhy+5GTRJiwkuGOV6rB9eYJ3UN9caP2hfipCMpJjlg3j/GwktjhuqcBHXhA
+HMhzxEOIDE5hmpDqZ051f8LGXld9aSL8RctoYFM8sgafPVmICTCq0Wh03dr5c2JA
+gEXy3ushYm/8i2WFmyldo7vbtTfx3DpmJc/EMpGKV+GxcI3/ERqSkde0kWlmfPZb
+o/5+hRqSryqfQtRKnFEQgAqAhPIwXwOkjCpPnDNfrkvzVEtl2/BWP/1/SOqzXjk9
+TIb1Q7MHANeFMrTCprzPLX6IdC4zLp+LpV91W2zygQJzPgWqH/Z/WFH4gXcBBqmI
+8bFpMPONYc9/67AWUABo2VOCojgtQmjxuFn+uGNw9PvxJAF3yjl781PVLUw3n66d
+wHRmYj4hqxNDLywhhnL/CC7KUDtBnUU/CKn/0Xgm9oz3thuxG6i3F3pQgpp7MeMn
+tKhLFWRXo9Bie8z/c0NV4K5HcpbGa8QPqoDseB5WaO4yGIBOt+nizM4DLrI+v07y
+Xe3Jm7zBSpYSrGarZGK68qamS3XPzMshPdoXXz33bkQrTPpivGYQVRZuzd/R6b+6
+IurV+QARAQABiQIfBBgBCAAJBQJQKkpYAhsMAAoJEF7bG2LsSSbq59EP/1U3815/
+yHV3cf/JeHgh6WS/Oy2kRHp/kJt3ev/l/qIxfMIpyM3u/D6siORPTUXHPm3AaZrb
+w0EDWByA3jHQEzlLIbsDGZgrnl+mxFuHwC1yEuW3xrzgjtGZCJureZ/BD6xfRuRc
+mvnetAZv/z98VN/oj3rvYhUi71NApqSvMExpNBGrdO6gQlI5azhOu8xGNy4OSke8
+J6pAsMUXIcEwjVEIvewJuqBW/3rj3Hh14tmWjQ7shNnYBuSJwbLeUW2e8bURnfXE
+TxrCmXzDmQldD5GQWCcD5WDosk/HVHBmHlqrqy0VO2nE3c73dQlNcI4jVWeC4b4Q
+SpYVsFz/6Iqy5ZQkCOpQ57MCf0B6P5nF92c5f3TYPMxHf0x3DrjDbUVZytxDiZZa
+XsbZzsejbbc1bSNp4hb+IWhmWoFnq/hNHXzKPHBTapObnQju+9zUlQngV0BlPT62
+hOHOw3Pv7suOuzzfuOO7qpz0uAy8cFKe7kBtLSFVjBwaG5JX89mgttYW+lw9Rmsb
+p9Iw4KKFHIBLOwk7s+u0LUhP3d8neBI6NfkOYKZZCm3CuvkiOeQP9/2okFjtj+29
+jEL+9KQwrGNFEVNe85Un5MJfYIjgyqX3nJcwypYxidntnhMhr2VD3HL2R/4CiswB
+Oa4g9309p/+af/HU1smBrOfIeRoxb8jQoHu3
+=xg4S
+-----END PGP PUBLIC KEY BLOCK-----
+
diff --git a/files/default/cluster_stats.py b/files/default/cluster_stats.py
new file mode 100755
index 0000000..6f79362
--- /dev/null
+++ b/files/default/cluster_stats.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+
+import os
+import re
+import errno
+import subprocess
+
+USING_COLLECTD=0
+
+try:
+ import collectd
+ USING_COLLECTD=1
+except:
+ pass
+
+# ===============================================================================
+# [2012-06-19 21:37:04] Checking ring md5sum's on 3 hosts...
+# 3/3 hosts matched, 0 error[s] while checking hosts.
+# ===============================================================================
+def get_md5sums():
+ retval = 0
+ output = subprocess.Popen(['swift-recon', '--objmd5'], stdout=subprocess.PIPE).communicate()[0]
+ for line in output.split("\n"):
+ result = re.search("([0-9]+) error", line)
+ if result:
+ retval = result.group(1)
+ return retval
+
+
+# ===============================================================================
+# [2012-06-19 21:36:27] Checking replication times on 3 hosts...
+# [Replication Times] shortest: 0.00546943346659, longest: 0.00739345153173, avg: 0.00669538444943
+# ===============================================================================
+def get_replication_times():
+ retval = {}
+ output = subprocess.Popen(['swift-recon', '-r'], stdout=subprocess.PIPE).communicate()[0]
+ for line in output.split("\n"):
+ result = re.search("shortest: ([0-9\.]+), longest: ([0-9\.]+), avg: ([0-9\.]+)", line)
+ if result:
+ retval['shortest'] = float(result.group(1))
+ retval['longest'] = float(result.group(2))
+ retval['average'] = float(result.group(3))
+ return retval
+
+def get_all():
+ stats = {}
+ stats['md5sums'] = get_md5sums()
+ stats['replication_times'] = get_replication_times()
+ return stats
+
+def config_callback(conf):
+ pass
+
+def read_callback():
+ stats = get_all()
+
+ if not stats:
+ return
+
+ # blarg, this should be fixed
+ for key in stats.keys():
+ path = '%s' % key
+ value = stats[key]
+
+ if type(value) != type({}):
+ # must be an int
+ val = collectd.Values(plugin=path)
+ val.type = 'gauge'
+ val.values = [int(value)]
+ val.dispatch()
+ else:
+ # must be a hash
+ for subvalue in value.keys():
+ path = '%s.%s' % (key, subvalue)
+ val = collectd.Values(plugin=path)
+ val.type = 'gauge'
+ if type(value[subvalue]) == type("string"):
+ val.values = [int(value[subvalue])]
+ else:
+ val.values = value[subvalue]
+ val.dispatch()
+
+if not USING_COLLECTD:
+ stats = get_all()
+ print stats
+else:
+ collectd.register_config(config_callback)
+ collectd.register_read(read_callback)
diff --git a/files/default/git-daemon.default b/files/default/git-daemon.default
new file mode 100644
index 0000000..cb97a41
--- /dev/null
+++ b/files/default/git-daemon.default
@@ -0,0 +1,16 @@
+# Defaults for git-daemon initscript
+# sourced by /etc/init.d/git-daemon
+# installed at /etc/default/git-daemon by the maintainer scripts
+
+#
+# This is a POSIX shell fragment
+#
+
+GIT_DAEMON_ENABLE=true
+GIT_DAEMON_USER=gitdaemon
+GIT_DAEMON_DIRECTORY=/var/cache/git
+GIT_DAEMON_BASE_PATH=/var/cache/git
+
+
+# Additional options that are passed to the Daemon.
+GIT_DAEMON_OPTIONS=""
diff --git a/files/default/rsync.init b/files/default/rsync.init
new file mode 100644
index 0000000..6eb27b0
--- /dev/null
+++ b/files/default/rsync.init
@@ -0,0 +1,44 @@
+#! /bin/sh
+#
+# chkconfig: 2345 50 50
+# description: rsync service
+
+# source function library
+ . /etc/rc.d/init.d/functions
+
+PROG='/usr/bin/rsync'
+BASE=${0##*/}
+
+# Adapt the --config parameter to point to your rsync daemon configuration
+# The config file must contain following line:
+# pid file = /var/run/.pid
+# Where is the filename of the init script (= this file)
+OPTIONS="--daemon --config=/etc/rsyncd.conf"
+
+case "$1" in
+ start)
+ echo -n $"Starting $BASE: "
+ daemon --check $BASE $PROG $OPTIONS
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$BASE
+ echo
+ ;;
+ stop)
+ echo -n $"Shutting down $BASE: "
+ killproc $BASE
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$BASE
+ echo
+ ;;
+ restart|force-reload)
+ $0 stop
+ sleep 1
+ $0 start
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/files/default/rsync.service b/files/default/rsync.service
new file mode 100644
index 0000000..172ba5e
--- /dev/null
+++ b/files/default/rsync.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Rsync Server
+After=local-fs.target
+
+[Service]
+Type=forking
+ExecStart=/usr/bin/rsync --daemon
+PIDFile=/var/run/rsyncd.pid
+
+[Install]
+WantedBy=multi-user.target
diff --git a/files/default/swift_stats.py b/files/default/swift_stats.py
new file mode 100755
index 0000000..dab33b9
--- /dev/null
+++ b/files/default/swift_stats.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+import os
+import errno
+
+from resource import getpagesize
+
+USING_COLLECTD=0
+
+try:
+ import collectd
+ USING_COLLECTD=1
+except:
+ pass
+
+def get_unmounts(mountpath="/srv/node/"):
+ try:
+ candidates = [ x for x in os.listdir(mountpath) if os.path.isdir(mountpath + x) ]
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise
+ return 0
+
+ mounts = []
+ with open('/proc/mounts', 'r') as procmounts:
+ for line in procmounts:
+ _, mounted_path, _, _, _, _ = line.rstrip().split()
+ if mounted_path.startswith(mountpath):
+ mounts.append(mounted_path.split('/')[-1])
+
+ return len(set(candidates) - set(mounts))
+
+def get_sockstats():
+ sockstat = {}
+ try:
+ with open('/proc/net/sockstat') as proc_sockstat:
+ for entry in proc_sockstat:
+ if entry.startswith("TCP: inuse"):
+ tcpstats = entry.split()
+ sockstat['tcp_in_use'] = int(tcpstats[2])
+ sockstat['orphan'] = int(tcpstats[4])
+ sockstat['time_wait'] = int(tcpstats[6])
+ sockstat['tcp_mem_allocated_bytes'] = \
+ int(tcpstats[10]) * getpagesize()
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise
+ try:
+ with open('/proc/net/sockstat6') as proc_sockstat6:
+ for entry in proc_sockstat6:
+ if entry.startswith("TCP6: inuse"):
+ sockstat['tcp6_in_use'] = int(entry.split()[2])
+ except IOError as e:
+ if e.errno != errno.ENOENT:
+ raise
+ return sockstat
+
+def get_all():
+ stats = {}
+ stats['socket'] = get_sockstats()
+ stats['unmounts'] = get_unmounts()
+ return stats
+
+def config_callback(conf):
+ pass
+
+def read_callback():
+ stats = get_all()
+
+ if not stats:
+ return
+
+ # blarg, this should be fixed
+ for key in stats.keys():
+ path = "%s" % key
+ value = stats[key]
+
+ if type(value) != type({}):
+ # must be an int
+ val = collectd.Values(plugin=path)
+ val.type = 'gauge'
+ val.values = [int(value)]
+ val.dispatch()
+ else:
+ # must be a hash
+ for subvalue in value.keys():
+ path = '%s.%s' % (key, subvalue)
+ val = collectd.Values(plugin=path)
+ val.type = 'gauge'
+ val.values = [int(value[subvalue])]
+ val.dispatch()
+
+if not USING_COLLECTD:
+ stats = get_all()
+ print stats
+else:
+ collectd.register_config(config_callback)
+ collectd.register_read(read_callback)
diff --git a/libraries/drive_utils.rb b/libraries/drive_utils.rb
new file mode 100644
index 0000000..aef75d0
--- /dev/null
+++ b/libraries/drive_utils.rb
@@ -0,0 +1,34 @@
+#
+# Cookbook Name:: swift
+# Library:: drive_utils
+#
+# Copyright 2012, Rackspace US, 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.
+#
+# Author: Ron Pedde
+#
+
+module DriveUtils
+ def locate_disks(enum_expression, filter_expressions)
+ candidate_disks = eval(enum_expression)
+ candidate_expression = "candidate_disks.select{|candidate,info| (" +
+ filter_expressions.map{|x| "(#{x})"}.join(" and ") + ")}"
+ # TODO(mancdaz): fix this properly so the above works in the first place
+ candidate_expression.gsub!(/\[\'removable\'\] = 0/, "['removable'].to_i == 0")
+ drives = Hash[eval(candidate_expression)]
+ Chef::Log.info("Using candidate drives: #{drives.keys.join(", ")}")
+ drives.keys
+ end
+end
+
diff --git a/libraries/ip_utils.rb b/libraries/ip_utils.rb
new file mode 100644
index 0000000..35c9710
--- /dev/null
+++ b/libraries/ip_utils.rb
@@ -0,0 +1,45 @@
+#
+# Cookbook Name:: swift
+# Library:: ip_utils
+#
+# Copyright 2013, ATT 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.
+#
+# Author: Alan Meadows
+#
+
+require "ipaddr"
+
+module IPUtils
+ def locate_ip_in_cidr(network, node)
+ Chef::Log.debug("Searching for ip within #{network} on node #{node.name}")
+ net = IPAddr.new(network)
+ node["network"]["interfaces"].each do |interface|
+ if interface[1].has_key?("addresses") then
+ interface[1]["addresses"].each do |k,v|
+ if v["family"] == "inet6" or (v["family"] == "inet" and v["prefixlen"] != "32") then
+ addr=IPAddr.new(k)
+ if net.include?(addr) then
+ return k
+ end
+ end
+ end
+ end
+ end
+
+ error = "Can't find address within network #{network} for node #{node.name}"
+ Chef::Log.error(error)
+ raise error
+ end
+end
diff --git a/metadata.rb b/metadata.rb
new file mode 100644
index 0000000..e1766da
--- /dev/null
+++ b/metadata.rb
@@ -0,0 +1,19 @@
+name "swift"
+maintainer "ATT, Inc."
+license "Apache 2.0"
+description "Installs and configures Openstack Swift"
+long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
+version "1.0.16"
+recipe "swift::account-server", "Installs the swift account server"
+recipe "swift::object-server", "Installs the swift object server"
+recipe "swift::proxy-server", "Installs the swift proxy server"
+recipe "swift::container-server", "Installs the swift container server"
+
+%w{ centos ubuntu }.each do |os|
+ supports os
+end
+
+depends "memcached"
+depends "sysctl"
+depends "statsd"
+depends "apt"
diff --git a/providers/disk.rb b/providers/disk.rb
new file mode 100644
index 0000000..b4c8433
--- /dev/null
+++ b/providers/disk.rb
@@ -0,0 +1,264 @@
+#
+# Copyright 2011, Dell
+#
+# 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.
+#
+# Author: andi abes
+#
+
+require 'chef/mixin/shell_out'
+include Chef::Mixin::ShellOut
+
+def load_current_resource
+ dev_name = @new_resource.name
+ @current = Chef::Resource::SwiftDisk.new(dev_name)
+
+ parted_partition_parse dev_name
+ parts = @current.part()
+
+ if not @current.blocks
+ # parted didn't return anything -- empty disk.
+ # get size from sfdisk
+ sfdisk_get_size(dev_name)
+ end
+
+ Chef::Log.info("About to print partition table")
+
+ s = < part_num,
+ :start => Regexp.last_match(2).to_i / 1024,
+ :end => Regexp.last_match(3).to_i / 1024,
+ :size => Regexp.last_match(4).to_i / 1024,
+ :type => Regexp.last_match(5),
+ :system => Regexp.last_match(6),
+ :flags => Regexp.last_match(7) }
+ part_tab << part_info
+ }
+ end
+
+ @current.part(part_tab)
+ part_tab
+end
+
+action :list do
+ Chef::Log.info("at some point there'll be a list")
+end
+
+####
+# compare the requested partition table parameters to what exists
+# if differences found - remove all current partitions, and create new ones.
+# An existing partition is considered a match if:
+# - it has the same serial # (1,2,3)
+# - it has the same size
+#
+# We also want to start to partition at 1M to be correctly aligned
+# even due to 4K sector size and controller stripe sizes.
+#
+# Plus, then parted doesn't bitch every time you run it.
+
+action :ensure_exists do
+ Chef::Log.info("Entering :ensure_exists")
+
+ req = @new_resource.part
+ cur = @current.part
+ dev_name = @new_resource.name
+ update = false
+
+ recreate, delete_existing = false
+
+ disk_blocks = @current.blocks #1k blocks
+
+ if (cur.nil?)
+ recreate = true;
+ else
+ idx = 0
+ current_block=0
+
+ Chef::Log.info("Checking partition #{idx}")
+
+ req.each { |params|
+ if (cur[idx].nil?)
+ recreate = true
+ Chef::Log.info("no current #{idx}")
+ next
+ end
+
+ req_size = params[:size] # size in Mb - convert to blocks
+ if (req_size == :remaining)
+ req_size = disk_blocks - current_block
+ else
+ req_size = req_size * 1024
+ end
+
+ cur_size = cur[idx][:size]
+
+ cur_min, cur_max = req_size*0.9, req_size*1.1
+ if !(cur_size > cur_min and cur_size < cur_max)
+ recreate = true
+ end
+
+ current_block += cur[idx][:size]
+ Chef::Log.info("partition #{idx} #{(recreate ? 'differs' : 'is same')}: #{cur_size}/#{req_size}")
+ idx+=1
+ }
+ end
+
+ if !recreate
+ Chef::Log.info("partition table matches - not recreating")
+ else
+ ### make sure to ensure that there are no mounted
+ ### filesystems on the device
+ re = /^(#{Regexp.escape(dev_name)}[0-9]+)/
+ mounted = []
+ shell_out!("mount").stdout.each_line { |line|
+ md = re.match(line)
+ next unless md
+ mounted << md[1]
+ }
+ mounted.each { |m|
+ Chef::Log.info("unmounting #{m}")
+ shell_out!("umount #{m}")
+ }
+
+ # Nuke current partition table.
+ execute "create new partition table" do
+ command "parted -s -m #{dev_name} mktable gpt"
+ end
+
+ # create new partitions
+ idx = 0
+ req.each { | params |
+ start_block = 0
+
+ if idx == 0
+ start_block = "1M"
+ end
+
+ if (params[:size] == :remaining)
+ requested_size = "100%"
+ else
+ requested_size = "#{params[:size]}M"
+ end
+
+ s = "parted -m -s #{dev_name} "
+ s << "mkpart #{idx} #{start_block} #{requested_size}" # #{params[:type]}
+ Chef::Log.info("creating new partition #{idx+1} with:" + s)
+ execute "creating partition #{idx}" do
+ command s
+ end
+ idx+=1
+
+ }
+ update = true
+ end
+
+ # walk through the partitions and enforce disk format
+ idx=1
+ req.each do |params|
+ device = "#{dev_name}#{idx}"
+ Chef::Log.info("Checking #{device}")
+
+ if ::File.exist?(device)
+ # FIXME: check the format on the file system. This should be
+ # handled by a disk format provider. Maybe the xfs/btrfs/etc
+ # providers?
+ Chef::Log.info("Testing file system on #{device} for type #{params[:type]}")
+
+ case params[:type]
+ when "xfs"
+ if not system("xfs_admin -l #{device}")
+ Mixlib::ShellOut.new("mkfs.xfs -f -i size=512 #{device}").run_command
+ update = true
+ end
+ when "ext4"
+ if not system("tune2fs -l #{device} | grep \"Filesystem volume name:\" | awk \'{print $4}\' | grep -v \"\"")
+ Mixlib::ShellOut.new("mkfs.ext4 #{device}").run_command
+ update = true
+ end
+ end
+ end
+ end
+ new_resource.updated_by_last_action(update)
+end
+
diff --git a/providers/mounts.rb b/providers/mounts.rb
new file mode 100644
index 0000000..bf2ed22
--- /dev/null
+++ b/providers/mounts.rb
@@ -0,0 +1,168 @@
+#
+# Cookbook Name:: swift
+# Provider:: mounts
+#
+# Copyright 2012, Rackspace US, 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.
+#
+# Author: Ron Pedde
+#
+
+require "chef/util/file_edit"
+
+action :ensure_exists do
+ proposed_devices = @new_resource.devices
+ path = @new_resource.name
+ dev_info = {}
+
+ Chef::Log.info("IN MOUNTS")
+
+ new_resource.updated_by_last_action(false)
+
+ # walk through the devices, gathering information
+ proposed_devices.each do |device|
+ next if !::File.exists?("/dev/#{device}")
+
+ info = {}
+ info["device"] = device
+ info["ip"] = @new_resource.ip
+ info["format"] = @new_resource.format
+ info["uuid"] = `blkid /dev/#{device} -s UUID -o value`.strip
+ info["mountpoint"] = info["uuid"].split("-").join("")
+ info["mounted"] = system("mount | grep '#{path}/#{info["mountpoint"]}\'")
+ info["size"] = `sfdisk -s /dev/#{device}`.to_i / 1024
+
+ next if (info["uuid"] == '')
+
+ dev_info[info["uuid"]] = info
+ end
+
+ Chef::Log.info("Physical Inventory:")
+ dev_info.each do |_,v|
+ Chef::Log.info("Device: #{v['device']}, UUID: #{v['uuid']}, Mounted: #{v['mounted']}, Format: #{v['format']}")
+ end
+
+ # make sure we have a "path"
+ Directory(path) do
+ group "swift"
+ owner "swift"
+ recursive true
+ end.run_action(:create)
+
+ # find what should be mounted, and what IS mounted
+ mounts=node["filesystem"].inject({}) { |hsh, (k,v)| hsh.merge(v["mount"] => k) }
+ valid_mounts = dev_info.inject([]) {|ary, (_,v)| ary << "#{path}/#{v['mountpoint']}"}
+ mountpoints = Dir.new(path).reject {|x| x[/^\./] }.collect { |d| "#{path}/#{d}" }
+ inverted_mounts = dev_info.inject({}) {|hsh,(k,v)| hsh.merge({v["mountpoint"] => v.merge("uuid" => k)})}
+ fstabs=::File.readlines("/etc/fstab").inject({}) do |hash,line|
+ line = line.split("#")[0].split()
+ Chef::Log.info("#{line[0]} ... #{line[1]}")
+ hash.merge(line[1] => line[0])
+ end.reject { |k,v| !k or !v or !k.length or !v.length }
+
+ Chef::Log.info("Mounts: #{mounts}")
+ Chef::Log.info("Valid Mounts: #{valid_mounts}")
+ Chef::Log.info("Mountpoints: #{mountpoints}")
+ Chef::Log.info("Fstabs: #{fstabs}")
+
+ # mounts in /srv/node that shouldn't be there
+ (mounts.keys.select{|x| x and x[/^#{path}/]} - valid_mounts).each do |dev|
+ Chef::Log.info("Unmounting #{dev}")
+ system("umount #{dev}") if system("mount | grep '#{dev}'")
+ new_resource.updated_by_last_action(true)
+ end
+
+ # fstab entries that don't need to be there anymore
+ (fstabs.keys.select {|k| k.start_with? path} - valid_mounts).each do |dev|
+ fe = Chef::Util::FileEdit.new("/etc/fstab")
+ fe.search_file_delete_line(Regexp.new(dev.gsub("/","\/")))
+ fe.write_file
+ new_resource.updated_by_last_action(true)
+ end
+
+ # directories/mountpoints in /srv/node that are now useless
+ (mountpoints - valid_mounts).each do |mountpoint|
+ Chef::Log.info("rmdiring #{mountpoint}")
+ begin
+ Dir.rmdir(mountpoint)
+ rescue SystemCallError
+ Chef::Log.info("Directory #{mountpoint} appears non-empty")
+ end
+ new_resource.updated_by_last_action(true)
+ end
+
+ # new, unmounted devices
+ (valid_mounts - mounts.keys).each do |mountpoint|
+ info = inverted_mounts[mountpoint.gsub("#{path}/","")]
+
+ Chef::Log.info("mounting #{mountpoint} (#{info['device']})")
+
+ mount_path = "#{path}/#{info['mountpoint']}"
+
+ Directory(mount_path) do
+ group "swift"
+ owner "swift"
+ recursive true
+ end.run_action(:create)
+
+ case info['format']
+ when 'ext4'
+ mount_options = "noatime,nodiratime,nobarrier,user_xattr"
+ when 'xfs'
+ case node["platform"]
+ when "ubuntu","debian"
+ mount_options = "noatime,nodiratime,nobarrier,logbufs=8,nobootwait"
+ else
+ mount_options = "noatime,nodiratime,nobarrier,logbufs=8"
+ end
+ end
+
+ mt = Mount(mount_path) do
+ device info['uuid']
+ device_type :uuid
+ options mount_options
+ dump 0
+ fstype info['format']
+ action :nothing
+ end
+
+ if not fstabs.has_key?(mount_path)
+ # then its a brand-new drive, freshly formatted
+ Chef::Log.info("Mounting new device #{info['mountpoint']}")
+ mt.run_action(:enable)
+ mt.run_action(:mount)
+ end
+
+ new_resource.updated_by_last_action(true)
+ end
+
+ dev_info.reject { |k,v| v["mounted"] }.keys.each do |uuid|
+ dev_info[uuid]["mounted"] = system("mount | grep '#{path}/#{dev_info[uuid]["mountpoint"]}\'")
+ end
+
+ if @new_resource.publish_attributes and dev_info != {}
+ dev_info.each do |k,v|
+ node.set["swift"]["state"]["devs"][k] = {
+ :device => v["device"],
+ :size => v["size"],
+ :uuid => v["uuid"],
+ :mounted => v["mounted"],
+ :format => v["format"],
+ :mountpoint => v["mountpoint"],
+ :ip => v["ip"]
+ }
+ end
+ end
+end
+
diff --git a/providers/ring_script.rb b/providers/ring_script.rb
new file mode 100644
index 0000000..7218a39
--- /dev/null
+++ b/providers/ring_script.rb
@@ -0,0 +1,262 @@
+#
+# Cookbook Name:: swift
+# Resource:: ring_script
+#
+# Copyright 2012, Rackspace US, 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.
+#
+# Author: Ron Pedde
+#
+
+require "pp"
+
+def generate_script
+ # need to load and parse the existing rings.
+ ports = { "object" => "6000", "container" => "6001", "account" => "6002" }
+ must_rebalance = false
+
+ ring_path = @new_resource.ring_path
+ ring_data = { :raw => {}, :parsed => {}, :in_use => {} }
+ disk_data = {}
+ dirty_cluster_reasons = []
+
+ [ "account", "container", "object" ].each do |which|
+ ring_data[:raw][which] = nil
+
+ if ::File.exist?("#{ring_path}/#{which}.builder")
+ IO.popen("su swift -c 'swift-ring-builder #{ring_path}/#{which}.builder'") do |pipe|
+ ring_data[:raw][which] = pipe.readlines
+ # Chef::Log.debug("#{ which.capitalize } Ring data: #{ring_data[:raw][which]}")
+ ring_data[:parsed][which] = parse_ring_output(ring_data[:raw][which])
+
+ node.set["swift"]["state"]["ring"][which] = ring_data[:parsed][which]
+ end
+ else
+ Chef::Log.info("#{which.capitalize} ring builder files do not exist!")
+ end
+
+ # collect all the ring data, and note what disks are in use. All I really
+ # need is a hash of device and id
+
+ ring_data[:in_use][which] ||= {}
+ if ring_data[:parsed][which][:hosts]
+ ring_data[:parsed][which][:hosts].each do |ip, dev|
+ dev.each do |dev_id, devhash|
+ ring_data[:in_use][which].store(devhash[:device], devhash[:id])
+ end
+ end
+ end
+
+ Chef::Log.debug("#{which.capitalize} Ring - In use: #{PP.pp(ring_data[:in_use][which],dump='')}")
+
+ # figure out what's present in the cluster
+ disk_data[which] = {}
+ disk_state,_,_ = Chef::Search::Query.new.search(:node,"chef_environment:#{node.chef_environment} AND roles:swift-#{which}-server")
+
+ # for a running track of available disks
+ disk_data[:available] ||= {}
+ disk_data[:available][which] ||= {}
+
+ disk_state.each do |swiftnode|
+ if swiftnode[:swift][:state] and swiftnode[:swift][:state][:devs]
+ swiftnode[:swift][:state][:devs].each do |k,v|
+ disk_data[which][v[:ip]] = disk_data[which][v[:ip]] || {}
+ disk_data[which][v[:ip]][k] = {}
+ v.keys.each { |x| disk_data[which][v[:ip]][k].store(x,v[x]) }
+
+ if swiftnode[:swift].has_key?("#{which}-zone")
+ disk_data[which][v[:ip]][k]["zone"]=swiftnode[:swift]["#{which}-zone"]
+ elsif swiftnode[:swift].has_key?("zone")
+ disk_data[which][v[:ip]][k]["zone"]=swiftnode[:swift]["zone"]
+ else
+ raise "Node #{swiftnode[:hostname]} has no zone assigned"
+ end
+
+ disk_data[:available][which][v[:mountpoint]] = v[:ip]
+
+ if not v[:mounted]
+ dirty_cluster_reasons << "Disk #{v[:name]} (#{v[:uuid]}) is not mounted on host #{v[:ip]} (#{swiftnode[:hostname]})"
+ end
+ end
+ end
+ end
+ Chef::Log.debug("#{which.capitalize} Ring - Avail: #{PP.pp(disk_data[:available][which],dump='')}")
+ end
+
+ # Have the raw data, now bump it together and drop the script
+
+ s = "#!/bin/bash\n\n# This script is automatically generated.\n"
+ s << "# Running it will likely blow up your system if you don't review it carefully.\n"
+ s << "# You have been warned.\n\n"
+ if not node["swift"]["auto_rebuild_rings"]
+ s << "if [ \"$1\" != \"--force\" ]; then\n"
+ s << " echo \"Auto rebuild rings is disabled, so you must use --force to generate rings\"\n"
+ s << " exit 0\n"
+ s << "fi\n\n"
+ end
+
+ # Chef::Log.debug("#{PP.pp(disk_data, dump='')}")
+
+ new_disks = {}
+ missing_disks = {}
+ new_servers = []
+
+ [ "account", "container", "object" ].each do |which|
+ # remove available disks that are already in the ring
+ new_disks[which] = disk_data[:available][which].reject{ |k,v| ring_data[:in_use][which].has_key?(k) }
+
+ # find all in-ring disks that are not in the cluster
+ missing_disks[which] = ring_data[:in_use][which].reject{ |k,v| disk_data[:available][which].has_key?(k) }
+
+ Chef::Log.debug("#{which.capitalize} Ring - Missing: #{PP.pp(missing_disks[which],dump='')}")
+ Chef::Log.debug("#{which.capitalize} Ring - New: #{PP.pp(new_disks[which],dump='')}")
+
+ s << "\n# -- #{which.capitalize} Servers --\n\n"
+ disk_data[which].keys.sort.each do |ip|
+ s << "# #{ip}\n"
+ disk_data[which][ip].keys.sort.each do |k|
+ v = disk_data[which][ip][k]
+ s << "# " + v.keys.sort.select{|x| ["ip", "device", "uuid"].include?(x)}.collect{|x| v[x] }.join(", ")
+ if new_disks[which].has_key?(v["mountpoint"])
+ s << " (NEW!)"
+ new_servers << ip unless new_servers.include?(ip)
+ end
+ s << "\n"
+ end
+ end
+
+ # for all those servers, check if they are already in the ring. If not,
+ # then we need to add them to the ring. For those that *were* in the
+ # ring, and are no longer in the ring, we need to delete those.
+
+ s << "\n"
+
+ # add the new disks
+ disk_data[which].keys.sort.each do |ip|
+ disk_data[which][ip].keys.sort.each do |uuid|
+ v = disk_data[which][ip][uuid]
+ if new_disks[which].has_key?(v['mountpoint'])
+ s << "swift-ring-builder #{ring_path}/#{which}.builder add z#{v['zone']}-#{v['ip']}:#{ports[which]}/#{v['mountpoint']} #{v['size']}\n"
+ must_rebalance = true
+ end
+ end
+ end
+
+ # remove the disks -- sort to ensure consistent order
+ missing_disks[which].keys.sort.each do |mountpoint|
+ diskinfo=ring_data[:parsed][which][:hosts].select{|k,v| v.has_key?(mountpoint)}.collect{|_,v| v[mountpoint]}[0]
+ Chef::Log.debug("Missing diskinfo: #{PP.pp(diskinfo,dump='')}")
+ description = Hash[diskinfo.select{|k,v| [:zone, :ip, :device].include?(k)}].collect{|k,v| "#{k}: #{v}" }.join(", ")
+ s << "# #{description}\n"
+ s << "swift-ring-builder #{ring_path}/#{which}.builder remove d#{missing_disks[which][mountpoint]}\n"
+ must_rebalance = true
+ end
+
+ s << "\n"
+
+ if(must_rebalance)
+ s << "swift-ring-builder #{ring_path}/#{which}.builder rebalance\n\n\n"
+ else
+ s << "# #{which.capitalize} ring has no outstanding changes!\n\n"
+ end
+
+ # we'll only rebalance if we meet the minimums for new adds
+ if node["swift"].has_key?("wait_for")
+ if node["swift"]["wait_for"] > new_servers.count
+ Chef::Log.debug("New servers, but not enough to force a rebalance")
+ must_rebalance = false
+ end
+ end
+ end
+ [ s, must_rebalance ]
+end
+
+# Parse the raw output of swift-ring-builder
+def parse_ring_output(ring_data)
+ output = { :state => {} }
+
+ ring_data.each do |line|
+ if line =~ /build version ([0-9]+)/
+ output[:state][:build_version] = $1
+ elsif line =~ /^Devices:\s+id\s+region\s+zone\s+/
+ next
+ elsif line =~ /^Devices:\s+id\s+zone\s+/
+ next
+ elsif line =~ /^\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+)\s+(\S+)\s+([0-9.]+)\s+(\d+)\s+([-0-9.]+)\s*$/
+ output[:hosts] ||= {}
+ output[:hosts][$3] ||= {}
+
+ output[:hosts][$3][$5] = {}
+
+ output[:hosts][$3][$5][:id] = $1
+ output[:hosts][$3][$5][:region] = $2
+ output[:hosts][$3][$5][:zone] = $3
+ output[:hosts][$3][$5][:ip] = $4
+ output[:hosts][$3][$5][:port] = $5
+ output[:hosts][$3][$5][:device] = $6
+ output[:hosts][$3][$5][:weight] = $7
+ output[:hosts][$3][$5][:partitions] = $8
+ output[:hosts][$3][$5][:balance] = $9
+ elsif line =~ /^\s+(\d+)\s+(\d+)\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+)\s+(\S+)\s+([0-9.]+)\s+(\d+)\s+([-0-9.]+)\s*$/
+ output[:hosts] ||= {}
+ output[:hosts][$3] ||= {}
+
+ output[:hosts][$3][$5] = {}
+
+ output[:hosts][$3][$5][:id] = $1
+ output[:hosts][$3][$5][:zone] = $2
+ output[:hosts][$3][$5][:ip] = $3
+ output[:hosts][$3][$5][:port] = $4
+ output[:hosts][$3][$5][:device] = $5
+ output[:hosts][$3][$5][:weight] = $6
+ output[:hosts][$3][$5][:partitions] = $7
+ output[:hosts][$3][$5][:balance] = $8
+ elsif line =~ /(\d+) partitions, (\d+\.\d+) replicas, (\d+) regions, (\d+) zones, (\d+) devices, (\d+\.\d+) balance$/
+ output[:state][:partitions] = $1
+ output[:state][:replicas] = $2
+ output[:state][:regions] = $3
+ output[:state][:zones] = $4
+ output[:state][:devices] = $5
+ output[:state][:balance] = $6
+ elsif line =~ /(\d+) partitions, (\d+) replicas, (\d+) zones, (\d+) devices, (\d+\.\d+) balance$/
+ output[:state][:partitions] = $1
+ output[:state][:replicas] = $2
+ output[:state][:zones] = $3
+ output[:state][:devices] = $4
+ output[:state][:balance] = $5
+ elsif line =~ /^The minimum number of hours before a partition can be reassigned is (\d+)$/
+ output[:state][:min_part_hours] = $1
+ else
+ raise "Cannot parse ring builder output for #{line}"
+ end
+ end
+
+ output
+end
+
+action :ensure_exists do
+ Chef::Log.debug("Ensuring #{new_resource.name}")
+ new_resource.updated_by_last_action(false)
+ s,must_update = generate_script
+
+ script_file = File new_resource.name do
+ owner new_resource.owner
+ group new_resource.group
+ mode new_resource.mode
+ content s
+ end
+
+ script_file.run_action(:create)
+ new_resource.updated_by_last_action(must_update)
+end
diff --git a/recipes/account-server.rb b/recipes/account-server.rb
new file mode 100644
index 0000000..53d85e5
--- /dev/null
+++ b/recipes/account-server.rb
@@ -0,0 +1,96 @@
+#
+# Cookbook Name:: swift
+# Recipe:: account-server
+#
+# Copyright 2012, Rackspace US, 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.
+#
+
+include_recipe "swift::common"
+include_recipe "swift::storage-common"
+include_recipe "swift::disks"
+
+platform_options = node["swift"]["platform"]
+
+platform_options["account_packages"].each.each do |pkg|
+ package pkg do
+ action :install
+ options platform_options["override_options"] # retain configs
+ end
+end
+
+# epel/f-17 missing init scripts for the non-major services.
+# https://bugzilla.redhat.com/show_bug.cgi?id=807170
+%w{auditor reaper replicator}.each do |svc|
+ template "/etc/systemd/system/openstack-swift-account-#{svc}.service" do
+ owner "root"
+ group "root"
+ mode "0644"
+ source "simple-systemd-config.erb"
+ variables({ :description => "OpenStack Object Storage (swift) - " +
+ "Account #{svc.capitalize}",
+ :user => "swift",
+ :exec => "/usr/bin/swift-account-#{svc} " +
+ "/etc/swift/account-server.conf"
+ })
+ only_if { platform?(%w{fedora}) }
+ end
+end
+
+# TODO(breu): track against upstream epel packages to determine if this
+# is still necessary
+# https://bugzilla.redhat.com/show_bug.cgi?id=807170
+%w{auditor reaper replicator}.each do |svc|
+ template "/etc/init.d/openstack-swift-account-#{svc}" do
+ owner "root"
+ group "root"
+ mode "0755"
+ source "simple-redhat-init-config.erb"
+ variables({ :description => "OpenStack Object Storage (swift) - " +
+ "Account #{svc.capitalize}",
+ :exec => "account-#{svc}"
+ })
+ only_if { platform?(%w{redhat centos}) }
+ end
+end
+
+%w{swift-account swift-account-auditor swift-account-reaper swift-account-replicator}.each do |svc|
+ service_name = platform_options["service_prefix"] + svc + platform_options["service_suffix"]
+ service svc do
+ service_name service_name
+ provider platform_options["service_provider"]
+ supports :status => true, :restart => true
+ action [:enable, :start]
+ only_if "[ -e /etc/swift/account-server.conf ] && [ -e /etc/swift/account.ring.gz ]"
+ end
+end
+
+# retrieve bind information from node
+bind_ip = node["swift"]["network"]["bind_ip"]
+bind_port = node["swift"]["network"]["bind_port"]
+
+# create account server template
+template "/etc/swift/account-server.conf" do
+ source "account-server.conf.erb"
+ owner "swift"
+ group "swift"
+ mode "0600"
+ variables("bind_ip" => node["swift"]["network"]["account-bind-ip"],
+ "bind_port" => node["swift"]["network"]["account-bind-port"])
+
+ notifies :restart, "service[swift-account]", :immediately
+ notifies :restart, "service[swift-account-auditor]", :immediately
+ notifies :restart, "service[swift-account-reaper]", :immediately
+ notifies :restart, "service[swift-account-replicator]", :immediately
+end
diff --git a/recipes/common.rb b/recipes/common.rb
new file mode 100644
index 0000000..74dcff0
--- /dev/null
+++ b/recipes/common.rb
@@ -0,0 +1,110 @@
+#
+# Cookbook Name:: swift
+# Recipe:: swift-common
+#
+# Copyright 2012, Rackspace US, 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::Recipe
+ include DriveUtils
+end
+
+include_recipe 'sysctl::default'
+
+# optionally statsd daemon for stats collection
+if node["swift"]["enable_statistics"]
+ include_recipe 'statsd::server'
+end
+
+platform_options = node["swift"]["platform"]
+
+# update repository if requested with the ubuntu cloud
+case node["platform"]
+when "ubuntu"
+
+ Chef::Log.info("Creating apt repository for http://ubuntu-cloud.archive.canonical.com/ubuntu")
+ Chef::Log.info("chefspec: #{node['lsb']['codename']}-updates/#{node['swift']['release']}")
+ apt_repository "ubuntu_cloud" do
+ uri "http://ubuntu-cloud.archive.canonical.com/ubuntu"
+ distribution "#{node['lsb']['codename']}-updates/#{node['swift']['release']}"
+ components ["main"]
+ key "5EDB1B62EC4926EA"
+ action :add
+ end
+end
+
+
+platform_options["swift_packages"].each do |pkg|
+ package pkg do
+ action :install
+ end
+end
+
+directory "/etc/swift" do
+ action :create
+ owner "swift"
+ group "swift"
+ mode "0700"
+ only_if "/usr/bin/id swift"
+end
+
+# determine hash
+if node['swift']['swift_secret_databag_name'].nil?
+ swifthash = node['swift']['swift_hash']
+else
+ swift_secrets = Chef::EncryptedDataBagItem.load "secrets", node['swift']['swift_secret_databag_name']
+ swifthash = swift_secrets['swift_hash']
+end
+
+
+file "/etc/swift/swift.conf" do
+ action :create
+ owner "swift"
+ group "swift"
+ mode "0700"
+ content "[swift-hash]\nswift_hash_path_suffix=#{swifthash}\n"
+ only_if "/usr/bin/id swift"
+end
+
+# need a swift user
+user "swift" do
+ shell "/bin/bash"
+ action :modify
+ only_if "/usr/bin/id swift"
+end
+
+package "git" do
+ action :install
+end
+
+# drop a ring puller script
+# TODO: make this smarter
+git_builder_ip = node["swift"]["git_builder_ip"]
+template "/etc/swift/pull-rings.sh" do
+ source "pull-rings.sh.erb"
+ owner "swift"
+ group "swift"
+ mode "0700"
+ variables({
+ :builder_ip => git_builder_ip,
+ :service_prefix => platform_options["service_prefix"]
+ })
+ only_if "/usr/bin/id swift"
+end
+
+execute "/etc/swift/pull-rings.sh" do
+ cwd "/etc/swift"
+ only_if "[ -x /etc/swift/pull-rings.sh ]"
+end
diff --git a/recipes/container-server.rb b/recipes/container-server.rb
new file mode 100644
index 0000000..bed6773
--- /dev/null
+++ b/recipes/container-server.rb
@@ -0,0 +1,93 @@
+#
+# Cookbook Name:: swift
+# Recipe:: swift-container-server
+#
+# Copyright 2012, Rackspace US, 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.
+#
+
+include_recipe "swift::common"
+include_recipe "swift::storage-common"
+include_recipe "swift::disks"
+
+
+platform_options = node["swift"]["platform"]
+
+platform_options["container_packages"].each do |pkg|
+ package pkg do
+ action :install
+ options platform_options["override_options"]
+ end
+end
+
+# epel/f-17 missing init scripts for the non-major services.
+# https://bugzilla.redhat.com/show_bug.cgi?id=807170
+%w{auditor updater replicator}.each do |svc|
+ template "/etc/systemd/system/openstack-swift-container-#{svc}.service" do
+ owner "root"
+ group "root"
+ mode "0644"
+ source "simple-systemd-config.erb"
+ variables({ :description => "OpenStack Object Storage (swift) - " +
+ "Container #{svc.capitalize}",
+ :user => "swift",
+ :exec => "/usr/bin/swift-container-#{svc} " +
+ "/etc/swift/container-server.conf"
+ })
+ only_if { platform?(%w{fedora}) }
+ end
+end
+
+# TODO(breu): track against upstream epel packages to determine if this
+# is still necessary
+# https://bugzilla.redhat.com/show_bug.cgi?id=807170
+%w{auditor updater replicator}.each do |svc|
+ template "/etc/init.d/openstack-swift-container-#{svc}" do
+ owner "root"
+ group "root"
+ mode "0755"
+ source "simple-redhat-init-config.erb"
+ variables({ :description => "OpenStack Object Storage (swift) - " +
+ "Container #{svc.capitalize}",
+ :exec => "container-#{svc}"
+ })
+ only_if { platform?(%w{redhat centos}) }
+ end
+end
+
+%w{swift-container swift-container-auditor swift-container-replicator swift-container-updater}.each do |svc|
+ service_name=platform_options["service_prefix"] + svc + platform_options["service_suffix"]
+
+ service svc do
+ service_name service_name
+ provider platform_options["service_provider"]
+ supports :status => true, :restart => true
+ action [:enable, :start]
+ only_if "[ -e /etc/swift/container-server.conf ] && [ -e /etc/swift/container.ring.gz ]"
+ end
+end
+
+template "/etc/swift/container-server.conf" do
+ source "container-server.conf.erb"
+ owner "swift"
+ group "swift"
+ mode "0600"
+ variables("bind_ip" => node["swift"]["network"]["container-bind-ip"],
+ "bind_port" => node["swift"]["network"]["container-bind-port"])
+
+ notifies :restart, "service[swift-container]", :immediately
+ notifies :restart, "service[swift-container-replicator]", :immediately
+ notifies :restart, "service[swift-container-updater]", :immediately
+ notifies :restart, "service[swift-container-auditor]", :immediately
+end
diff --git a/recipes/disks.rb b/recipes/disks.rb
new file mode 100644
index 0000000..a6ca5b9
--- /dev/null
+++ b/recipes/disks.rb
@@ -0,0 +1,66 @@
+#
+# Cookbook Name:: swift
+# Recipe:: disks
+#
+# Copyright 2012, Rackspace US, 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.
+#
+# Author: Ron Pedde
+# Inspired by: Andi Abes @ Dell
+
+class Chef::Recipe
+ include IPUtils
+end
+
+
+platform_options = node["swift"]["platform"]
+
+package "xfsprogs" do
+ action :install
+ only_if { platform?(%w{ubuntu debian fedora centos}) }
+end
+
+%w(parted util-linux).each do |pkg|
+ package pkg do
+ action :install
+ end
+end
+
+disk_enum_expr = node["swift"]["disk_enum_expr"]
+disk_test_filter = node["swift"]["disk_test_filter"]
+
+disks = locate_disks(disk_enum_expr, disk_test_filter)
+
+disks.each do |disk|
+ swift_disk "/dev/#{disk}" do
+ part [{:type => platform_options["disk_format"] , :size => :remaining}]
+ action :ensure_exists
+ end
+end
+
+# FIXME: "#{x}1" is only really valid for {v,s,h}dx. Doesn't
+# work for loop or probably for hp-style /dev/cciss/c0d0p1x0t0g0m1whatever
+#
+# additionally, there is an implicit assumption that bind ports
+# for all object/container/account services are on the same net
+disk_ip = locate_ip_in_cidr(node["swift"]["network"]["object-cidr"], node)
+
+swift_mounts "/srv/node" do
+ action :ensure_exists
+ publish_attributes "swift/state/devs"
+ devices disks.collect { |x| "#{x}1" }
+ ip disk_ip
+ format platform_options["disk_format"]
+end
+
diff --git a/recipes/management-server.rb b/recipes/management-server.rb
new file mode 100644
index 0000000..f55fd8f
--- /dev/null
+++ b/recipes/management-server.rb
@@ -0,0 +1,55 @@
+#
+# Cookbook Name:: swift
+# Recipe:: management-server
+#
+# Copyright 2012, Rackspace US, 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.
+#
+
+include_recipe "swift::common"
+
+# FIXME: This should probably be a role (ring-builder?), so you don't end up
+# with multiple repos!
+include_recipe "swift::ring-repo"
+
+platform_options = node["swift"]["platform"]
+
+if node["swift"]["authmode"] == "swauth"
+ platform_options["swauth_packages"].each.each do |pkg|
+ package pkg do
+ action :install
+ options platform_options["override_options"] # retain configs
+ end
+ end
+end
+
+# determine where to find dispersion login information
+if node['swift']['swift_secret_databag_name'].nil?
+ auth_user = node["swift"]["dispersion"]["auth_user"]
+ auth_key = node["swift"]["dispersion"]["auth_key"]
+else
+ swift_secrets = Chef::EncryptedDataBagItem.load "secrets", node['swift']['swift_secret_databag_name']
+ auth_user = swift_secrets['dispersion_auth_user']
+ auth_key = swift_secrets['dispersion_auth_key']
+end
+
+template "/etc/swift/dispersion.conf" do
+ source "dispersion.conf.erb"
+ owner "swift"
+ group "swift"
+ mode "0600"
+ variables("auth_url" => node["swift"]["auth_url"],
+ "auth_user" => auth_user,
+ "auth_key" => auth_key)
+end
diff --git a/recipes/memcached.rb b/recipes/memcached.rb
new file mode 100644
index 0000000..9cc533a
--- /dev/null
+++ b/recipes/memcached.rb
@@ -0,0 +1,20 @@
+#
+# Cookbook Name:: swift
+# Recipe:: memcached
+#
+# Copyright 2012, Rackspace US, 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.
+#
+
+include_recipe "memcached"
diff --git a/recipes/object-server.rb b/recipes/object-server.rb
new file mode 100644
index 0000000..9bd1ad5
--- /dev/null
+++ b/recipes/object-server.rb
@@ -0,0 +1,101 @@
+#
+# Cookbook Name:: swift
+# Recipe:: swift-object-server
+#
+# Copyright 2012, Rackspace US, 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.
+#
+
+include_recipe "swift::common"
+include_recipe "swift::storage-common"
+include_recipe "swift::disks"
+
+platform_options = node["swift"]["platform"]
+
+platform_options["object_packages"].each do |pkg|
+ package pkg do
+ action :install
+ options platform_options["override_options"] # retain configs
+ end
+end
+
+# epel/f-17 missing init scripts for the non-major services.
+# https://bugzilla.redhat.com/show_bug.cgi?id=807170
+%w{auditor updater replicator}.each do |svc|
+ template "/etc/systemd/system/openstack-swift-object-#{svc}.service" do
+ owner "root"
+ group "root"
+ mode "0644"
+ source "simple-systemd-config.erb"
+ variables({ :description => "OpenStack Object Storage (swift) - " +
+ "Object #{svc.capitalize}",
+ :user => "swift",
+ :exec => "/usr/bin/swift-object-#{svc} " +
+ "/etc/swift/object-server.conf"
+ })
+ only_if { platform?(%w{fedora})}
+ end
+end
+
+# TODO(breu): track against upstream epel packages to determine if this
+# is still necessary
+# https://bugzilla.redhat.com/show_bug.cgi?id=807170
+%w{auditor updater replicator}.each do |svc|
+ template "/etc/init.d/openstack-swift-object-#{svc}" do
+ owner "root"
+ group "root"
+ mode "0755"
+ source "simple-redhat-init-config.erb"
+ variables({ :description => "OpenStack Object Storage (swift) - " +
+ "Object #{svc.capitalize}",
+ :exec => "object-#{svc}"
+ })
+ only_if { platform?(%w{redhat centos}) }
+ end
+end
+
+%w{swift-object swift-object-replicator swift-object-auditor swift-object-updater}.each do |svc|
+ service_name=platform_options["service_prefix"] + svc + platform_options["service_suffix"]
+
+ service svc do
+ service_name service_name
+ provider platform_options["service_provider"]
+ # the default ubuntu provider uses invoke-rc.d, which apparently is
+ # status-illy broken in ubuntu
+ supports :status => false, :restart => true
+ action [:enable, :start]
+ only_if "[ -e /etc/swift/object-server.conf ] && [ -e /etc/swift/object.ring.gz ]"
+ end
+
+end
+
+template "/etc/swift/object-server.conf" do
+ source "object-server.conf.erb"
+ owner "swift"
+ group "swift"
+ mode "0600"
+ variables("bind_ip" => node["swift"]["network"]["object-bind-ip"],
+ "bind_port" => node["swift"]["network"]["object-bind-port"])
+
+ notifies :restart, "service[swift-object]", :immediately
+ notifies :restart, "service[swift-object-replicator]", :immediately
+ notifies :restart, "service[swift-object-updater]", :immediately
+ notifies :restart, "service[swift-object-auditor]", :immediately
+end
+
+cron "swift-recon" do
+ minute "*/5"
+ command "swift-recon-cron /etc/swift/object-server.conf"
+ user "swift"
+end
diff --git a/recipes/proxy-server.rb b/recipes/proxy-server.rb
new file mode 100644
index 0000000..088a680
--- /dev/null
+++ b/recipes/proxy-server.rb
@@ -0,0 +1,116 @@
+#
+# Cookbook Name:: swift
+# Recipe:: proxy-server
+#
+# Copyright 2012, Rackspace US, 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.
+
+include_recipe "swift::common"
+include_recipe "swift::memcached"
+
+class Chef::Recipe
+ include IPUtils
+end
+
+if node.run_list.expand(node.chef_environment).recipes.include?("swift::setup")
+ Chef::Log.info("I ran the swift::setup so I will use my own swift passwords")
+else
+ setup = search(:node, "chef_environment:#{node.chef_environment} AND roles:swift-setup")
+ if setup.length == 0
+ Chef::Application.fatal! "You must have run the swift::setup recipe (on this or another node) before running the swift::proxy recipe on this node"
+ elsif setup.length == 1
+ Chef::Log.info "Found swift::setup node: #{setup[0].name}"
+ node.set["swift"]["service_pass"] = setup[0]["swift"]["service_pass"]
+ elsif setup.length >1
+ Chef::Application.fatal! "You have multiple nodes in your environment that have run swift-setup, and that is not allowed"
+ end
+end
+
+platform_options = node["swift"]["platform"]
+
+# install platform-specific packages
+platform_options["proxy_packages"].each do |pkg|
+ package pkg do
+ action :install
+ options platform_options["override_options"]
+ end
+end
+
+package "python-swauth" do
+ action :install
+ only_if { node["swift"]["authmode"] == "swauth" }
+end
+
+package "python-swift-informant" do
+ action :install
+ only_if { node["swift"]["use_informant"] }
+end
+
+package "python-keystone" do
+ action :install
+ only_if { node["swift"]["authmode"] == "keystone" }
+end
+
+directory "/var/cache/swift" do
+ owner "swift"
+ group "swift"
+ mode 00700
+end
+
+swift_proxy_service = platform_options["service_prefix"] + "swift-proxy" + platform_options["service_suffix"]
+service "swift-proxy" do
+ # openstack-swift-proxy.service on fedora-17, swift-proxy on ubuntu
+ service_name swift_proxy_service
+ provider platform_options["service_provider"]
+ supports :status => true, :restart => true
+ action [ :enable, :start ]
+ only_if "[ -e /etc/swift/proxy-server.conf ] && [ -e /etc/swift/object.ring.gz ]"
+end
+
+# use localhost when using chef solo otherwise, include all memcache
+# servers from all known proxies
+if Chef::Config[:solo]
+ memcache_servers = [ "127.0.0.1:11211" ]
+else
+ memcache_servers = []
+ proxy_nodes = search(:node, "chef_environment:#{node.chef_environment} AND roles:swift-proxy-server")
+ proxy_nodes.each do |proxy|
+ proxy_ip = locate_ip_in_cidr(node["swift"]["network"]["proxy-cidr"], proxy)
+ next if not proxy_ip # skip nil ips so we dont break the config
+ server_str = "#{proxy_ip}:11211"
+ memcache_servers << server_str unless memcache_servers.include?(server_str)
+ end
+end
+
+# determine authkey to use
+if node['swift']['swift_secret_databag_name'].nil?
+ authkey = node['swift']['authkey']
+else
+ swift_secrets = Chef::EncryptedDataBagItem.load "secrets", node['swift']['swift_secret_databag_name']
+ authkey = swift_secrets['swift_authkey']
+end
+
+# create proxy config file
+template "/etc/swift/proxy-server.conf" do
+ source "proxy-server.conf.erb"
+ owner "swift"
+ group "swift"
+ mode "0600"
+ variables("authmode" => node["swift"]["authmode"],
+ "bind_host" => node["swift"]["network"]["proxy-bind-ip"],
+ "bind_port" => node["swift"]["network"]["proxy-bind-port"],
+ "authkey" => authkey,
+ "memcache_servers" => memcache_servers)
+ notifies :restart, "service[swift-proxy]", :immediately
+end
diff --git a/recipes/ring-repo.rb b/recipes/ring-repo.rb
new file mode 100644
index 0000000..54f0e48
--- /dev/null
+++ b/recipes/ring-repo.rb
@@ -0,0 +1,183 @@
+#
+# Cookbook Name:: swift
+# Recipe:: ring-repo
+#
+# Copyright 2012, Rackspace US, 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 recipe creates a git ring repository on the management node
+# for purposes of ring synchronization
+#
+
+platform_options = node["swift"]["platform"]
+ring_options = node["swift"]["ring"]
+
+platform_options["git_packages"].each do |pkg|
+ package pkg do
+ action :install
+ end
+end
+
+service "xinetd" do
+ supports :status => false, :restart => true
+ action [ :enable, :start ]
+ only_if { platform?(%w{centos redhat fedora}) }
+end
+
+execute "create empty git repo" do
+ cwd "/tmp"
+ umask 022
+ command "mkdir $$; cd $$; git init; echo \"backups\" \> .gitignore; git add .gitignore; git commit -m 'initial commit' --author='chef '; git push file:///#{platform_options["git_dir"]}/rings master"
+ user "swift"
+ action :nothing
+end
+
+directory "git-directory" do
+ path "#{platform_options["git_dir"]}/rings"
+ owner "swift"
+ group "swift"
+ mode "0755"
+ recursive true
+ action :create
+end
+
+execute "initialize git repo" do
+ cwd "#{platform_options["git_dir"]}/rings"
+ umask 022
+ user "swift"
+ command "git init --bare && touch git-daemon-export-ok"
+ creates "#{platform_options["git_dir"]}/rings/config"
+ action :run
+ notifies :run, "execute[create empty git repo]", :immediately
+end
+
+# epel/f-17 missing systemd-ified inits
+# https://bugzilla.redhat.com/show_bug.cgi?id=737183
+template "/etc/systemd/system/git.service" do
+ owner "root"
+ group "root"
+ mode "0644"
+ source "simple-systemd-config.erb"
+ variables({ :description => "Git daemon service",
+ :user => "nobody",
+ :exec => "/usr/libexec/git-core/git-daemon " +
+ "--base-path=/var/lib/git --export-all --user-path=public_git" +
+ "--syslog --verbose"
+ })
+ only_if { platform?(%w{fedora}) }
+end
+
+case node["platform"]
+when "centos","redhat","fedora"
+ service "git-daemon" do
+ service_name platform_options["git_service"]
+ action [ :enable ]
+ end
+when "ubuntu","debian"
+ service "git-daemon" do
+ service_name platform_options["git_service"]
+ action [ :enable, :start ]
+ end
+end
+
+cookbook_file "/etc/default/git-daemon" do
+ owner "root"
+ group "root"
+ mode "644"
+ source "git-daemon.default"
+ action :create
+ notifies :restart, "service[git-daemon]", :immediately
+ not_if { platform?(%w{fedora centos redhat}) }
+end
+
+directory "/etc/swift/ring-workspace" do
+ owner "swift"
+ group "swift"
+ mode "0755"
+ action :create
+end
+
+execute "checkout-rings" do
+ cwd "/etc/swift/ring-workspace"
+ command "git clone file://#{platform_options["git_dir"]}/rings"
+ user "swift"
+ creates "/etc/swift/ring-workspace/rings"
+end
+
+[ "account", "container", "object" ].each do |ring_type|
+
+ part_power = ring_options["part_power"]
+ min_part_hours = ring_options["min_part_hours"]
+ replicas = ring_options["replicas"]
+
+ Chef::Log.info("Building initial ring #{ring_type} using part_power=#{part_power}, " +
+ "min_part_hours=#{min_part_hours}, replicas=#{replicas}")
+ execute "add #{ring_type}.builder" do
+ cwd "/etc/swift/ring-workspace/rings"
+ command "git add #{ring_type}.builder && git commit -m 'initial ring builders' --author='chef '"
+ user "swift"
+ action :nothing
+ end
+
+ execute "create #{ring_type} builder" do
+ cwd "/etc/swift/ring-workspace/rings"
+ command "swift-ring-builder #{ring_type}.builder create #{part_power} #{replicas} #{min_part_hours}"
+ user "swift"
+ creates "/etc/swift/ring-workspace/rings/#{ring_type}.builder"
+ notifies :run, "execute[add #{ring_type}.builder]", :immediate
+ end
+end
+
+bash "rebuild-rings" do
+ action :nothing
+ cwd "/etc/swift/ring-workspace/rings"
+ user "swift"
+ code <<-EOF
+ set -x
+
+ # Should this be done?
+ git reset --hard
+ git clean -df
+
+ ../generate-rings.sh
+ for d in object account container; do swift-ring-builder ${d}.builder; done
+
+ add=0
+ if test -n "$(find . -maxdepth 1 -name '*gz' -print -quit)"
+ then
+ git add *builder *gz
+ add=1
+ else
+ git add *builder
+ add=1
+ fi
+ if [ $add -ne 0 ]
+ then
+ git commit -m "Autobuild of rings on $(date +%Y%m%d) by Chef" --author="chef "
+ git push
+ fi
+
+ EOF
+end
+
+swift_ring_script "/etc/swift/ring-workspace/generate-rings.sh" do
+ owner "swift"
+ group "swift"
+ mode "0700"
+ ring_path "/etc/swift/ring-workspace/rings"
+ action :ensure_exists
+ notifies :run, "bash[rebuild-rings]", :immediate
+end
+
diff --git a/recipes/rsync.rb b/recipes/rsync.rb
new file mode 100644
index 0000000..4f0fbf4
--- /dev/null
+++ b/recipes/rsync.rb
@@ -0,0 +1,85 @@
+#
+# Cookbook Name:: swift
+# Recipe:: rsync
+#
+# Copyright 2012, Rackspace US, 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.
+#
+
+platform_options = node["swift"]["platform"]
+
+platform_options["rsync_packages"].each do |pkg|
+ package pkg do
+ action :install
+ options platform_options["override_options"]
+ end
+end
+
+# epel/f-17 broken: https://bugzilla.redhat.com/show_bug.cgi?id=737710
+cookbook_file "/etc/systemd/system/rsync.service" do
+ owner "root"
+ group "root"
+ mode "0644"
+ source "rsync.service"
+ action :create
+ only_if { platform?(%w{fedora}) }
+end
+
+# rhel based systems install rsync and run it with rsync. We don't want to do that
+cookbook_file "/etc/init.d/rsyncd" do
+ owner "root"
+ group "root"
+ mode "0755"
+ source "rsync.init"
+ action :create
+ only_if { platform?(%w{centos redhat scientific}) }
+end
+
+# FIXME: chicken and egg
+case node["platform"]
+when "centos","redhat","fedora"
+ # enable rsyncd
+ rsync_servicename = "rsyncd"
+ service "rsyncd" do
+ supports :status => false, :restart => true, :start => true, :stop => true
+ action [ :enable, :start ]
+ only_if "[ -f /etc/rsyncd.conf ]"
+ end
+ # disable rsync (the one via xinetd)
+ service "rsync" do
+ supports :status => false, :restart => false, :start => false, :stop => false
+ action [ :disable ]
+ end
+when "ubuntu","debian"
+ rsync_servicename = "rsync"
+ service "rsync" do
+ supports :status => false, :restart => true
+ action [ :enable, :start ]
+ only_if "[ -f /etc/rsyncd.conf ]"
+ end
+end
+
+template "/etc/rsyncd.conf" do
+ source "rsyncd.conf.erb"
+ mode "0644"
+ notifies :restart, "service[#{rsync_servicename}]", :immediately
+end
+
+execute "enable rsync" do
+ command "sed -i 's/RSYNC_ENABLE=false/RSYNC_ENABLE=true/' /etc/default/rsync"
+ only_if "grep -q 'RSYNC_ENABLE=false' /etc/default/rsync"
+ notifies :restart, "service[rsync]", :immediately
+ action :run
+ not_if { platform?(%w{fedora centos redhat scientific}) }
+end
diff --git a/recipes/setup.rb b/recipes/setup.rb
new file mode 100644
index 0000000..d515f31
--- /dev/null
+++ b/recipes/setup.rb
@@ -0,0 +1,58 @@
+#
+# Cookbook Name:: swift
+# Recipe:: setup
+#
+# Copyright 2012, Rackspace US, 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.
+
+include_recipe "swift::common"
+
+# make sure we die if there are multiple swift-setups
+if Chef::Config[:solo]
+ Chef::Application.fatal! "This recipe uses search. Chef Solo does not support search."
+else
+ setup_role_count = search(:node, "chef_environment:#{node.chef_environment} AND roles:swift-setup").length
+ if setup_role_count > 1
+ Chef::Application.fatal! "You can only have one node with the swift-setup role"
+ end
+end
+
+unless node["swift"]["service_pass"]
+ Chef::Log.info("Running swift setup - setting swift passwords")
+end
+
+platform_options = node["swift"]["platform"]
+
+# install platform-specific packages
+platform_options["proxy_packages"].each do |pkg|
+ package pkg do
+ action :upgrade
+ options platform_options["override_options"]
+ end
+end
+
+package "python-swauth" do
+ action :upgrade
+ only_if { node["swift"]["authmode"] == "swauth" }
+end
+
+package "python-swift-informant" do
+ action :upgrade
+ only_if { node["swift"]["use_informant"] }
+end
+
+package "python-keystone" do
+ action :upgrade
+ only_if { node["swift"]["authmode"] == "keystone" }
+end
diff --git a/recipes/storage-common.rb b/recipes/storage-common.rb
new file mode 100644
index 0000000..3aaf61f
--- /dev/null
+++ b/recipes/storage-common.rb
@@ -0,0 +1,41 @@
+#
+# Cookbook Name:: swift
+# Recipe:: storage-common
+#
+# Copyright 2012, Rackspace US, 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.
+#
+
+include_recipe "swift::rsync"
+
+template "/etc/swift/drive-audit.conf" do
+ source "drive-audit.conf.erb"
+ owner "swift"
+ group "swift"
+ mode "0600"
+end
+
+cron "drive-audit" do
+ hour node["swift"]["audit_hour"]
+ minute "10"
+ command "swift-drive-audit /etc/swift/drive-audit.conf"
+end
+
+directory "/var/cache/swift" do
+ group "swift"
+ owner "swift"
+ recursive true
+ action :create
+ mode 00700
+end
diff --git a/resources/disk.rb b/resources/disk.rb
new file mode 100644
index 0000000..6c4e2ed
--- /dev/null
+++ b/resources/disk.rb
@@ -0,0 +1,44 @@
+#
+# Copyright 2011, Dell
+#
+# 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.
+#
+# Author: andi abes
+#
+
+=begin
+ Ensure that a disk's partition table matches expectations.
+ Sample use:
+
+ swift_disk "/dev/sdb" do
+ part(
+ {[:type => "xfs", :size =>swift_disk::ONE_GIG*4 ],
+ [:type => "xfs", :size =>swift_disk::remaining})
+ action :ensure_exists
+ end
+
+=end
+
+actions :ensure_exists
+
+def initialize(*args)
+ super
+ @action = :ensure_exists
+end
+
+attribute :name, :kind_of => String
+attribute :size, :kind_of => Integer
+attribute :blocks, :kind_of => Integer
+attribute :device, :kind_of => String
+attribute :part, :kind_of => Array
+attribute :status, :kind_of => Symbol
diff --git a/resources/mounts.rb b/resources/mounts.rb
new file mode 100644
index 0000000..4c431a0
--- /dev/null
+++ b/resources/mounts.rb
@@ -0,0 +1,71 @@
+#
+# Cookbook Name:: swift
+# Resource:: mounts
+#
+# Copyright 2012, Rackspace US, 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.
+#
+# Author: Ron Pedde
+#
+
+=begin
+ Ensure that swift mounts are strongly enforced. This
+ will ensure specified drives are mounted, and unspecified
+ drives are not mounted. In addition, if there is a stale
+ mountpoint (from disk failure, maybe?), then that mountpoint
+ will try to be unmounted
+
+ Sample use:
+
+ swift_mounts "/srv/node" do
+ devices [ "sdb1", "sdc1" ]
+ action :ensure_exists
+ ip "10.1.1.1"
+ end
+
+ It will force mounts based on fs uuid (mangled to remove
+ dashes) and return a structure that describes the disks
+ mounted.
+
+ As this is expected to be consumed for the purposes of
+ swift, the ip address should be the address that gets
+ embedded into the ring (i.e. the listen port of the storage server)
+
+ Example return structure:
+
+ { "2a9452c5-d929-43d9-9631-4340ace45279": {
+ "device": "sdb1",
+ "ip": "10.1.1.1",
+ "mounted": "true",
+ "mountpoint": "2a9452c5d92943d996314340ace45279",
+ "size": 1022 (in 1k increments)
+ "uuid": "2a9452c5-d929-43d9-9631-4340ace45279"
+ },
+ ...
+ }
+
+=end
+
+actions :ensure_exists
+
+def initialize(*args)
+ super
+ @action = :ensure_exists
+end
+
+attribute :name, :kind_of => String
+attribute :devices, :kind_of => Array
+attribute :ip, :kind_of => String, :default => "127.0.0.1"
+attribute :publish_attributes, :kind_of => String, :default => nil
+attribute :format, :kind_of => String, :default => "xfs"
diff --git a/resources/ring_script.rb b/resources/ring_script.rb
new file mode 100644
index 0000000..5dd7839
--- /dev/null
+++ b/resources/ring_script.rb
@@ -0,0 +1,44 @@
+#
+# Copyright 2012, Rackspace US, 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.
+#
+# Author: Ron Pedde
+#
+
+=begin
+ Build a proposed ring-building script
+ Sample use:
+
+ swift_ring_script "/tmp/build-rings.sh" do
+ owner "root"
+ group "swift"
+ mode "0700"
+ ring_path "/etc/swift/ring-workspace"
+ action :ensure_exists
+ end
+
+=end
+
+actions :ensure_exists
+
+def initialize(*args)
+ super
+ @action = :ensure_exists
+end
+
+attribute :name, :kind_of => String
+attribute :owner, :kind_of => String, :default => "root"
+attribute :group, :kind_of => String, :default => "root"
+attribute :mode, :kind_of => String, :default => "0600"
+attribute :ring_path, :kind_of => String, :default => "/etc/swift"
diff --git a/run_tests.bash b/run_tests.bash
new file mode 100755
index 0000000..e00ccf8
--- /dev/null
+++ b/run_tests.bash
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+
+# A script to run tests locally before committing.
+
+set -e
+
+COOKBOOK=$(awk '/^name/ {print $NF}' metadata.rb |tr -d \"\')
+if [ -z $COOKBOOK ]; then
+ echo "Cookbook name not defined in metadata.rb"
+ exit 1
+fi
+
+BUNDLE_PATH=${BUNDLE_PATH:-.bundle}
+BERKSHELF_PATH=${BERKSHELF_PATH:-.cookbooks}
+
+echo "Using bundle path: $BUNDLE_PATH"
+echo "Using berkshelf path: $BERKSHELF_PATH"
+
+bundle install --path=${BUNDLE_PATH}
+bundle exec berks install --path=${BERKSHELF_PATH}
+bundle exec rspec ${BERKSHELF_PATH}/${COOKBOOK}
+bundle exec foodcritic -f any -t ~FC003 -t ~FC023 ${BERKSHELF_PATH}/${COOKBOOK}
+
diff --git a/spec/account_spec.rb b/spec/account_spec.rb
new file mode 100644
index 0000000..c8d9c30
--- /dev/null
+++ b/spec/account_spec.rb
@@ -0,0 +1,67 @@
+require 'spec_helper'
+
+describe 'swift::account-server' do
+
+ #-------------------
+ # UBUNTU
+ #-------------------
+
+ describe "ubuntu" do
+
+ before do
+ swift_stubs
+ @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS
+ @node = @chef_run.node
+ @node.set['lsb']['code'] = 'precise'
+ @node.set['swift']['authmode'] = 'swauth'
+ @node.set['swift']['network']['account-bind-ip'] = '10.0.0.1'
+ @node.set['swift']['network']['account-bind-port'] = '8080'
+ @node.set['swift']['disk_enum_expr'] = "[{ 'sda' => {}}]"
+ @node.set['swift']['disk_test_filter'] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/",
+ "File.exist?('/dev/' + candidate)",
+ "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')",
+ "not info.has_key?('removable') or info['removable'] == 0.to_s"]
+
+ # mock out an interface on the storage node
+ @node.set["network"] = MOCK_NODE_NETWORK_DATA['network']
+
+ @chef_run.converge "swift::account-server"
+ end
+
+ it "installs swift account packages" do
+ expect(@chef_run).to install_package "swift-account"
+ end
+
+ it "installs swiftclient package" do
+ expect(@chef_run).to install_package "python-swiftclient"
+ end
+
+ it "starts swift account services on boot" do
+ %w{swift-account swift-account-auditor swift-account-reaper swift-account-replicator}.each do |svc|
+ expect(@chef_run).to set_service_to_start_on_boot svc
+ end
+ end
+
+ describe "/etc/swift/account-server.conf" do
+
+ before do
+ @file = @chef_run.template "/etc/swift/account-server.conf"
+ end
+
+ it "has proper owner" do
+ expect(@file).to be_owned_by "swift", "swift"
+ end
+
+ it "has proper modes" do
+ expect(sprintf("%o", @file.mode)).to eq "600"
+ end
+
+ it "template contents" do
+ pending "TODO: implement"
+ end
+
+ end
+
+ end
+
+end
diff --git a/spec/common_spec.rb b/spec/common_spec.rb
new file mode 100644
index 0000000..8302af6
--- /dev/null
+++ b/spec/common_spec.rb
@@ -0,0 +1,92 @@
+require 'spec_helper'
+
+describe 'swift::common' do
+
+ #-------------------
+ # UBUNTU
+ #-------------------
+
+ describe "ubuntu" do
+
+ before do
+ swift_stubs
+ @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS
+ @node = @chef_run.node
+ @node.set['platform_family'] = "debian"
+ @node.set['lsb']['codename'] = "precise"
+ @node.set['swift']['release'] = "folsom"
+ @node.set['swift']['authmode'] = 'swauth'
+ @node.set['swift']['git_builder_ip'] = '10.0.0.10'
+
+ # TODO: this does not work
+ # ::Chef::Log.should_receive(:info).with("chefspec: precise-updates/folsom")
+
+ @chef_run.converge "swift::common"
+ end
+
+
+ it 'should set syctl paramaters' do
+ # N.B. we could examine chef log
+ pending "TODO: right now theres no way to do lwrp and test for this"
+ end
+
+ it 'installs git package for ring management' do
+ expect(@chef_run).to install_package "git"
+ end
+
+ describe "/etc/swift" do
+
+ before do
+ @file = @chef_run.directory "/etc/swift"
+ end
+
+ it "has proper owner" do
+ expect(@file).to be_owned_by "swift", "swift"
+ end
+
+ it "has proper modes" do
+ expect(sprintf("%o", @file.mode)).to eq "700"
+ end
+
+ end
+
+ describe "/etc/swift/swift.conf" do
+
+ before do
+ @file = @chef_run.file "/etc/swift/swift.conf"
+ end
+
+ it "has proper owner" do
+ expect(@file).to be_owned_by "swift", "swift"
+ end
+
+ it "has proper modes" do
+ expect(sprintf("%o", @file.mode)).to eq "700"
+ end
+
+ end
+
+ describe "/etc/swift/pull-rings.sh" do
+
+ before do
+ @file = @chef_run.template "/etc/swift/pull-rings.sh"
+ end
+
+ it "has proper owner" do
+ expect(@file).to be_owned_by "swift", "swift"
+ end
+
+ it "has proper modes" do
+ expect(sprintf("%o", @file.mode)).to eq "700"
+ end
+
+ it "template contents" do
+ pending "TODO: implement"
+ end
+
+ end
+
+ end
+
+
+end
diff --git a/spec/container_spec.rb b/spec/container_spec.rb
new file mode 100644
index 0000000..066e837
--- /dev/null
+++ b/spec/container_spec.rb
@@ -0,0 +1,63 @@
+require 'spec_helper'
+
+describe 'swift::container-server' do
+
+ #-------------------
+ # UBUNTU
+ #-------------------
+
+ describe "ubuntu" do
+
+ before do
+ swift_stubs
+ @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS
+ @node = @chef_run.node
+ @node.set['lsb']['code'] = 'precise'
+ @node.set['swift']['authmode'] = 'swauth'
+ @node.set['swift']['network']['container-bind-ip'] = '10.0.0.1'
+ @node.set['swift']['network']['container-bind-port'] = '8080'
+ @node.set['swift']['disk_enum_expr'] = "[{ 'sda' => {}}]"
+ @node.set['swift']['disk_test_filter'] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/",
+ "File.exist?('/dev/' + candidate)",
+ "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')",
+ "not info.has_key?('removable') or info['removable'] == 0.to_s"]
+
+ # mock out an interface on the storage node
+ @node.set["network"] = MOCK_NODE_NETWORK_DATA['network']
+
+ @chef_run.converge "swift::container-server"
+ end
+
+ it "installs swift container packages" do
+ expect(@chef_run).to install_package "swift-container"
+ end
+
+ it "starts swift container services on boot" do
+ %w{swift-container swift-container-auditor swift-container-replicator swift-container-updater}.each do |svc|
+ expect(@chef_run).to set_service_to_start_on_boot svc
+ end
+ end
+
+ describe "/etc/swift/container-server.conf" do
+
+ before do
+ @file = @chef_run.template "/etc/swift/container-server.conf"
+ end
+
+ it "has proper owner" do
+ expect(@file).to be_owned_by "swift", "swift"
+ end
+
+ it "has proper modes" do
+ expect(sprintf("%o", @file.mode)).to eq "600"
+ end
+
+ it "template contents" do
+ pending "TODO: implement"
+ end
+
+ end
+
+ end
+
+end
diff --git a/spec/disks_spec.rb b/spec/disks_spec.rb
new file mode 100644
index 0000000..8d82e96
--- /dev/null
+++ b/spec/disks_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe 'swift::disks' do
+
+ #-------------------
+ # UBUNTU
+ #-------------------
+
+ describe "ubuntu" do
+
+ before do
+ swift_stubs
+ @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS
+ @node = @chef_run.node
+ @node.set['platform_family'] = "debian"
+ @node.set['lsb']['codename'] = "precise"
+ @node.set['swift']['release'] = "folsom"
+ @node.set['swift']['authmode'] = 'swauth'
+ @node.set['swift']['git_builder_ip'] = '10.0.0.10'
+ @node.set['swift']['disk_enum_expr'] = "[{ 'sda' => {}}]"
+ @node.set['swift']['disk_test_filter'] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/",
+ "File.exist?('/dev/' + candidate)",
+ "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')",
+ "not info.has_key?('removable') or info['removable'] == 0.to_s"]
+
+ # mock out an interface on the storage node
+ @node.set["network"] = MOCK_NODE_NETWORK_DATA['network']
+
+ @chef_run.converge "swift::disks"
+ end
+
+ it 'installs xfs progs package' do
+ expect(@chef_run).to install_package "xfsprogs"
+ end
+
+ it 'installs parted package' do
+ expect(@chef_run).to install_package "parted"
+ end
+
+ end
+
+
+end
diff --git a/spec/management_spec.rb b/spec/management_spec.rb
new file mode 100644
index 0000000..6fc894f
--- /dev/null
+++ b/spec/management_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe 'swift::management-server' do
+
+ #-------------------
+ # UBUNTU
+ #-------------------
+
+ describe "ubuntu" do
+
+ before do
+ swift_stubs
+ @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS
+ @node = @chef_run.node
+ @node.set['lsb']['code'] = 'precise'
+ @node.set['swift']['authmode'] = 'swauth'
+
+ @chef_run.converge "swift::management-server"
+ end
+
+ it "installs swift swauth package" do
+ expect(@chef_run).to install_package "swauth"
+ end
+
+ describe "/etc/swift/dispersion.conf" do
+
+ before do
+ @file = @chef_run.template "/etc/swift/dispersion.conf"
+ end
+
+ it "has proper owner" do
+ expect(@file).to be_owned_by "swift", "swift"
+ end
+
+ it "has proper modes" do
+ expect(sprintf("%o", @file.mode)).to eq "600"
+ end
+
+ it "template contents" do
+ pending "TODO: implement"
+ end
+
+ end
+
+ end
+
+end
diff --git a/spec/object_spec.rb b/spec/object_spec.rb
new file mode 100644
index 0000000..2c343ab
--- /dev/null
+++ b/spec/object_spec.rb
@@ -0,0 +1,71 @@
+require 'spec_helper'
+
+describe 'swift::object-server' do
+
+ #-------------------
+ # UBUNTU
+ #-------------------
+
+ describe "ubuntu" do
+
+ before do
+ swift_stubs
+ @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS
+ @node = @chef_run.node
+ @node.set['lsb']['code'] = 'precise'
+ @node.set['swift']['authmode'] = 'swauth'
+ @node.set['swift']['network']['object-bind-ip'] = '10.0.0.1'
+ @node.set['swift']['network']['object-bind-port'] = '8080'
+ @node.set['swift']['disk_enum_expr'] = "[{ 'sda' => {}}]"
+ @node.set['swift']['disk_test_filter'] = [ "candidate =~ /sd[^a]/ or candidate =~ /hd[^a]/ or candidate =~ /vd[^a]/ or candidate =~ /xvd[^a]/",
+ "File.exist?('/dev/' + candidate)",
+ "not system('/sbin/parted /dev/' + candidate + ' -s print | grep linux-swap')",
+ "not info.has_key?('removable') or info['removable'] == 0.to_s"]
+
+ # mock out an interface on the storage node
+ @node.set["network"] = MOCK_NODE_NETWORK_DATA['network']
+
+ @chef_run.converge "swift::object-server"
+ end
+
+ it "installs swift packages" do
+ expect(@chef_run).to install_package "swift-object"
+ end
+
+ it "starts swift object services on boot" do
+ %w{swift-object swift-object-replicator swift-object-auditor swift-object-updater}.each do |svc|
+ expect(@chef_run).to set_service_to_start_on_boot svc
+ end
+ end
+
+ describe "/var/spool/crontab/root" do
+
+ it "template contents" do
+ pending "TODO: check for recon script"
+ end
+
+ end
+
+ describe "/etc/swift/object-server.conf" do
+
+ before do
+ @file = @chef_run.template "/etc/swift/object-server.conf"
+ end
+
+ it "has proper owner" do
+ expect(@file).to be_owned_by "swift", "swift"
+ end
+
+ it "has proper modes" do
+ expect(sprintf("%o", @file.mode)).to eq "600"
+ end
+
+ it "template contents" do
+ pending "TODO: implement"
+ end
+
+ end
+
+ end
+
+end
diff --git a/spec/proxy_spec.rb b/spec/proxy_spec.rb
new file mode 100644
index 0000000..8bbabeb
--- /dev/null
+++ b/spec/proxy_spec.rb
@@ -0,0 +1,60 @@
+require 'spec_helper'
+
+describe 'swift::proxy-server' do
+
+ #--------------
+ # UBUNTU
+ #--------------
+
+ describe "ubuntu" do
+
+ before do
+ swift_stubs
+ @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS
+ @node = @chef_run.node
+ @node.set['lsb']['code'] = 'precise'
+ @node.set['swift']['authmode'] = 'swauth'
+ @node.set['swift']['network']['proxy-bind-ip'] = '10.0.0.1'
+ @node.set['swift']['network']['proxy-bind-port'] = '8080'
+ @chef_run.converge "swift::proxy-server"
+ end
+
+ it "installs memcache python packages" do
+ expect(@chef_run).to install_package "python-memcache"
+ end
+
+ it "installs swift packages" do
+ expect(@chef_run).to install_package "swift-proxy"
+ end
+
+ it "installs swauth package if swauth is selected" do
+ expect(@chef_run).to install_package "python-swauth"
+ end
+
+ it "starts swift-proxy on boot" do
+ expect(@chef_run).to set_service_to_start_on_boot "swift-proxy"
+ end
+
+ describe "/etc/swift/proxy-server.conf" do
+
+ before do
+ @file = @chef_run.template "/etc/swift/proxy-server.conf"
+ end
+
+ it "has proper owner" do
+ expect(@file).to be_owned_by "swift", "swift"
+ end
+
+ it "has proper modes" do
+ expect(sprintf("%o", @file.mode)).to eq "600"
+ end
+
+ it "template contents" do
+ pending "TODO: implement"
+ end
+
+ end
+
+ end
+
+end
diff --git a/spec/ring-repo_spec.rb b/spec/ring-repo_spec.rb
new file mode 100644
index 0000000..6ba02be
--- /dev/null
+++ b/spec/ring-repo_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe 'swift::ring-repo' do
+
+ #-------------------
+ # UBUNTU
+ #-------------------
+
+ describe "ubuntu" do
+
+ before do
+ swift_stubs
+ @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS
+ @node = @chef_run.node
+ @node.set['platform_family'] = "debian"
+ @node.set['lsb']['codename'] = "precise"
+ @node.set['swift']['release'] = "folsom"
+ @node.set['swift']['authmode'] = 'swauth'
+ @node.set['swift']['git_builder_ip'] = '10.0.0.10'
+ @chef_run.converge "swift::ring-repo"
+ end
+
+ it 'installs git package for ring management' do
+ expect(@chef_run).to install_package "git-daemon-sysvinit"
+ end
+
+ it "starts xinetd services on boot" do
+ %w{xinetd}.each do |svc|
+ expect(@chef_run).to set_service_to_start_on_boot svc
+ end
+ end
+
+ describe "/etc/swift/ring-workspace/generate-rings.sh" do
+
+ it "gets installed" do
+ pending "TODO: determine some way to ensure this LWRP script gets created"
+ end
+
+ end
+
+ end
+
+end
diff --git a/spec/rsync_spec.rb b/spec/rsync_spec.rb
new file mode 100644
index 0000000..cebd664
--- /dev/null
+++ b/spec/rsync_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe 'swift::rsync' do
+
+ #-------------------
+ # UBUNTU
+ #-------------------
+
+ describe "ubuntu" do
+
+ before do
+ swift_stubs
+ @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS
+ @node = @chef_run.node
+ @node.set['platform_family'] = "debian"
+ @node.set['lsb']['codename'] = "precise"
+ @node.set['swift']['release'] = "folsom"
+ @node.set['swift']['authmode'] = 'swauth'
+ @node.set['swift']['git_builder_ip'] = '10.0.0.10'
+ @chef_run.converge "swift::rsync"
+ end
+
+ it 'installs git package for ring management' do
+ expect(@chef_run).to install_package "rsync"
+ end
+
+ it "starts rsync service on boot" do
+ %w{rsync}.each do |svc|
+ expect(@chef_run).to set_service_to_start_on_boot svc
+ end
+ end
+
+ describe "/etc/rsyncd.conf" do
+
+ before do
+ @file = @chef_run.template "/etc/rsyncd.conf"
+ end
+
+ it "has proper modes" do
+ expect(sprintf("%o", @file.mode)).to eq "644"
+ end
+
+ it "template contents" do
+ pending "TODO: implement"
+ end
+
+ end
+
+ end
+
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..a70cbcc
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,49 @@
+require "chefspec"
+
+::LOG_LEVEL = :fatal
+::REDHAT_OPTS = {
+ :platform => "redhat",
+ :log_level => ::LOG_LEVEL
+}
+::UBUNTU_OPTS = {
+ :platform => "ubuntu",
+ :version => "12.04",
+ :log_level => ::LOG_LEVEL
+}
+
+MOCK_NODE_NETWORK_DATA =
+ {
+ "ipaddress" => '10.0.0.2',
+ "fqdn" => 'localhost.localdomain',
+ "hostname" => 'localhost',
+ "network" => {
+ "default_interface" => "eth0",
+ "interfaces" => {
+ "eth0" => {
+ "addresses" => {
+ "fe80::a00:27ff:feca:ab08" => {"scope" => "Link", "prefixlen" => "64", "family" => "inet6"},
+ "10.0.0.2" => {"netmask" => "255.255.255.0", "broadcast" => "10.0.0.255", "family" => "inet"},
+ "08:00:27:CA:AB:08" => {"family" => "lladdr"}
+ },
+ },
+ "lo" => {
+ "addresses" => {
+ "::1" => {"scope" => "Node", "prefixlen" => "128", "family" => "inet6"},
+ "127.0.0.1" => {"netmask" => "255.0.0.0", "family" => "inet"}
+ },
+ },
+ },
+ }
+ }
+
+def swift_stubs
+ # create mock cluster
+ n = Chef::Node.new()
+ n.name('manager')
+ n.default_attrs = {
+ "swift" => {
+ "service_pass" => "foobar"
+ }
+ }
+ Chef::Recipe.any_instance.stub(:search).with(:node, 'chef_environment:_default AND roles:swift-setup').and_return([n])
+end
diff --git a/spec/storage-common_spec.rb b/spec/storage-common_spec.rb
new file mode 100644
index 0000000..dfdc081
--- /dev/null
+++ b/spec/storage-common_spec.rb
@@ -0,0 +1,58 @@
+require 'spec_helper'
+
+describe 'swift::storage-common' do
+
+ #-------------------
+ # UBUNTU
+ #-------------------
+
+ describe "ubuntu" do
+
+ before do
+ swift_stubs
+ @chef_run = ::ChefSpec::ChefRunner.new ::UBUNTU_OPTS
+ @node = @chef_run.node
+ @node.set['lsb']['code'] = 'precise'
+ @node.set['swift']['authmode'] = 'swauth'
+ @chef_run.converge "swift::storage-common"
+ end
+
+ describe "/var/cache/swift" do
+
+ before do
+ @file = @chef_run.directory "/var/cache/swift"
+ end
+
+ it "has proper owner" do
+ expect(@file).to be_owned_by "swift", "swift"
+ end
+
+ it "has proper modes" do
+ expect(sprintf("%o", @file.mode)).to eq "700"
+ end
+
+ end
+
+ describe "/etc/swift/drive-audit.conf" do
+
+ before do
+ @file = @chef_run.template "/etc/swift/drive-audit.conf"
+ end
+
+ it "has proper owner" do
+ expect(@file).to be_owned_by "swift", "swift"
+ end
+
+ it "has proper modes" do
+ expect(sprintf("%o", @file.mode)).to eq "600"
+ end
+
+ it "template contents" do
+ pending "TODO: implement"
+ end
+
+ end
+
+ end
+
+end
diff --git a/templates/default/account-server.conf.erb b/templates/default/account-server.conf.erb
new file mode 100644
index 0000000..6ba2498
--- /dev/null
+++ b/templates/default/account-server.conf.erb
@@ -0,0 +1,78 @@
+[DEFAULT]
+# bind_ip = 0.0.0.0
+# bind_port = 6002
+# backlog = 4096
+# workers = 1
+# user = swift
+# swift_dir = /etc/swift
+# devices = /srv/node
+# mount_check = true
+# You can specify default log routing here if you want:
+# log_name = swift
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+#####
+bind_ip = <%= @bind_ip %>
+bind_port = <%= @bind_port %>
+workers = 10
+<% if node[:swift][:enable_statistics] -%>
+log_statsd_host = localhost
+log_statsd_port = 8125
+log_statsd_default_sample_rate = 1
+log_statsd_metric_prefix = openstack.swift.<%= node[:hostname] %>
+<% end %>
+
+[pipeline:main]
+pipeline = account-server
+
+[app:account-server]
+use = egg:swift#account
+# You can override the default log routing for this app here:
+# set log_name = account-server
+# set log_facility = LOG_LOCAL0
+# set log_level = INFO
+# set log_requests = True
+
+[account-replicator]
+# You can override the default log routing for this app here (don't use set!):
+# log_name = account-replicator
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+# vm_test_mode = no
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+# per_diff = 1000
+# max_diffs = 100
+# concurrency = 8
+# interval = 30
+# How long without an error before a node's error count is reset. This will
+# also be how long before a node is reenabled after suppression is triggered.
+# error_suppression_interval = 60
+# How many errors can accumulate before a node is temporarily ignored.
+# error_suppression_limit = 10
+# node_timeout = 10
+# conn_timeout = 0.5
+# The replicator also performs reclamation
+# reclaim_age = 86400
+
+[account-auditor]
+# You can override the default log routing for this app here (don't use set!):
+# log_name = account-auditor
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+# Will audit, at most, 1 account per device per interval
+# interval = 1800
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+
+[account-reaper]
+# You can override the default log routing for this app here (don't use set!):
+# log_name = account-reaper
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+# concurrency = 25
+# interval = 3600
+# node_timeout = 10
+# conn_timeout = 0.5
+# log_facility = LOG_LOCAL0
+# log_level = INFO
diff --git a/templates/default/container-server.conf.erb b/templates/default/container-server.conf.erb
new file mode 100644
index 0000000..144214f
--- /dev/null
+++ b/templates/default/container-server.conf.erb
@@ -0,0 +1,88 @@
+[DEFAULT]
+# bind_ip = 0.0.0.0
+# bind_port = 6001
+# backlog = 4096
+# workers = 1
+# user = swift
+# swift_dir = /etc/swift
+# devices = /srv/node
+# mount_check = true
+# This is a comma separated list of hosts allowed in the X-Container-Sync-To
+# field for containers.
+# allowed_sync_hosts = 127.0.0.1
+# You can specify default log routing here if you want:
+# log_name = swift
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+####
+bind_ip = <%= @bind_ip %>
+bind_port = <%= @bind_port %>
+workers = 10
+<% if node[:swift][:enable_statistics] -%>
+log_statsd_host = localhost
+log_statsd_port = 8125
+log_statsd_default_sample_rate = 1
+log_statsd_metric_prefix = openstack.swift.<%= node[:hostname] %>
+<% end %>
+
+[pipeline:main]
+pipeline = container-server
+
+[app:container-server]
+use = egg:swift#container
+# You can override the default log routing for this app here:
+# set log_name = container-server
+# set log_facility = LOG_LOCAL0
+# set log_level = INFO
+# set log_requests = True
+# node_timeout = 3
+# conn_timeout = 0.5
+
+[container-replicator]
+# You can override the default log routing for this app here (don't use set!):
+# log_name = container-replicator
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+# vm_test_mode = no
+# per_diff = 1000
+# max_diffs = 100
+# concurrency = 8
+# interval = 30
+# node_timeout = 10
+# conn_timeout = 0.5
+# The replicator also performs reclamation
+# reclaim_age = 604800
+
+[container-updater]
+# You can override the default log routing for this app here (don't use set!):
+# log_name = container-updater
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+# interval = 300
+# concurrency = 4
+# node_timeout = 3
+# conn_timeout = 0.5
+# slowdown will sleep that amount between containers
+# slowdown = 0.01
+# Seconds to suppress updating an account that has generated an error
+# account_suppression_time = 60
+
+[container-auditor]
+# You can override the default log routing for this app here (don't use set!):
+# log_name = container-auditor
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+# Will audit, at most, 1 container per device per interval
+# interval = 1800
+
+[container-sync]
+# You can override the default log routing for this app here (don't use set!):
+# log_name = container-sync
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+# If you need to use an HTTP Proxy, set it here; defaults to no proxy.
+# sync_proxy = http://127.0.0.1:8888
+# Will sync, at most, each container once per interval
+# interval = 300
+# Maximum amount of time to spend syncing each container per pass
+# container_time = 60
diff --git a/templates/default/dispersion.conf.erb b/templates/default/dispersion.conf.erb
new file mode 100644
index 0000000..3a1cd87
--- /dev/null
+++ b/templates/default/dispersion.conf.erb
@@ -0,0 +1,13 @@
+[dispersion]
+auth_url = <%= @auth_url %>
+auth_user = <%= @auth_user %>
+auth_key = <%= @auth_key %>
+auth_version = 1.0
+endpoint_type = publicURL
+swift_dir = /etc/swift
+dispersion_coverage = 5
+retries = 5
+concurrency = 25
+container_report = yes
+object_report = yes
+dump_json = no
diff --git a/templates/default/drive-audit.conf.erb b/templates/default/drive-audit.conf.erb
new file mode 100644
index 0000000..ddde011
--- /dev/null
+++ b/templates/default/drive-audit.conf.erb
@@ -0,0 +1,5 @@
+[drive-audit]
+log_facility = LOG_LOCAL0
+log_level = INFO
+device_dir = /srv/node
+minutes = 60
diff --git a/templates/default/object-server.conf.erb b/templates/default/object-server.conf.erb
new file mode 100644
index 0000000..ea55dd8
--- /dev/null
+++ b/templates/default/object-server.conf.erb
@@ -0,0 +1,104 @@
+[DEFAULT]
+# bind_ip = 0.0.0.0
+# bind_port = 6000
+# backlog = 4096
+# workers = 1
+# user = swift
+# swift_dir = /etc/swift
+# devices = /srv/node
+# mount_check = true
+# expiring_objects_container_divisor = 86400
+# You can specify default log routing here if you want:
+# log_name = swift
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+#####
+bind_ip = <%= @bind_ip %>
+bind_port = <%= @bind_port %>
+workers = 10
+<% if node[:swift][:enable_statistics] -%>
+log_statsd_host = localhost
+log_statsd_port = 8125
+log_statsd_default_sample_rate = 1
+log_statsd_metric_prefix = openstack.swift.<%= node[:hostname] %>
+<% end %>
+
+[pipeline:main]
+pipeline = recon object-server
+
+[app:object-server]
+use = egg:swift#object
+# You can override the default log routing for this app here:
+# set log_name = object-server
+# set log_facility = LOG_LOCAL0
+# set log_level = INFO
+# set log_requests = True
+# node_timeout = 3
+# conn_timeout = 0.5
+# network_chunk_size = 65536
+# disk_chunk_size = 65536
+# max_upload_time = 86400
+# slow = 1
+# on PUTs, sync data every n MB
+# mb_per_sync = 512
+# Comma separated list of headers that can be set in metadata on an object.
+# This list is in addition to X-Object-Meta-* headers and cannot include
+# Content-Type, etag, Content-Length, or deleted
+# allowed_headers = Content-Disposition, Content-Encoding, X-Delete-At, X-Object-Manifest
+
+[filter:recon]
+use = egg:swift#recon
+recon_cache_path = /var/cache/swift
+
+[object-replicator]
+# You can override the default log routing for this app here (don't use set!):
+# log_name = object-replicator
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+# vm_test_mode = no
+# daemonize = on
+# run_pause = 30
+# concurrency = 1
+# stats_interval = 300
+# max duration of a partition rsync
+# rsync_timeout = 900
+# passed to rsync for io op timeout
+# rsync_io_timeout = 30
+# max duration of an http request
+# http_timeout = 60
+# attempts to kill all workers if nothing replicates for lockup_timeout seconds
+# lockup_timeout = 1800
+# The replicator also performs reclamation
+# reclaim_age = 604800
+# enable logging of replication stats for recon
+# recon_enable = no
+# recon_cache_path = /var/cache/swift
+#####
+recon_enable = yes
+recon_cache_path = /var/cache/swift
+concurrency = 2
+
+
+[object-updater]
+# You can override the default log routing for this app here (don't use set!):
+# log_name = object-updater
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+# interval = 300
+# concurrency = 1
+# node_timeout = 10
+# conn_timeout = 0.5
+# slowdown will sleep that amount between objects
+# slowdown = 0.01
+concurrency = 2
+
+[object-auditor]
+# You can override the default log routing for this app here (don't use set!):
+# log_name = object-auditor
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+# files_per_second = 20
+# bytes_per_second = 10000000
+# log_time = 3600
+# zero_byte_files_per_second = 50
+
diff --git a/templates/default/proxy-server.conf.erb b/templates/default/proxy-server.conf.erb
new file mode 100644
index 0000000..9576af4
--- /dev/null
+++ b/templates/default/proxy-server.conf.erb
@@ -0,0 +1,262 @@
+<%
+
+case @authmode
+when "keystone"
+ pipeline="authtoken keystoneauth"
+when "swauth"
+ pipeline="swauth"
+end
+
+account_management=false
+if node[:roles].include?("swift-management-server") and node[:swift][:authmode] == "swauth" then
+ account_management="true"
+end
+-%>
+# This file is managed by chef. Do not edit it.
+#
+# Cluster info:
+# Auth mode: <%= node[:swift][:authmode] %>
+# Management server: <%= node[:roles].include?("swift-management-server") %>
+# Account management enabled: <%= account_management %>
+# Auth pipeline: <%= pipeline %>
+
+[DEFAULT]
+# bind_ip = 0.0.0.0
+# bind_port = 8080
+# backlog = 4096
+# swift_dir = /etc/swift
+# workers = 1
+# user = swift
+# cert_file = /etc/swift/proxy.crt
+# key_file = /etc/swift/proxy.key
+# expiring_objects_container_divisor = 86400
+# You can specify default log routing here if you want:
+# log_name = swift
+# log_facility = LOG_LOCAL0
+# log_level = INFO
+######
+workers = <%= [ node[:cpu][:total] - 1, 1 ].max %>
+bind_ip = <%= @bind_host %>
+bind_port = <%= @bind_port %>
+<% if node[:swift][:enable_statistics] -%>
+log_statsd_host = localhost
+log_statsd_port = 8125
+log_statsd_default_sample_rate = 1
+log_statsd_metric_prefix = openstack.swift.<%= node[:hostname] %>
+<% end %>
+
+
+[pipeline:main]
+pipeline = catch_errors healthcheck cache ratelimit <%= pipeline %> proxy-logging proxy-server
+
+[app:proxy-server]
+use = egg:swift#proxy
+# You can override the default log routing for this app here:
+# set log_name = proxy-server
+# set log_facility = LOG_LOCAL0
+# set log_level = INFO
+# set access_log_name = proxy-server
+# set access_log_facility = LOG_LOCAL0
+# set access_log_level = INFO
+# set log_headers = False
+# recheck_account_existence = 60
+# recheck_container_existence = 60
+# object_chunk_size = 8192
+# client_chunk_size = 8192
+# node_timeout = 10
+# client_timeout = 60
+# conn_timeout = 0.5
+# How long without an error before a node's error count is reset. This will
+# also be how long before a node is reenabled after suppression is triggered.
+# error_suppression_interval = 60
+# How many errors can accumulate before a node is temporarily ignored.
+# error_suppression_limit = 10
+# If set to 'true' any authorized user may create and delete accounts; if
+# 'false' no one, even authorized, can.
+# allow_account_management = false
+# Set object_post_as_copy = false to turn on fast posts where only the metadata
+# changes are stored anew and the original data file is kept in place. This
+# makes for quicker posts; but since the container metadata isn't updated in
+# this mode, features like container sync won't be able to sync posts.
+# object_post_as_copy = true
+# If set to 'true' authorized accounts that do not yet exist within the Swift
+# cluster will be automatically created.
+# account_autocreate = false
+######
+#
+# N.B. ideally allow_account_management would only be set on the
+# management server, but swauth will delete using the cluster url
+# and not the local url
+# allow_account_managemnet = <%= account_management %>
+allow_account_management = true
+
+<% if @authmode == "keystone" -%>
+account_autocreate = true
+<% end %>
+
+<% if @authmode == "swauth" -%>
+[filter:swauth]
+use = egg:swauth#swauth
+# set log_name = swauth
+# super_admin_key =
+######
+<% if account_management -%>
+super_admin_key = <%= @authkey %>
+default_swift_cluster = local#<%= node[:swift][:swift_url] %>#<%= node[:swift][:swauth_url] %>
+<% else %>
+default_swift_cluster = local#<%= node[:swift][:swift_url] %>
+<% end %>
+<% end %>
+
+[filter:healthcheck]
+use = egg:swift#healthcheck
+# You can override the default log routing for this filter here:
+# set log_name = healthcheck
+# set log_facility = LOG_LOCAL0
+# set log_level = INFO
+# set log_headers = False
+
+[filter:cache]
+use = egg:swift#memcache
+# You can override the default log routing for this filter here:
+# set log_name = cache
+# set log_facility = LOG_LOCAL0
+# set log_level = INFO
+# set log_headers = False
+# Default for memcache_servers is to try to read the property from
+# memcache.conf (see memcache.conf-sample) or lacking that file, it will
+# default to the value below. You can specify multiple servers separated with
+# commas, as in: 10.1.2.3:11211,10.1.2.4:11211
+# memcache_servers = 127.0.0.1:11211
+#####
+memcache_servers = <%= @memcache_servers.join(",") %>
+
+[filter:ratelimit]
+use = egg:swift#ratelimit
+# You can override the default log routing for this filter here:
+# set log_name = ratelimit
+# set log_facility = LOG_LOCAL0
+# set log_level = INFO
+# set log_headers = False
+# clock_accuracy should represent how accurate the proxy servers' system clocks
+# are with each other. 1000 means that all the proxies' clock are accurate to
+# each other within 1 millisecond. No ratelimit should be higher than the
+# clock accuracy.
+# clock_accuracy = 1000
+# max_sleep_time_seconds = 60
+# log_sleep_time_seconds of 0 means disabled
+# log_sleep_time_seconds = 0
+# allows for slow rates (e.g. running up to 5 sec's behind) to catch up.
+# rate_buffer_seconds = 5
+# account_ratelimit of 0 means disabled
+# account_ratelimit = 0
+
+# these are comma separated lists of account names
+# account_whitelist = a,b
+# account_blacklist = c,d
+
+# with container_limit_x = r
+# for containers of size x limit requests per second to r. The container
+# rate will be linearly interpolated from the values given. With the values
+# below, a container of size 5 will get a rate of 75.
+# container_ratelimit_0 = 100
+# container_ratelimit_10 = 50
+# container_ratelimit_50 = 20
+
+[filter:domain_remap]
+use = egg:swift#domain_remap
+# You can override the default log routing for this filter here:
+# set log_name = domain_remap
+# set log_facility = LOG_LOCAL0
+# set log_level = INFO
+# set log_headers = False
+# storage_domain = example.com
+# path_root = v1
+# reseller_prefixes = AUTH
+
+[filter:catch_errors]
+use = egg:swift#catch_errors
+# You can override the default log routing for this filter here:
+# set log_name = catch_errors
+# set log_facility = LOG_LOCAL0
+# set log_level = INFO
+# set log_headers = False
+
+[filter:cname_lookup]
+# Note: this middleware requires python-dnspython
+use = egg:swift#cname_lookup
+# You can override the default log routing for this filter here:
+# set log_name = cname_lookup
+# set log_facility = LOG_LOCAL0
+# set log_level = INFO
+# set log_headers = False
+# storage_domain = example.com
+# lookup_depth = 1
+
+# Note: Put staticweb just after your auth filter(s) in the pipeline
+[filter:staticweb]
+use = egg:swift#staticweb
+# Seconds to cache container x-container-meta-web-* header values.
+# cache_timeout = 300
+# You can override the default log routing for this filter here:
+# set log_name = staticweb
+# set log_facility = LOG_LOCAL0
+# set log_level = INFO
+# set access_log_name = staticweb
+# set access_log_facility = LOG_LOCAL0
+# set access_log_level = INFO
+# set log_headers = False
+
+# Note: Put tempurl just before your auth filter(s) in the pipeline
+[filter:tempurl]
+use = egg:swift#tempurl
+#
+# The headers to remove from incoming requests. Simply a whitespace delimited
+# list of header names and names can optionally end with '*' to indicate a
+# prefix match. incoming_allow_headers is a list of exceptions to these
+# removals.
+# incoming_remove_headers = x-timestamp
+#
+# The headers allowed as exceptions to incoming_remove_headers. Simply a
+# whitespace delimited list of header names and names can optionally end with
+# '*' to indicate a prefix match.
+# incoming_allow_headers =
+#
+# The headers to remove from outgoing responses. Simply a whitespace delimited
+# list of header names and names can optionally end with '*' to indicate a
+# prefix match. outgoing_allow_headers is a list of exceptions to these
+# removals.
+# outgoing_remove_headers = x-object-meta-*
+#
+# The headers allowed as exceptions to outgoing_remove_headers. Simply a
+# whitespace delimited list of header names and names can optionally end with
+# '*' to indicate a prefix match.
+# outgoing_allow_headers = x-object-meta-public-*
+
+# Note: Put formpost just before your auth filter(s) in the pipeline
+[filter:formpost]
+use = egg:swift#formpost
+
+[filter:keystoneauth]
+operator_roles = Member,admin
+use = egg:swift#keystoneauth
+
+[filter:proxy-logging]
+use = egg:swift#proxy_logging
+# access_log_name = proxy
+# access_log_facility = LOG_LOCAL0
+# access_log_level = INFO
+# access_log_address = /dev/log
+# If set, access_log_udp_host will override access_log_address
+# access_log_udp_host =
+# access_log_udp_port = 514
+# You can use log_statsd_* from [DEFAULT] or override them here:
+# access_log_statsd_host = localhost
+# access_log_statsd_port = 8125
+# access_log_statsd_default_sample_rate = 1
+# access_log_statsd_metric_prefix =
+# access_log_headers = False
+# What HTTP methods are allowed for StatsD logging (comma-sep); request methods
+# not in this list will have "BAD_METHOD" for the portion of the metric.
+# log_statsd_valid_http_methods = GET,HEAD,POST,PUT,DELETE,COPY
+
diff --git a/templates/default/pull-rings.sh.erb b/templates/default/pull-rings.sh.erb
new file mode 100644
index 0000000..5e4f57c
--- /dev/null
+++ b/templates/default/pull-rings.sh.erb
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# this has to be run as root to restart the services...
+
+if [ ! -d /etc/swift/rings ] || [ ! -e /etc/swift/rings/.git/config ]; then
+ rm -rf /etc/swift/rings
+ git clone git://<%= @builder_ip %>/rings /etc/swift/rings
+fi
+
+cd /etc/swift/rings
+git reset --hard
+git clean -df
+git pull
+
+[ -e /etc/swift/rings ] && chown -R swift: /etc/swift/rings
+
+for d in object account container; do
+ if [ -e /etc/swift/rings/${d}.ring.gz ]; then
+ if [ ! -e ../${d}.ring.gz ] || [ "$(md5sum ${d}.ring.gz | cut -f1 -d' ')" != "$(md5sum ../${d}.ring.gz | cut -f1 -d' ')" ]; then
+ cp ${d}.ring.gz ../${d}.ring.new
+ chown swift: ../${d}.ring.new
+ mv ../${d}.ring.new ../${d}.ring.gz
+
+ if [ -e /etc/swift/${d}-server.conf ]; then
+ service <%= @service_prefix %>swift-${d}-replicator restart
+ fi
+ fi
+ fi
+done
diff --git a/templates/default/rsyncd.conf.erb b/templates/default/rsyncd.conf.erb
new file mode 100644
index 0000000..5bd8526
--- /dev/null
+++ b/templates/default/rsyncd.conf.erb
@@ -0,0 +1,24 @@
+uid = swift
+gid = swift
+
+log file = /var/log/rsyncd.log
+pid file = /var/run/rsyncd.pid
+address = 0.0.0.0
+
+[account]
+max connections = 10
+path = /srv/node/
+read only = false
+lock file = /var/lock/account.lock
+
+[container]
+max connections = 10
+path = /srv/node/
+read only = false
+lock file = /var/lock/container.lock
+
+[object]
+max connections = 10
+path = /srv/node/
+read only = false
+lock file = /var/lock/object.lock
diff --git a/templates/default/simple-redhat-init-config.erb b/templates/default/simple-redhat-init-config.erb
new file mode 100644
index 0000000..28c002f
--- /dev/null
+++ b/templates/default/simple-redhat-init-config.erb
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+### BEGIN INIT INFO
+# Provides: <%= @exec %>
+# Required-Start: $remote_fs
+# Required-Stop: $remote_fs
+# Default-Stop: 0 1 6
+# Description: <%= @description %>
+### END INIT INFO
+
+# chkconfig: - 98 02
+
+
+. /etc/rc.d/init.d/functions
+
+name="<%= @exec =%>"
+
+[ -e "/etc/sysconfig/openstack-swift-$name" ] && . "/etc/sysconfig/openstack-swift-$name"
+
+lockfile="/var/lock/subsys/openstack-swift-$name"
+
+start() {
+ swift-init "$name" start
+ retval=$?
+ [ $retval -eq 0 ] && touch $lockfile
+ return $retval
+}
+
+stop() {
+ swift-init "$name" stop
+ retval=$?
+ [ $retval -eq 0 ] && rm -f $lockfile
+ return $retval
+}
+
+restart() {
+ stop
+ start
+}
+
+rh_status() {
+ swift-init "$name" status
+ retval=$?
+ return $retval
+}
+
+rh_status_q() {
+ rh_status &> /dev/null
+}
+
+
+case "$1" in
+ start)
+ rh_status_q && exit 0
+ $1
+ ;;
+ stop)
+ rh_status_q || exit 0
+ $1
+ ;;
+ restart)
+ $1
+ ;;
+ reload)
+ ;;
+ status)
+ rh_status
+ ;;
+ condrestart|try-restart)
+ rh_status_q || exit 0
+ restart
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart}"
+ exit 2
+esac
+exit $?
diff --git a/templates/default/simple-systemd-config.erb b/templates/default/simple-systemd-config.erb
new file mode 100644
index 0000000..3958250
--- /dev/null
+++ b/templates/default/simple-systemd-config.erb
@@ -0,0 +1,11 @@
+[Unit]
+Description=<%= @description %>
+After=syslog.target network.target
+
+[Service]
+Type=simple
+User=<%= @user %>
+ExecStart=<%= @exec %>
+
+[Install]
+WantedBy=multi-user.target