commit 47d49e7780156175f86a9bec853453649b9c5de1 Author: Dan Bode Date: Thu Jan 19 18:58:37 2012 -0800 Initial commit. This initial commit only targets a single node swift install on Natty based on 1.4.6 diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..8d968b6c --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README b/README new file mode 100644 index 00000000..1a85549b --- /dev/null +++ b/README @@ -0,0 +1,148 @@ +==Disclaimer + +This is not ready to be used. + +This is pre-beta code that is actively being developed. + +It is currently being developed against swift trunk. + +I am actively seeking users who understand that this code +is in a pre-alpha state. Feel free to contact me (Dan Bode) +at dan < at > puppetlabs com or bodepd < on > freenode. + +Any feedback greatly appreciated. + +==Dependencies + +This module has the following dependencies: + + https://github.com/bodepd/puppet-ssh + (this should actaully depend on https://github.com/saz/puppet-ssh + and can when pull request https://github.com/saz/puppet-ssh/pull/1 + is merged) + https://github.com/bodepd/puppet-rsync + (there is a pull request to merge this into the parent repo: + https://github.com/bodepd/puppet-rsync) + https://github.com/saz/puppet-memcached + https://github.com/puppetlabs/puppetlabs-stdlib + + This module has only been tested on Ubuntu Natty, with Puppet 2.7.9 + +this module is intended to complement other openstack modules and +will eventually be a submodule of the openstack set of modules: + + https://github.com/puppetlabs/puppetlabs-openstack + + These modules have only been verified as working against the + Swift all in one installation instructions: http://swift.openstack.org/development_saio.html + + They have also only been tested for 1.4.6 (and will probably not work for Diablo... yet) + +==Usage: + + swift: + + class that sets up base packages and the base + /etc/swift/swift.conf. + + class { 'swift': + # shared salt used when hashing ring mappings + swift_hash_suffix => 'shared_secret', + } + + swift::proxy: + + class that installs and configures the swift + proxy server + + class { 'swift::proxy': + # specifies that account should be automatically created + account_autocreate = true, + #proxy_local_net_ip = '127.0.0.1', + #proxy_port = '11211', + # auth type defaults to tempauth - this is the + # only auth that has been tested + #auth_type = 'tempauth', + } + + swift::storage + + class that sets up all of the configuration and dependencies + for swift storage instances + + class { 'swift::storage': + # address that swift should bind to + storage_local_net_ip => '127.0.0.1' + } + + swift::storage::device + + defined resource type that can be used to + indicate a specific device to be managed + + This will configure the rsync server instance + and swift storage instance to manage the device (which + basically maps port to device) + + # the title for this device is the port where it + # will be hosted + swift::storage::device { '6010' + # the type of device (account/object/container) + type => 'object', + # directory where device is mounted + devices = '/srv/node', + # address to bind to + storage_local_net_ip = '127.0.0.1' + ) { + + swift::storage::loopback + + This defined resource was created to test + swift by creating loopback devices that can be + used for testing + + It creates a partition of size [$seek] + at base_dir/[$name] using dd with [$byte_size], + formats it to be an xfs + filesystem which is mounted at /src/node/[$name] + + It then creates swift::storage::devices for each device + type using the title as the 3rd digit of + a four digit port number + + 60[digit][role] (object = 0, container = 1, account = 2) + + swift::storage::loopback { '1': + base_dir = '/srv/loopback-device', + mnt_base_dir = '/srv/node', + byte_size = '1024', + seek = '25000', + storage_local_net_ip = '127.0.0.1' + } + + swift::ringbuiler + + class that knows how to build rings. This only exists as a vague idea + + the ring building will like be built as a combination of native types + (for adding the drives) and defined types for rebalancing + +==Example + +For an example of how to use this module to build out a single node +swift cluster, you can try running puppet apply examples/site.pp +(feel free to look at the code to see how to use this module) + +There are a few known issues with this code: + + - for some reason the ringbuilding script does not run + after the manifest fails, you still need to login + and run bash /etc/swift/ringbuilder.sh and start swift-proxy + - once swift is running, you can test the swift instance with the + ruby script stored in files/swift_tester.rb + +This example can be used as follows: + +puppet apply examples/site.pp --certname pre_swift + +puppet apply examples/site.pp --certname swift_all diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..6242a370 --- /dev/null +++ b/Rakefile @@ -0,0 +1,14 @@ +require 'rake' + +task :default => [:spec] + +desc "Run all module spec tests (Requires rspec-puppet gem)" +task :spec do + system("rspec spec/**/*_spec.rb") +end + +desc "Build package" +task :build do + system("puppet-module build") +end + diff --git a/TODO b/TODO new file mode 100644 index 00000000..5aecf005 --- /dev/null +++ b/TODO @@ -0,0 +1,30 @@ +* +Disclaimer - this does not work yet. + +I will make a release when I feel like things are coming together. + +I am only releasing this on github, b/c people are interested in the +code dispite the fact that it does not quite work yet. + +* + + +This currently only works on ubuntu releases that +can configure swift using upstart + + +swift is currently not using ssl for transport b/c I did not create +self signed certs as a part of the installation +(also swift proxy is not configured to use those certs) + +# not + +# disable TIME_WAIT.. wait.. +net.ipv4.tcp_tw_recycle=1 +net.ipv4.tcp_tw_reuse=1 +# disable syn cookies +net.ipv4.tcp_syncookies = 0 +# double amount of allowed conntrack +net.ipv4.netfilter.ip_conntrack_max = 26214 + +autocreate - should be set to true if tempauth is used diff --git a/examples/site.pp b/examples/site.pp new file mode 100644 index 00000000..5ae87bee --- /dev/null +++ b/examples/site.pp @@ -0,0 +1,100 @@ +# +# This example file is almost the +# same as what I have been using +# to build swift in my environment (which is based on vagrant) + +$proxy_local_net_ip='127.0.0.1' +$swift_shared_secret='changeme' +Exec { logoutput => true } + +# set up all of the pre steps +# this shoud be run +node pre_swift { + + class { 'apt':} + # use the swift trunk ppa + class { 'swift::repo::trunk':} + + # use our apt repo + apt::source { 'puppet': + location => 'http://apt.puppetlabs.com/ubuntu', + release => 'natty', + key => '4BD6EC30', + } + # install the latest version of Puppet + package { 'puppet': + ensure => latest, + require => Apt::Source['puppet'], + } +} + +node swift_all { + # install curl (we will need it for testing) + package { 'curl': ensure => present } + + # ensure that sshd is installed + class { 'ssh::server::install': } + + # install memcached for the proxy + class { 'memcached': + listen_ip => $proxy_local_net_ip, + } + + # set up the swift base deps + class { 'swift': + # not sure how I want to deal with this shared secret + swift_hash_suffix => $swift_shared_secret, + package_ensure => latest, + } + + # configure base deps for storage + class { 'swift::storage': } + + # set up three loopback devices for testing + swift::storage::loopback { ['1', '2', '3']: + require => Class['swift'], + } + + # this is just here temporarily until I can better figure out how to model it + file { '/etc/swift/ringbuilder.sh': + content => ' +#!/bin/bash +# sets up a basic ring-builder +# which is hard-coded to only work +# for this example + +cd /etc/swift + +rm -f *.builder *.ring.gz backups/*.builder backups/*.ring.gz + +swift-ring-builder object.builder create 18 3 1 +swift-ring-builder object.builder add z1-127.0.0.1:6010/1 1 +swift-ring-builder object.builder add z2-127.0.0.1:6020/2 1 +swift-ring-builder object.builder add z3-127.0.0.1:6030/3 1 +swift-ring-builder object.builder rebalance +swift-ring-builder container.builder create 18 3 1 +swift-ring-builder container.builder add z1-127.0.0.1:6011/1 1 +swift-ring-builder container.builder add z2-127.0.0.1:6021/2 1 +swift-ring-builder container.builder add z3-127.0.0.1:6031/3 1 +swift-ring-builder container.builder rebalance +swift-ring-builder account.builder create 18 3 1 +swift-ring-builder account.builder add z1-127.0.0.1:6012/1 1 +swift-ring-builder account.builder add z2-127.0.0.1:6022/2 1 +swift-ring-builder account.builder add z3-127.0.0.1:6032/3 1 +swift-ring-builder account.builder rebalance + ', + mode => '555', + }~> + # TODO - figure out why this is failing ;( + exec { 'build-ring': + command => '/etc/swift/ringbuilder.sh', + refreshonly => true, + notify => Service['swift-proxy'] + } + + # configure swift proxy after the run is build + class { 'swift::proxy': + account_autocreate => true, + } + +} diff --git a/files/ringbuilder.sh b/files/ringbuilder.sh new file mode 100644 index 00000000..a81846f5 --- /dev/null +++ b/files/ringbuilder.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +cd /etc/swift + +rm -f *.builder *.ring.gz backups/*.builder backups/*.ring.gz + +swift-ring-builder object.builder create 18 3 1 +swift-ring-builder object.builder add z1-127.0.0.1:6010/sdb1 1 +swift-ring-builder object.builder add z2-127.0.0.1:6020/sdb2 1 +swift-ring-builder object.builder add z3-127.0.0.1:6030/sdb3 1 +swift-ring-builder object.builder rebalance +swift-ring-builder container.builder create 18 3 1 +swift-ring-builder container.builder add z1-127.0.0.1:6011/sdb1 1 +swift-ring-builder container.builder add z2-127.0.0.1:6021/sdb2 1 +swift-ring-builder container.builder add z3-127.0.0.1:6031/sdb3 1 +swift-ring-builder container.builder rebalance +swift-ring-builder account.builder create 18 3 1 +swift-ring-builder account.builder add z1-127.0.0.1:6012/sdb1 1 +swift-ring-builder account.builder add z3-127.0.0.1:6022/sdb2 1 +swift-ring-builder account.builder add z4-127.0.0.1:6032/sdb3 1 +swift-ring-builder account.builder rebalance diff --git a/files/swift_tester.rb b/files/swift_tester.rb new file mode 100644 index 00000000..3ce3ca53 --- /dev/null +++ b/files/swift_tester.rb @@ -0,0 +1,118 @@ +#!/usr/bin/env ruby +# +# This is a script that uses +# instructions here: http://swift.openstack.org/howto_installmultinode.html +# Even though I expect this script will work with a wide range +# of swift versions, it is currently only tested with: 1.4.6 +require 'open3' +require 'fileutils' + +# connection variables +proxy_local_net_ip='127.0.0.1' +user='test:tester' +password='testing' + +# headers for curl requests +user_header="-H 'X-Storage-User: #{user}'" +password_header="-H 'X-Storage-Pass: #{password}'" +get_cred_command="curl -k -v #{user_header} #{password_header} http://#{proxy_local_net_ip}:8080/auth/v1.0" + +# verify that we can retrive credentials from our user +result_hash = {} +puts "getting credentials: #{get_cred_command}" +Open3.popen3(get_cred_command) do |stdin, stdout, stderr| + result_hash[:stderr] = stderr.read + result_hash[:stderr].split("\n").each do |line| + if line =~ /^< HTTP\/\d\.\d (\d\d\d)/ + result_hash[:status_code]=$1 + end + if line =~ /< X-Storage-Url: (http\S+)/ + result_hash[:auth_url]=$1 + end + if line =~ /< X-Storage-Token: (AUTH_\S+)/ + result_hash[:auth_token]=$1 + end + end +end +raise(Exception, "Call to get auth tokens failed:\n#{result_hash[:stderr]}") unless result_hash[:status_code] == '200' + +# verify that the credentials are valid +auth_token_header="-H 'X-Auth-Token: #{result_hash[:auth_token]}'" +get_account_head="curl -k -v #{auth_token_header} #{result_hash[:auth_url]}" +# what is the expected code? +puts "verifying connection auth: #{get_account_head}" +Open3.popen3(get_account_head) do |stdin, stdout, stderr| + #puts stdout.read + #puts stderr.read +end + + +proxy_local_net_ip='127.0.0.1' +user='test:tester' +password='testing' +swift_command_prefix="swift -A http://#{proxy_local_net_ip}:8080/auth/v1.0 -U #{user} -K #{password}" + +swift_test_command="#{swift_command_prefix} stat" + +puts "Testing swift: #{swift_test_command}" +status_hash={} +Open3.popen3(swift_test_command) do |stdin, stdout, stderr| + status_hash[:stdout] = stdout.read + status_hash[:stderr] = stderr.read + status_hash[:stdout].split("\n").each do |line| + if line =~ /\s*Containers:\s+(\d+)/ + status_hash[:containers] = $1 + end + if line =~ /\s*Objects:\s+(\d+)/ + status_hash[:objects] = $1 + end + end +end + +unless(status_hash[:containers] =~ /\d+/ and status_hash[:objects] =~ /\d+/) + raise(Exception, "Expected to find the number of containers/objects:\n#{status_hash[:stdout]}\n#{status_hash[:stderr]}") +else + puts "found containers/objects: #{status_hash[:containers]}/#{status_hash[:objects]}" +end + +# test that we can upload something +File.open('/tmp/foo1', 'w') do |fh| + fh.write('test1') +end + +container = 'my_container' + +swift_upload_command="#{swift_command_prefix} upload #{container} /tmp/foo1" +puts "Uploading file to swift with command: #{swift_upload_command}" + +Open3.popen3(swift_upload_command) do |stdin, stdout, stderr| + puts stdout.read + puts stderr.read +end + +# test that we can download the thing that we uploaded +download_test_dir = '/tmp/test/downloadtest/' +FileUtils.rm_rf download_test_dir +FileUtils.mkdir_p download_test_dir + +swift_download_command="#{swift_command_prefix} download #{container}" +puts "Downloading file with command: #{swift_download_command}" +Dir.chdir(download_test_dir) do + Open3.popen3(swift_download_command) do |stdin, stdout, stderr| + puts stdout.read + puts stderr.read + end +end + +expected_file = File.join(download_test_dir, 'tmp', 'foo1') + +if File.exists?(expected_file) + if File.read(expected_file) == 'test1' + puts "Dude!!!! It actually seems to work, we can upload and download files!!!!" + else + raise(Exception, "So close, but the contents of the downloaded file are not what I expected: Got: #{File.read(expected_file)}, expected: test1") + end +else + raise(Exception, "file #{expected_file} did not exist somehow, probably b/c swift is not installed correctly") +end + diff --git a/manifests/init.pp b/manifests/init.pp new file mode 100644 index 00000000..e798c26e --- /dev/null +++ b/manifests/init.pp @@ -0,0 +1,71 @@ +# Install and configure base swift components +# == Parameters +# [*swift_hash_suffix*] string of text to be used +# as a salt when hashing to determine mappings in the ring. +# This file should be the same on every node in the cluster! +# +# [*swift_ssh_key*] NOT YET IMPLEMENTED +# + +class swift( + $swift_hash_suffix, +# $swift_ssh_key, + $package_ensure = 'present' +) { + + # maybe I should just install ssh? + Class['ssh::server::install'] -> Class['swift'] + + package { 'swift': + ensure => $package_ensure, + } + + File { owner => 'swift', group => 'swift', require => Package['swift'] } + + file { '/home/swift': + ensure => directory, + mode => 0700, + } + + file { '/etc/swift': + ensure => directory, + mode => 2770, + } + + file { '/var/run/swift': + ensure => directory, + } + + file { '/etc/swift/swift.conf': + ensure => present, + mode => 0660, + content => template('swift/swift.conf.erb'), + } + +# if ($swift_ssh_key) { +# if $swift_ssh_key !~ /^(ssh-...) +([^ ]*) *([^ \n]*)/ { +# err("Can't parse swift_ssh_key") +# notify { "Can't parse public key file $name on the keymaster: skipping ensure => $e +#nsure": } +# } else { +# $keytype = $1 +# $modulus = $2 +# $comment = $3 +# ssh_authorized_key { $comment: +# ensure => "present", +# user => "swift", +# type => $keytype, +# key => $modulus, +# options => $options ? { "" => undef, default => $options }, +# require => File["/home/swift"] +# } +# } +# } +# does swift need an ssh key? +# they are adding one in the openstack modules + +# +# I do not understand how to configure the rings +# or why rings would be configured on the proxy? + +} diff --git a/manifests/proxy.pp b/manifests/proxy.pp new file mode 100644 index 00000000..3df721a5 --- /dev/null +++ b/manifests/proxy.pp @@ -0,0 +1,81 @@ +# +# [*auth_type*] - specified the type of authorization to use. +# valid values are tempauth, swauth, and keystone. Optional +# Defaults to keystone +class swift::proxy( + # why did cloudbuilders default this to false? + $allow_account_management = true, + $account_autocreate = false, + $proxy_local_net_ip = '127.0.0.1', + $proxy_port = '11211', + $auth_type = 'tempauth', + $swauth_endpoint = '127.0.0.1', + $swauth_super_admin_key = 'swauthkey', + $package_ensure = 'present' +) inherits swift { + + Class['memcached'] -> Class['swift::proxy'] + + validate_re($auth_type, 'tempauth|swauth|keystone') + + if(auth_type == 'keystone') { + fail('Keystone is currently not supported, it should be supported soon :)') + } + + if($user_swauth) { + package { 'python-swauth': + ensure => $package_ensure, + before => Package['swift-proxy'], + } + } + + package { 'swift-proxy': + ensure => $package_ensure, + } + + file { "/etc/swift/proxy-server.conf": + ensure => present, + owner => 'swift', + group => 'swift', + mode => 0660, + content => template('swift/proxy-server.conf.erb'), + require => Package['swift-proxy'], + } + + # TODO - this needs to be updated once the init file is not broken + + file { '/etc/init/swift-proxy.conf': + mode => '0644', + owner => 'root', + group => 'root', + content => ' +# swift-proxy - SWIFT Proxy Server +# This is temporarily managed by Puppet +# until 917893 is fixed +# The swift proxy server. + +description "SWIFT Proxy Server" +author "Marc Cluet " + +start on runlevel [2345] +stop on runlevel [016] + +pre-start script + if [ -f "/etc/swift/proxy-server.conf" ]; then + exec /usr/bin/swift-init proxy-server start + else + exit 1 + fi +end script + +post-stop exec /usr/bin/swift-init proxy-server stop', + before => Service['swift-proxy'], + } + + service { 'swift-proxy': + ensure => running, + enable => true, + provider => 'upstart', + subscribe => File['/etc/swift/proxy-server.conf'], + } +} diff --git a/manifests/repo/trunk.pp b/manifests/repo/trunk.pp new file mode 100644 index 00000000..fce3da19 --- /dev/null +++ b/manifests/repo/trunk.pp @@ -0,0 +1,6 @@ +# +# sets up the swift trunk ppa +# +class swift::repo::trunk { + apt::ppa { 'ppa:swift-core/trunk': } +} diff --git a/manifests/ringbuilder.pp b/manifests/ringbuilder.pp new file mode 100644 index 00000000..d5229d86 --- /dev/null +++ b/manifests/ringbuilder.pp @@ -0,0 +1,13 @@ +# +# role for deploying +# +class swift::ringbuider( + +) { + # search for all nodes that have class swift::storage + # + # determine what their swift drives are (how can I determine this?) + #exec { + # command => + #} +} diff --git a/manifests/ringbuilder/balance.pp b/manifests/ringbuilder/balance.pp new file mode 100644 index 00000000..823525ba --- /dev/null +++ b/manifests/ringbuilder/balance.pp @@ -0,0 +1,8 @@ +define ringbuilder::rebalance() { + validate_re($name, '^object|contianer|account$') + exec { "rebalance_${name}": + command => "swift-ring-builder ${name}.builder rebalance", + path => ['/usr/bin'], + refreshonly => true, + } +} diff --git a/manifests/storage.pp b/manifests/storage.pp new file mode 100644 index 00000000..5386ec23 --- /dev/null +++ b/manifests/storage.pp @@ -0,0 +1,86 @@ +# +# class for building out a storage node +# +class swift::storage( + $package_ensure = 'present', + # TODO - should this default to 0.0.0.0? + $storage_local_net_ip = '127.0.0.1' +) inherits swift { + + + class{ 'rsync::server': + use_xinetd => false, + address => $storage_local_net_ip, + } + + Service { + ensure => running, + enable => true, + hasstatus => true, + subscribe => Service['rsync'], + } + + File { + owner => 'swift', + group => 'swift', + } + + # package dependencies + package { ['xfsprogs', 'parted']: + ensure => 'present' + } + + package { 'swift-account': + ensure => $package_ensure, + } + + file { '/etc/swift/account-server.conf': + ensure => present, + mode => 0660, + content => template('swift/account-server.conf.erb') + } + + file { '/etc/swift/account-server/': + ensure => directory, + } + + service { 'swift-account': + provider => 'upstart', + } + + package { 'swift-container': + ensure => $package_ensure, + } + + file { '/etc/swift/container-server.conf': + ensure => present, + mode => 0660, + content => template('swift/container-server.conf.erb') + } + + file { '/etc/swift/container-server/': + ensure => directory, + } + + service { 'swift-container': + provider => 'upstart', + } + + package { 'swift-object': + ensure => $package_ensure, + } + + file { '/etc/swift/object-server.conf': + ensure => present, + mode => 0660, + content => template('swift/object-server.conf.erb') + } + + file { '/etc/swift/object-server/': + ensure => directory, + } + + service { 'swift-object': + provider => 'upstart', + } +} diff --git a/manifests/storage/device.pp b/manifests/storage/device.pp new file mode 100644 index 00000000..0680d01a --- /dev/null +++ b/manifests/storage/device.pp @@ -0,0 +1,44 @@ +# +# I am not sure if this is the right name +# - should it be device? +# +# name - is going to be port +define swift::storage::device( + $type, + $devices = '/srv/node', + $owner = 'swift', + $group = 'swift', + $max_connections = 25, + $storage_local_net_ip = '127.0.0.1' +) { + + validate_re($name, '^\d+$') + validate_re($type, '^object|container|account$') + # TODO - validate that name is an integer + + # This makes me think that perhaps the rsync class + # should be split into install and config + # + Swift::Storage::Device[$name] ~> Service['rsync'] + + $bind_port = $name + + Rsync::Server::Module { + uid => $owner, + gid => $group, + max_connections => $max_connections, + read_only => false, + } + + rsync::server::module { "${type}${name}": + path => $devices, + lock_file => "/var/lock/${type}${name}.lock" + } + + file { "/etc/swift/${type}-server/${name}.conf": + content => template("swift/${type}-server.conf.erb"), + owner => $owner, + group => $group, + } + +} diff --git a/manifests/storage/loopback.pp b/manifests/storage/loopback.pp new file mode 100644 index 00000000..54f60bb0 --- /dev/null +++ b/manifests/storage/loopback.pp @@ -0,0 +1,50 @@ +# follow the instructions for creating a loopback device +# for storage from: http://swift.openstack.org/development_saio.html +# +# +# creates a managed loopback interface +# - creates a file +# - formats the file to be an xfs device and mounts it as a loopback device at /srv/node/$name +# - sets up each mount point as a swift endpoint +define swift::storage::loopback( + $base_dir = '/srv/loopback-device', + $mnt_base_dir = '/srv/node', + $byte_size = '1024', + $seek = '25000', + $storage_local_net_ip = '127.0.0.1' +) { + + if(!defined(File[$base_dir])) { + file { $base_dir: + ensure => directory, + } + } + + if(!defined(File[$mnt_base_dir])) { + file { $mnt_base_dir: + owner => 'swift', + group => 'swift', + ensure => directory, + } + } + + exec { "create_partition-${name}": + command => "dd if=/dev/zero of=${base_dir}/${name} bs=${byte_size} count=0 seek=${seek}", + path => ['/usr/bin/', '/bin'], + unless => "test -f ${base_dir}/${name}", + require => File[$base_dir], + } + + swift::storage::xfs { $name: + device => "${base_dir}/${name}", + mnt_base_dir => $mnt_base_dir, + byte_size => $byte_size, + subscribe => Exec["create_partition-${name}"], + } + + swift::storage::node { $name: + mnt_base_dir => $mnt_base_dir, + storage_local_net_ip => $storage_local_net_ip, + require => Swift::Storage::Xfs[$name] + } +} diff --git a/manifests/storage/mount.pp b/manifests/storage/mount.pp new file mode 100644 index 00000000..4fac6302 --- /dev/null +++ b/manifests/storage/mount.pp @@ -0,0 +1,43 @@ +# +# Usage +# swift::storage::mount +# +# +define swift::storage::mount( + $device, + $mnt_base_dir = '/srv/node', +) { + + # the directory that represents the mount point + # needs to exist + file { "${mnt_base_dir}/${name}": + ensure => directory, + owner => 'swift', + group => 'swift', + } + + mount { "${mnt_base_dir}/${name}": + ensure => present, + device => $device, + fstype => 'xfs', + options => 'loop,noatime,nodiratime,nobarrier,logbufs=8', + require => File["${mnt_base_dir}/${name}"] + } + + # double checks to make sure that things are mounted + exec { "mount_${name}": + command => "mount ${mnt_base_dir}/${name}", + path => ['/bin'], + require => Mount["${mnt_base_dir}/${name}"], + unless => "grep ${mnt_base_dir}/${name} /etc/mtab", + # TODO - this needs to be removed when I am done testing + logoutput => true, + } + + exec { "fix_mount_permissions_${name}": + command => "chown -R swift:swift ${mnt_base_dir}/${name}", + path => ['/usr/sbin', '/bin'], + subscribe => Exec["mount_${name}"], + refreshonly => true, + } +} diff --git a/manifests/storage/node.pp b/manifests/storage/node.pp new file mode 100644 index 00000000..107ca744 --- /dev/null +++ b/manifests/storage/node.pp @@ -0,0 +1,28 @@ +define swift::storage::node( + $mnt_base_dir, + $owner = 'swift', + $group = 'swift', + $max_connections = 25, + $storage_local_net_ip = '127.0.0.1' +) { + + Swift::Storage::Device { + devices => $mnt_base_dir, + owner => $owner, + group => $group, + max_connections => $max_connections, + } + + swift::storage::device { "60${name}0": + type => 'object', + } + + swift::storage::device { "60${name}1": + type => 'container', + } + + swift::storage::device { "60${name}2": + type => 'account', + } + +} diff --git a/manifests/storage/xfs.pp b/manifests/storage/xfs.pp new file mode 100644 index 00000000..93081f57 --- /dev/null +++ b/manifests/storage/xfs.pp @@ -0,0 +1,34 @@ +# follow the instructions for creating a loopback device +# for storage from: http://swift.openstack.org/development_saio.html +# +# +# +# this define needs to be sent a refresh signal to do anything +# +# +# [*title*] +# +# [*byte_size*] Byte size to use for every inode in the created filesystem. +# It is recommened to use 1024 to ensure that the metadata can fit in a single inode. +define swift::storage::xfs( + $device, + $byte_size = '1024', + $mnt_base_dir = '/srv/node' +) { + + # does this have to be refreshonly? + # how can I know if this drive has been formatted? + exec { "mkfs-${name}": + command => "mkfs.xfs -i size=${byte_size} ${device}", + path => ['/sbin/'], + refreshonly => true, + require => Package['xfsprogs'], + } + + swift::storage::mount { $name: + device => $device, + mnt_base_dir => $mount_base_dir, + subscribe => Exec["mkfs-${name}"] + } + +} diff --git a/spec/classes/.swift_storage_spec.rb.swp b/spec/classes/.swift_storage_spec.rb.swp new file mode 100644 index 00000000..216b9544 Binary files /dev/null and b/spec/classes/.swift_storage_spec.rb.swp differ diff --git a/spec/classes/swift_storage_spec.rb b/spec/classes/swift_storage_spec.rb new file mode 100644 index 00000000..0489325b --- /dev/null +++ b/spec/classes/swift_storage_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +describe 'swift::storage' do + + let :pre_condition do + "class { 'swift': swift_hash_suffix => 'changeme' } + include ssh::server::install + " + end + + let :default_params do + { + :package_ensure => 'present' + } + end + + [{}, + { + :package_ensure => 'latest' + } + ].each do |param_set| + + describe "when #{param_set == {} ? "using default" : "specifying"} class parameters" do + let :param_hash do + default_params.merge(param_set) + end + + let :params do + param_set + end + + ['xfsprogs', 'parted', 'rsync'].each do |present_package| + it { should contain_package(present_package).with_ensure('present') } + end + #it 'should compile the template based on the class parameters' do + # content = param_value(subject, 'file', '/etc/glance/glance-api.conf', 'content') + # expected_lines = [ + # "verbose = #{param_hash[:log_verbose]}", + # "debug = #{param_hash[:log_debug]}", + # "default_store = #{param_hash[:default_store]}", + # "bind_host = #{param_hash[:bind_host]}", + # "bind_port = #{param_hash[:bind_port]}", + # "registry_host = #{param_hash[:registry_host]}", + # "registry_port = #{param_hash[:registry_port]}", + # "log_file = #{param_hash[:log_file]}", + # "filesystem_store_datadir = #{param_hash[:filesystem_store_datadir]}", + # "swift_store_auth_address = #{param_hash[:swift_store_auth_address]}", + # "swift_store_user = #{param_hash[:swift_store_user]}", + # "swift_store_key = #{param_hash[:swift_store_key]}", + # "swift_store_container = #{param_hash[:swift_store_container]}", + # "swift_store_create_container_on_put = #{param_hash[:swift_store_create_container_on_put]}" + # ] + # (content.split("\n") & expected_lines).should == expected_lines + #end + end + end +end diff --git a/spec/spec.opts b/spec/spec.opts new file mode 100644 index 00000000..91cd6427 --- /dev/null +++ b/spec/spec.opts @@ -0,0 +1,6 @@ +--format +s +--colour +--loadby +mtime +--backtrace diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..d2648da2 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,11 @@ +require 'puppet' +require 'rubygems' +require 'rspec-puppet' + +def param_value(subject, type, title, param) + subject.resource(type, title).send(:parameters)[param.to_sym] +end + +RSpec.configure do |c| + c.module_path = File.join(File.dirname(__FILE__), '../../') +end diff --git a/templates/account-server.conf.erb b/templates/account-server.conf.erb new file mode 100644 index 00000000..f2c60127 --- /dev/null +++ b/templates/account-server.conf.erb @@ -0,0 +1,25 @@ +[DEFAULT] +<% if scope.lookupvar('devices') != :undefined -%> +devices = <%= devices %> +<% end -%> +bind_ip = <%= storage_local_net_ip %> +<% if scope.lookupvar('bind_port') != :undefined -%> +bind_port = <%= bind_port %> +<% end -%> +mount_check = false +user = swift +log_facility = LOG_LOCAL2 +workers = 2 + +[pipeline:main] +pipeline = account-server + +[app:account-server] +use = egg:swift#account + +[account-replicator] +vm_test_mode = yes + +[account-auditor] + +[account-reaper] diff --git a/templates/container-server.conf.erb b/templates/container-server.conf.erb new file mode 100644 index 00000000..e9b89d71 --- /dev/null +++ b/templates/container-server.conf.erb @@ -0,0 +1,27 @@ +[DEFAULT] +<% if scope.lookupvar('devices') != :undefined -%> +devices = <%= devices %> +<% end -%> +bind_ip = <%= storage_local_net_ip %> +<% if scope.lookupvar('bind_port') != :undefined -%> +bind_port = <%= bind_port %> +<% end -%> +mount_check = false +user = swift +log_facility = LOG_LOCAL2 +workers = 2 + +[pipeline:main] +pipeline = container-server + +[app:container-server] +use = egg:swift#container + +[container-replicator] +vm_test_mode = yes + +[container-updater] + +[container-auditor] + +[container-sync] diff --git a/templates/object-server.conf.erb b/templates/object-server.conf.erb new file mode 100644 index 00000000..ef4b5cc2 --- /dev/null +++ b/templates/object-server.conf.erb @@ -0,0 +1,25 @@ +[DEFAULT] +<% if scope.lookupvar('devices') != :undefined -%> +devices = <%= devices %> +<% end -%> +bind_ip = <%= storage_local_net_ip %> +<% if scope.lookupvar('bind_port') != :undefined -%> +bind_port = <%= bind_port %> +<% end -%> +mount_check = false +user = swift +log_facility = LOG_LOCAL2 +workers = 2 + +[pipeline:main] +pipeline = object-server + +[app:object-server] +use = egg:swift#object + +[object-replicator] +vm_test_mode = yes + +[object-updater] + +[object-auditor] diff --git a/templates/proxy-server.conf.erb b/templates/proxy-server.conf.erb new file mode 100644 index 00000000..110da113 --- /dev/null +++ b/templates/proxy-server.conf.erb @@ -0,0 +1,40 @@ +# This file is managed by puppet. Do not edit +# +[DEFAULT] +#cert_file = /etc/swift/cert.crt +#key_file = /etc/swift/cert.key +bind_port = 8080 +workers = 8 +user = swift + +[pipeline:main] +# ratelimit? +pipeline = healthcheck cache <%= auth_type %> proxy-server + +[app:proxy-server] +use = egg:swift#proxy +allow_account_management = <%= allow_account_management %> +account_autocreate = <%= account_autocreate %> + +<% if auth_type == 'swauth' %> +[filter:swauth] +use = egg:swauth#swauth +# this line is not in the install docs? +default_swift_cluster = local#<%= swauth_endpoint %> +super_admin_key = <%= swauth_super_admin_key %> +<% elsif auth_type == 'tempauth' %> +[filter:tempauth] +use = egg:swift#tempauth +user_admin_admin = admin .admin .reseller_admin +user_test_tester = testing .admin +user_test2_tester2 = testing2 .admin +user_test_tester3 = testing3 +<% end %> + +[filter:healthcheck] +use = egg:swift#healthcheck + +[filter:cache] +use = egg:swift#memcache +# multi-proxy config not supported +memcache_servers = <%= proxy_local_net_ip %>:<%= proxy_port %> diff --git a/templates/rsyncd.conf.erb b/templates/rsyncd.conf.erb new file mode 100644 index 00000000..53061d7f --- /dev/null +++ b/templates/rsyncd.conf.erb @@ -0,0 +1,25 @@ +uid = swift +gid = swift + +# this should really be bound on an internal-only network +log file = /var/log/rsyncd.log +pid file = /var/run/rsyncd.pid +address = 0.0.0.0 + +[account] +max connections = 2 +path = /srv/node/ +read only = false +lock file = /var/lock/account.lock + +[container] +max connections = 2 +path = /srv/node/ +read only = false +lock file = /var/lock/container.lock + +[object] +max connections = 2 +path = /srv/node/ +read only = false +lock file = /var/lock/object.lock diff --git a/templates/swift.conf.erb b/templates/swift.conf.erb new file mode 100644 index 00000000..3bee313d --- /dev/null +++ b/templates/swift.conf.erb @@ -0,0 +1,2 @@ +[swift-hash] +swift_hash_path_suffix = <%= swift_hash_suffix %>