--- /dev/null
+.vagrant
+Berksfile.lock
+*~
+*#
+.#*
+\#*#
+.*.sw[a-z]
+*.un~
+/cookbooks
+
+# Bundler
+Gemfile.lock
+bin/*
+.bundle/*
+
+.kitchen/
+.kitchen.local.yml
--- /dev/null
+---
+driver_plugin: vagrant
+driver_config:
+ vagrantfile_erb: test/integration/Vagrantfile.erb
+ require_chef_omnibus: true
+
+platforms:
+- name: ubuntu-12.04
+ run_list:
+ - recipe[apt]
+- name: ubuntu-14.04
+ run_list:
+ - recipe[apt]
+- name: debian-7.4
+ run_list:
+ - recipe[apt]
+- name: centos-6.5
+- name: centos-5.10
+- name: fedora-18
+
+provisioner:
+ name: chef_zero
+
+suites:
+- name: default
+ run_list:
+ - "recipe[ceph::repo]"
+ - "recipe[ceph]"
+ attributes: &defaults
+ ceph:
+ config:
+ fsid: ae3f1d03-bacd-4a90-b869-1a4fabb107f2
+ mon_initial_members:
+ - "127.0.0.1"
+- name: osd
+ run_list:
+ - "role[ceph-osd]"
+ attributes: *defaults
+- name: mon
+ run_list:
+ - "role[ceph-mon]"
+ attributes: *defaults
+- name: mds
+ run_list:
+ - "role[ceph-mds]"
+ attributes: *defaults
+- name: radosgw
+ run_list:
+ - "role[ceph-radosgw]"
+ attributes: *defaults
+- name: aio
+ attributes:
+ ceph:
+ config-sections:
+ global:
+ "osd journal size" : 128
+ "osd pool default size": 1
+ osd_devices:
+ - { device: "/dev/sdb" }
+ - { device: "/dev/sdc" }
+ - { device: "/dev/sdd" }
+ run_list:
+ - recipe[ceph::all_in_one]
+ - recipe[ceph_test::cephfs]
--- /dev/null
+AllCops:
+ Include:
+ - Berksfile
+ - Gemfile
+ - Rakefile
+ - Thorfile
+ - Guardfile
+ Exclude:
+ - vendor/**
+
+ClassLength:
+ Enabled: false
+Documentation:
+ Enabled: false
+Encoding:
+ Enabled: false
+HashSyntax:
+ Enabled: false
+LineLength:
+ Enabled: false
+MethodLength:
+ Enabled: false
+SignalException:
+ Enabled: false
+TrailingComma:
+ Enabled: false
+WordArray:
+ Enabled: false
--- /dev/null
+language: ruby
+rvm:
+ - 1.9.3
+ - 2.0.0
+bundler_args: --without integration
+script:
+ - bundle exec rake travis
--- /dev/null
+site :opscode
+
+metadata
+
+group :integration do
+ cookbook 'apt'
+ cookbook 'ceph_test', path: 'test/cookbooks/ceph_test'
+end
--- /dev/null
+ceph
+====
+
+v0.2.0 (2014-03-03)
+-------------------
+
+- Add tests and fixes related.
+- Add ceph-extra
+- Fix searching feature
+- Refactor RPM part
+- Add iscsi tgt
+
+
+v0.1.0 (2013-07-18)
+-------------------
+
+- Initial changelog
--- /dev/null
+source 'https://rubygems.org'
+
+gem 'chef', '~> 11'
+gem 'berkshelf', '~> 2.0.10'
+
+group :test do
+ gem 'foodcritic', '~> 3.0'
+ gem 'rubocop', '~> 0.23.0'
+end
+
+group :integration do
+ gem 'test-kitchen', '~> 1.1.1'
+ gem 'kitchen-vagrant', '~> 0.14'
+end
--- /dev/null
+# Chef cookbook [![Build Status](https://travis-ci.org/ceph/ceph-cookbook.svg?branch=master)](https://travis-ci.org/ceph/ceph-cookbook) [![Gitter chat](https://badges.gitter.im/ceph/ceph-cookbook.png)](https://gitter.im/ceph/ceph-cookbook)
+
+## DESCRIPTION
+
+Installs and configures Ceph, a distributed network storage and filesystem designed to provide excellent performance, reliability, and scalability.
+
+The current version is focused towards deploying Monitors and OSD on Ubuntu.
+
+For documentation on how to use this cookbook, refer to the [USAGE](#USAGE) section.
+
+For help, use [Gitter chat](https://gitter.im/ceph/ceph-cookbook), [mailing-list](mailto:ceph-users-join@lists.ceph.com) or [issues](https://github.com/ceph/ceph-cookbook/issues)
+
+## REQUIREMENTS
+
+### Chef
+
+>= 11.6.0
+
+### Platform
+
+Tested as working:
+
+* Ubuntu Precise (12.04)
+
+### Cookbooks
+
+The ceph cookbook requires the following cookbooks from Opscode:
+
+https://github.com/opscode/cookbooks
+
+* apt
+* apache2
+
+
+## ATTRIBUTES
+
+### Ceph Rados Gateway
+
+* node[:ceph][:radosgw][:api_fqdn]
+* node[:ceph][:radosgw][:admin_email]
+* node[:ceph][:radosgw][:rgw_addr]
+
+## TEMPLATES
+
+## USAGE
+
+Ceph cluster design is beyond the scope of this README, please turn to the
+public wiki, mailing lists, visit our IRC channel, or contact Inktank:
+
+http://ceph.com/docs/master
+http://ceph.com/resources/mailing-list-irc/
+http://www.inktank.com/
+
+
+### Ceph Monitor
+
+Ceph monitor nodes should use the ceph-mon role.
+
+Includes:
+
+* ceph::default
+* ceph::conf
+
+### Ceph Metadata Server
+
+Ceph metadata server nodes should use the ceph-mds role.
+
+Includes:
+
+* ceph::default
+
+### Ceph OSD
+
+Ceph OSD nodes should use the ceph-osd role
+
+Includes:
+
+* ceph::default
+* ceph::conf
+
+### Ceph Rados Gateway
+
+Ceph Rados Gateway nodes should use the ceph-radosgw role
+
+## Resources/Providers
+
+### ceph\_client
+
+The ceph\_client LWRP provides an easy way to construct a Ceph client key. These keys are needed by anything that needs to talk to the Ceph cluster, including RadosGW, CephFS, and RBD access.
+
+#### Actions
+
+- :add - creates a client key with the given parameters
+
+#### Parameters
+
+- :name - name attribute. The name of the client key to create. This is used to provide a default for the other parameters
+- :caps - A hash of capabilities that should be granted to the client key. Defaults to `{ 'mon' => 'allow r', 'osd' => 'allow r' }`
+- :as\_keyring - Whether the key should be saved in a keyring format or a simple secret key. Defaults to true, meaning it is saved as a keyring
+- :keyname - The key name to register in Ceph. Defaults to `client.#{name}.#{hostname}`
+- :filename - Where to save the key. Defaults to `/etc/ceph/ceph.client.#{name}.#{hostname}.keyring` if `as_keyring` and `/etc/ceph/ceph.client.#{name}.#{hostname}.secret` if not `as_keyring`
+- :owner - Which owner should own the saved key file. Defaults to root
+- :group - Which group should own the saved key file. Defaults to root
+- :mode - What file mode should be applied. Defaults to '00640'
+
+### ceph\_cephfs
+
+The ceph\_cephfs LWRP provides an easy way to mount CephFS. It will automatically create a Ceph client key for the machine and mount CephFS to the specified location. If the kernel client is used, instead of the fuse client, a pre-existing subdirectory of CephFS can be mounted instead of the root.
+
+#### Actions
+
+- :mount - mounts CephFS
+- :umount - unmounts CephFS
+- :remount - remounts CephFS
+- :enable - adds an fstab entry to mount CephFS
+- :disable - removes an fstab entry to mount CephFS
+
+#### Parameters
+
+- :directory - name attribute. Where to mount CephFS in the local filesystem
+- :use\_fuse - whether to use ceph-fuse or the kernel client to mount the filesystem. ceph-fuse is updated more often, but the kernel client allows for subdirectory mounting. Defaults to true
+- :cephfs\_subdir - which CephFS subdirectory to mount. Defaults to '/'. An exception will be thrown if this option is set to anything other than '/' if use\_fuse is also true
+
+## LICENSE AND AUTHORS
+
+* Author: Kyle Bader <kyle.bader@dreamhost.com>
+
+* Copyright 2013, DreamHost Web Hosting and Inktank Storage 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.
--- /dev/null
+#!/usr/bin/env rake
+
+# Style tests. Rubocop and Foodcritic
+namespace :style do
+ begin
+ require 'rubocop/rake_task'
+ desc 'Run Ruby style checks'
+ RuboCop::RakeTask.new(:ruby)
+ rescue LoadError
+ puts '>>>>> Rubocop gem not loaded, omitting tasks' unless ENV['CI']
+ end
+
+ begin
+ require 'foodcritic'
+
+ desc 'Run Chef style checks'
+ FoodCritic::Rake::LintTask.new(:chef) do |t|
+ t.options = {
+ fail_tags: ['any'],
+ tags: ['~FC003'],
+ chef_version: '11.6.0'
+ }
+ end
+ rescue LoadError
+ puts '>>>>> foodcritic gem not loaded, omitting tasks' unless ENV['CI']
+ end
+end
+
+desc 'Run all style checks'
+task style: ['style:chef', 'style:ruby']
+
+# Integration tests. Kitchen.ci
+namespace :integration do
+ begin
+ require 'kitchen/rake_tasks'
+
+ desc 'Run kitchen integration tests'
+ Kitchen::RakeTasks.new
+ rescue LoadError
+ puts '>>>>> Kitchen gem not loaded, omitting tasks' unless ENV['CI']
+ end
+end
+
+desc 'Run all tests on Travis'
+task travis: ['style']
+
+# Default
+task default: ['style', 'integration:kitchen:all']
--- /dev/null
+default['ceph']['cephfs_mount'] = '/ceph'
+
+case node['platform_family']
+when 'debian'
+ packages = ['ceph-fs-common', 'ceph-fuse']
+ packages += debug_packages(packages) if node['ceph']['install_debug']
+ default['ceph']['cephfs']['packages'] = packages
+else
+ default['ceph']['cephfs']['packages'] = []
+end
--- /dev/null
+default['ceph']['config'] = {}
+default['ceph']['config-sections'] = {}
+default['ceph']['search_environment'] = true
--- /dev/null
+default['ceph']['install_debug'] = false
+default['ceph']['encrypted_data_bags'] = false
+
+default['ceph']['install_repo'] = true
+
+case node['platform']
+when 'ubuntu'
+ default['ceph']['init_style'] = 'upstart'
+else
+ default['ceph']['init_style'] = 'sysvinit'
+end
+
+case node['platform_family']
+when 'debian'
+ packages = ['ceph-common']
+ packages += debug_packages(packages) if node['ceph']['install_debug']
+ default['ceph']['packages'] = packages
+when 'rhel', 'fedora'
+ packages = ['ceph']
+ packages += debug_packages(packages) if node['ceph']['install_debug']
+ default['ceph']['packages'] = packages
+else
+ default['ceph']['packages'] = []
+end
--- /dev/null
+include_attribute 'ceph'
+
+default['ceph']['mds']['init_style'] = node['init_style']
+
+case node['platform_family']
+when 'debian'
+ packages = ['ceph-mds']
+ packages += debug_packages(packages) if node['ceph']['install_debug']
+ default['ceph']['mds']['packages'] = packages
+else
+ default['ceph']['mds']['packages'] = []
+end
--- /dev/null
+include_attribute 'ceph'
+
+default['ceph']['mon']['init_style'] = node['ceph']['init_style']
+
+default['ceph']['mon']['secret_file'] = '/etc/chef/secrets/ceph_mon'
+
+case node['platform_family']
+when 'debian', 'rhel', 'fedora'
+ packages = ['ceph']
+ packages += debug_packages(packages) if node['ceph']['install_debug']
+ default['ceph']['mon']['packages'] = packages
+else
+ default['ceph']['mon']['packages'] = []
+end
--- /dev/null
+include_attribute 'ceph'
+
+default['ceph']['osd']['init_style'] = node['ceph']['init_style']
+
+default['ceph']['osd']['secret_file'] = '/etc/chef/secrets/ceph_osd'
+
+case node['platform_family']
+when 'debian', 'rhel', 'fedora'
+ packages = ['ceph']
+ packages += debug_packages(packages) if node['ceph']['install_debug']
+ default['ceph']['osd']['packages'] = packages
+else
+ default['ceph']['osd']['packages'] = []
+end
--- /dev/null
+#
+# Cookbook Name:: ceph
+# Attributes:: radosgw
+#
+# Copyright 2011, DreamHost Web Hosting
+#
+# 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_attribute 'ceph'
+
+default['ceph']['radosgw']['api_fqdn'] = 'localhost'
+default['ceph']['radosgw']['admin_email'] = 'admin@example.com'
+default['ceph']['radosgw']['rgw_addr'] = '*:80'
+default['ceph']['radosgw']['rgw_port'] = false
+default['ceph']['radosgw']['webserver_companion'] = 'apache2' # can be false
+default['ceph']['radosgw']['use_apache_fork'] = true
+default['ceph']['radosgw']['init_style'] = node['ceph']['init_style']
+
+case node['platform_family']
+when 'debian'
+ packages = ['radosgw']
+ packages += debug_packages(packages) if node['ceph']['install_debug']
+ default['ceph']['radosgw']['packages'] = packages
+when 'rhel', 'fedora', 'suse'
+ default['ceph']['radosgw']['packages'] = ['ceph-radosgw']
+else
+ default['ceph']['radosgw']['packages'] = []
+end
--- /dev/null
+case node['platform_family']
+when 'debian', 'suse'
+ default['ceph']['radosgw']['apache2']['packages'] = ['libapache2-mod-fastcgi']
+when 'rhel', 'fedora'
+ default['ceph']['radosgw']['apache2']['packages'] = ['mod_fastcgi']
+end
--- /dev/null
+default['ceph']['branch'] = 'stable' # Can be stable, testing or dev.
+# Major release version to install or gitbuilder branch
+default['ceph']['version'] = 'dumpling'
+default['ceph']['el_add_epel'] = true
+default['ceph']['repo_url'] = 'http://ceph.com'
+default['ceph']['extras_repo_url'] = 'http://ceph.com/packages/ceph-extras'
+default['ceph']['extras_repo'] = false
+
+case node['platform_family']
+when 'debian'
+ # Debian/Ubuntu default repositories
+ default['ceph']['debian']['stable']['repository'] = "#{node['ceph']['repo_url']}/debian-#{node['ceph']['version']}/"
+ default['ceph']['debian']['stable']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc'
+ default['ceph']['debian']['testing']['repository'] = "#{node['ceph']['repo_url']}/debian-testing/"
+ default['ceph']['debian']['testing']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc'
+ default['ceph']['debian']['dev']['repository'] = "http://gitbuilder.ceph.com/ceph-deb-#{node['lsb']['codename']}-x86_64-basic/ref/#{node['ceph']['version']}"
+ default['ceph']['debian']['dev']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/autobuild.asc'
+ default['ceph']['debian']['extras']['repository'] = "#{node['ceph']['extras_repo_url']}/debian/"
+ default['ceph']['debian']['extras']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc'
+when 'rhel'
+ # Redhat/CentOS default repositories
+ default['ceph']['rhel']['stable']['repository'] = "#{node['ceph']['repo_url']}/rpm-#{node['ceph']['version']}/el6/x86_64/"
+ default['ceph']['rhel']['stable']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc'
+ default['ceph']['rhel']['testing']['repository'] = "#{node['ceph']['repo_url']}/rpm-testing/el6/x86_64/"
+ default['ceph']['rhel']['testing']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc'
+ default['ceph']['rhel']['dev']['repository'] = "http://gitbuilder.ceph.com/ceph-rpm-centos6-x86_64-basic/ref/#{node['ceph']['version']}/x86_64/"
+ default['ceph']['rhel']['dev']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/autobuild.asc'
+ default['ceph']['rhel']['extras']['repository'] = "#{node['ceph']['extras_repo_url']}/rpm/rhel6/x86_64/"
+ default['ceph']['rhel']['extras']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc'
+when 'fedora'
+ # Fedora default repositories
+ default['ceph']['fedora']['stable']['repository'] = "#{node['ceph']['repo_url']}/rpm-#{node['ceph']['version']}/fc#{node['platform_version']}/x86_64/"
+ default['ceph']['fedora']['stable']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc'
+ default['ceph']['fedora']['testing']['repository'] = "#{node['ceph']['repo_url']}/rpm-testing/fc#{node['platform_version']}/x86_64/"
+ default['ceph']['fedora']['testing']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc'
+ default['ceph']['fedora']['dev']['repository'] = "http://gitbuilder.ceph.com/ceph-rpm-fc#{node['platform_version']}-x86_64-basic/ref/#{node['ceph']['version']}/RPMS/x86_64/"
+ default['ceph']['fedora']['dev']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/autobuild.asc'
+ default['ceph']['fedora']['extras']['repository'] = "#{node['ceph']['extras_repo_url']}/rpm/fedora#{node['platform_version']}/x86_64/"
+ default['ceph']['fedora']['extras']['repository_key'] = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc'
+when 'suse'
+ # (Open)SuSE default repositories
+ # Chef doesn't make a difference between suse and opensuse
+ suse = Mixlib::ShellOut.new("head -n1 /etc/SuSE-release| awk '{print $1}'").run_command.stdout.chomp.downcase
+ suse = 'sles' if suse == 'suse'
+ suse_version = suse << Mixlib::ShellOut.new("grep VERSION /etc/SuSE-release | awk -F'= ' '{print $2}'").run_command.stdout.chomp
+
+ default['ceph']['suse']['stable']['repository'] = "#{node['ceph']['repo_url']}/rpm-#{node['ceph']['version']}/#{suse_version}/x86_64/ceph-release-1-0.#{suse_version}.noarch.rpm"
+ default['ceph']['suse']['testing']['repository'] = "#{node['ceph']['repo_url']}/rpm-testing/#{suse_version}/x86_64/ceph-release-1-0.#{suse_version}.noarch.rpm"
+ default['ceph']['suse']['extras']['repository'] = "#{node['ceph']['extras_repo_url']}/rpm/#{suse_version}/x86_64/"
+else
+ fail "#{node['platform_family']} is not supported"
+end
--- /dev/null
+roles:
+- ceph-mds:
+- ceph-mon:
+- ceph-osd:
+- ceph-radosgw:
+- ceph-tgt:
--- /dev/null
+require 'json'
+
+def crowbar?
+ !defined?(Chef::Recipe::Barclamp).nil?
+end
+
+def mon_env_search_string
+ if crowbar?
+ mon_roles = search(:role, 'name:crowbar-* AND run_list:role\[ceph-mon\]')
+ unless mon_roles.empty?
+ search_string = mon_roles.map { |role_object| 'roles:' + role_object.name }.join(' OR ')
+ search_string = "(#{search_string}) AND ceph_config_environment:#{node['ceph']['config']['environment']}"
+ end
+ else
+ search_string = 'ceph_is_mon:true'
+ if node['ceph']['search_environment'].is_a?(String)
+ # search for nodes with this particular env
+ search_string += " AND chef_environment:#{node['ceph']['search_environment']}"
+ elsif node['ceph']['search_environment']
+ # search for any nodes with this environment
+ search_string += " AND chef_environment:#{node.chef_environment}"
+ else
+ # search for any mon nodes
+ end
+ end
+ search_string
+end
+
+def mon_nodes
+ search_string = mon_env_search_string
+
+ if use_cephx? && !node['ceph']['encrypted_data_bags']
+ search_string = "(#{search_string}) AND (ceph_bootstrap_osd_key:*)"
+ end
+ search(:node, search_string)
+end
+
+def osd_secret
+ if node['ceph']['encrypted_data_bags']
+ secret = Chef::EncryptedDataBagItem.load_secret(node['ceph']['osd']['secret_file'])
+ return Chef::EncryptedDataBagItem.load('ceph', 'osd', secret)['secret']
+ else
+ return mon_nodes[0]['ceph']['bootstrap_osd_key']
+ end
+end
+
+# If public_network is specified with one or more networks, we need to
+# search for a matching monitor IP in the node environment.
+# 1. For each public network specified:
+# a. We look if the network is IPv6 or IPv4
+# b. We look for a route matching the network
+# c. If we found match, we return the IP with the port
+def find_node_ip_in_network(network, nodeish = nil)
+ require 'netaddr'
+ nodeish = node unless nodeish
+ network.split(/\s*,\s*/).each do |n|
+ net = NetAddr::CIDR.create(n)
+ nodeish['network']['interfaces'].each do |_iface, addrs|
+ addresses = addrs['addresses'] || []
+ addresses.each do |ip, params|
+ return ip_address_to_ceph_address(ip, params) if ip_address_in_network?(ip, params, net)
+ end
+ end
+ end
+ nil
+end
+
+def ip_address_in_network?(ip, params, net)
+ # Find the IP on this interface that matches the public_network
+ # Uses a few heuristics to find the primary IP that ceph would bind to
+ # Most secondary IPs never have a broadcast value set
+ # Other secondary IPs have a prefix of /32
+ # Match the prefix that we want from the public_network prefix
+ if params['family'] == 'inet' && net.version == 4
+ ip4_address_in_network?(ip, params, net)
+ elsif params['family'] == 'inet6' && net.version == 6
+ ip6_address_in_network?(ip, params, net)
+ else
+ false
+ end
+end
+
+def ip4_address_in_network?(ip, params, net)
+ net.contains?(ip) && params.key?('broadcast') && params['prefixlen'].to_i == net.bits
+end
+
+def ip6_address_in_network?(ip, params, net)
+ net.contains?(ip) && params['prefixlen'].to_i == net.bits
+end
+
+def ip_address_to_ceph_address(ip, params)
+ if params['family'].eql?('inet')
+ return "#{ip}:6789"
+ elsif params['family'].eql?('inet6')
+ return "[#{ip}]:6789"
+ end
+end
+
+def mon_addresses
+ mon_ips = []
+
+ if File.exist?("/var/run/ceph/ceph-mon.#{node['hostname']}.asok")
+ mon_ips = quorum_members_ips
+ else
+ mons = []
+ # make sure if this node runs ceph-mon, it's always included even if
+ # search is laggy; put it first in the hopes that clients will talk
+ # primarily to local node
+ mons << node if node['ceph']['is_mon']
+
+ mons += mon_nodes
+ if crowbar?
+ mon_ips = mons.map { |node| Chef::Recipe::Barclamp::Inventory.get_network_by_type(node, 'admin').address }
+ else
+ if node['ceph']['config']['global'] && node['ceph']['config']['global']['public network']
+ mon_ips = mons.map { |nodeish| find_node_ip_in_network(node['ceph']['config']['global']['public network'], nodeish) }
+ else
+ mon_ips = mons.map { |node| node['ipaddress'] + ':6789' }
+ end
+ end
+ end
+ mon_ips.reject { |m| m.nil? }.uniq
+end
+
+def mon_secret
+ if node['ceph']['encrypted_data_bags']
+ secret = Chef::EncryptedDataBagItem.load_secret(node['ceph']['mon']['secret_file'])
+ Chef::EncryptedDataBagItem.load('ceph', 'mon', secret)['secret']
+ elsif !mon_nodes.empty?
+ mon_nodes[0]['ceph']['monitor-secret']
+ elsif node['ceph']['monitor-secret']
+ node['ceph']['monitor-secret']
+ else
+ Chef::Log.info('No monitor secret found')
+ nil
+ end
+end
+
+def quorum_members_ips
+ mon_ips = []
+ cmd = Mixlib::ShellOut.new("ceph --admin-daemon /var/run/ceph/ceph-mon.#{node['hostname']}.asok mon_status")
+ cmd.run_command
+ cmd.error!
+
+ mons = JSON.parse(cmd.stdout)['monmap']['mons']
+ mons.each do |k|
+ mon_ips.push(k['addr'][0..-3])
+ end
+ mon_ips
+end
+
+QUORUM_STATES = %w(leader, peon)
+def quorum?
+ # "ceph auth get-or-create-key" would hang if the monitor wasn't
+ # in quorum yet, which is highly likely on the first run. This
+ # helper lets us delay the key generation into the next
+ # chef-client run, instead of hanging.
+ #
+ # Also, as the UNIX domain socket connection has no timeout logic
+ # in the ceph tool, this exits immediately if the ceph-mon is not
+ # running for any reason; trying to connect via TCP/IP would wait
+ # for a relatively long timeout.
+
+ cmd = Mixlib::ShellOut.new("ceph --admin-daemon /var/run/ceph/ceph-mon.#{node['hostname']}.asok mon_status")
+ cmd.run_command
+ cmd.error!
+
+ state = JSON.parse(cmd.stdout)['state']
+ QUORUM_STATES.include?(state)
+end
+
+# Cephx is on by default, but users can disable it.
+# type can be one of 3 values: cluster, service, or client. If the value is none of the above, set it to cluster
+def use_cephx?(type = nil)
+ # Verify type is valid
+ type = 'cluster' if %w(cluster service client).index(type).nil?
+
+ # CephX is enabled if it's not configured at all, or explicity enabled
+ node['ceph']['config'].nil? ||
+ node['ceph']['config']['global'].nil? ||
+ node['ceph']['config']['global']["auth #{type} required"] == 'cephx'
+end
--- /dev/null
+def debug_packages(packages)
+ packages.map { |x| x + debug_ext }
+end
+
+def debug_ext
+ case node['platform_family']
+ when 'debian'
+ '-dbg'
+ when 'rhel', 'fedora'
+ '-debug'
+ else
+ ''
+ end
+end
--- /dev/null
+name 'ceph'
+maintainer 'Kyle Bader'
+maintainer_email 'kyle.bader@dreamhost.com'
+license 'Apache 2.0'
+description 'Installs/Configures the Ceph distributed filesystem'
+long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
+version '0.2.1'
+
+depends 'apache2', '>= 1.1.12'
+depends 'apt'
+depends 'yum', '>= 3.0'
+depends 'yum-epel'
--- /dev/null
+def create_client
+ # Client settings
+ client_name = "cephfs.#{node['hostname']}"
+ filename = "/etc/ceph/ceph.client.#{client_name}.secret"
+
+ name = 'cephfs'
+ ceph_client name do
+ filename filename
+ caps('mon' => 'allow r', 'osd' => 'allow rw', 'mds' => 'allow')
+ as_keyring false
+ end
+end
+
+def manage_mount(directory, subdir, use_fuse, action)
+ # Client settings
+ client_name = "cephfs.#{node['hostname']}"
+ filename = "/etc/ceph/ceph.client.#{client_name}.secret"
+
+ if use_fuse
+ if subdir != '/'
+ Chef::Application.fatal!("Can't use a subdir with fuse mounts yet")
+ end
+ mount "#{action} #{directory}" do
+ mount_point directory
+ fstype 'fuse.ceph'
+ # needs two slashes to indicate a network mount to chef
+ device "conf=//etc/ceph/ceph.conf,id=#{client_name},keyfile=#{filename}"
+ options 'defaults,_netdev'
+ dump 0
+ pass 0
+ action action
+ end
+ else
+ mons = mon_addresses.join(',') + ':' + subdir
+ mount "#{action} #{directory}" do
+ mount_point directory
+ fstype 'ceph'
+ device mons
+ options "_netdev,name=#{client_name},secretfile=#{filename}"
+ dump 0
+ pass 0
+ action action
+ end
+ end
+end
+
+def whyrun_supported?
+ true
+end
+
+def create_mount(action)
+ create_client
+ directory @new_resource.directory
+ manage_mount(@new_resource.directory, @new_resource.cephfs_subdir, @new_resource.use_fuse, action)
+end
+
+action :mount do
+ converge_by("Creating cephfs mount at #{@new_resource.directory}") do
+ create_mount(:mount)
+ end
+end
+
+action :remount do
+ converge_by("Remounting cephfs mount at #{@new_resource.directory}") do
+ create_mount(:remount)
+ end
+end
+
+action :umount do
+ converge_by("Unmounting cephfs mount at #{@new_resource.directory}") do
+ manage_mount(@new_resource.directory, @new_resource.cephfs_subdir, @new_resource.use_fuse, :umount)
+ end
+end
+
+action :enable do
+ converge_by("Enabling cephfs mount at #{@new_resource.directory}") do
+ create_mount(:enable)
+ end
+end
+
+action :disable do
+ converge_by("Disabling cephfs mount at #{@new_resource.directory}") do
+ manage_mount(@new_resource.directory, @new_resource.cephfs_subdir, @new_resource.use_fuse, :disable)
+ end
+end
--- /dev/null
+use_inline_resources
+
+def whyrun_supported?
+ true
+end
+
+action :add do
+ current_resource = @current_resource
+ filename = @current_resource.filename
+ keyname = @current_resource.keyname
+ caps = @new_resource.caps.map { |k, v| "#{k} '#{v}'" }.join(' ')
+ owner = @new_resource.owner
+ group = @new_resource.group
+ mode = @new_resource.mode
+ unless @current_resource.caps_match
+ converge_by("Set caps for #{@new_resource}") do
+ auth_set_key(keyname, caps)
+ current_resource.key = get_key(keyname)
+
+ end
+ end
+ # update the key in the file
+ file filename do
+ content file_content
+ owner owner
+ group group
+ mode mode
+ end
+
+end
+
+def load_current_resource
+ @current_resource = Chef::Resource::CephClient.new(@new_resource.name)
+ @current_resource.name(@new_resource.name)
+ @current_resource.as_keyring(@new_resource.as_keyring)
+ @current_resource.keyname(@new_resource.keyname || "client.#{current_resource.name}.#{node['hostname']}")
+ @current_resource.caps(get_caps(@current_resource.keyname))
+ default_filename = "/etc/ceph/ceph.client.#{@new_resource.name}.#{node['hostname']}.#{@new_resource.as_keyring ? 'keyring' : 'secret'}"
+ @current_resource.filename(@new_resource.filename || default_filename)
+ @current_resource.key = get_key(@current_resource.keyname)
+ @current_resource.caps_match = true if @current_resource.caps == @new_resource.caps
+end
+
+def file_content
+ @current_resource.as_keyring ? "[#{@current_resource.keyname}]\n\tkey = #{@current_resource.key}\n" : @current_resource.key
+end
+
+def get_key(keyname)
+ cmd = "ceph auth print_key #{keyname} --name mon. --key='#{mon_secret}'"
+ Mixlib::ShellOut.new(cmd).run_command.stdout
+end
+
+def get_caps(keyname)
+ caps = {}
+ cmd = "ceph auth get #{keyname} --name mon. --key='#{mon_secret}'"
+ output = Mixlib::ShellOut.new(cmd).run_command.stdout
+ output.scan(/caps\s*(\S+)\s*=\s*"([^"]*)"/) { |k, v| caps[k] = v }
+ caps
+end
+
+def auth_set_key(keyname, caps)
+ secret = mon_secret
+ # try to add the key
+ cmd = "ceph auth get-or-create #{keyname} #{caps} --name mon. --key='#{secret}'"
+ get_or_create = Mixlib::ShellOut.new(cmd)
+ get_or_create.run_command
+ if get_or_create.stderr.scan(/EINVAL.*but cap.*does not match/)
+ Chef::Log.info('Deleting old key with incorrect caps')
+ # delete an old key if it exists and is wrong
+ Mixlib::ShellOut.new("ceph auth del #{keyname} --name mon. --key='#{secret}'").run_command
+ # try to create again
+ get_or_create = Mixlib::ShellOut.new(cmd)
+ get_or_create.run_command
+ end
+ get_or_create.error!
+end
--- /dev/null
+include_recipe 'ceph::_common_install'
+
+# Tools needed by cookbook
+node['ceph']['packages'].each do |pck|
+ package pck
+end
+
+chef_gem 'netaddr'
--- /dev/null
+include_recipe 'ceph::repo' if node['ceph']['install_repo']
--- /dev/null
+
+include_recipe 'ceph::mon'
+include_recipe 'ceph::osd'
+include_recipe 'ceph::mds'
+include_recipe 'ceph::cephfs'
+include_recipe 'ceph::radosgw'
--- /dev/null
+
+include_recipe 'apt'
+
+branch = node['ceph']['branch']
+
+distribution_codename =
+case node['lsb']['codename']
+when 'jessie' then 'sid'
+else node['lsb']['codename']
+end
+
+apt_preference 'ceph_repo' do
+ package_name '*'
+ pin 'origin "ceph.com"'
+ pin_priority '1001'
+end
+
+apt_repository 'ceph' do
+ repo_name 'ceph'
+ uri node['ceph']['debian'][branch]['repository']
+ distribution distribution_codename
+ components ['main']
+ key node['ceph']['debian'][branch]['repository_key']
+end
+
+apt_repository 'ceph-extras' do
+ repo_name 'ceph-extras'
+ uri node['ceph']['debian']['extras']['repository']
+ distribution distribution_codename
+ components ['main']
+ key node['ceph']['debian']['extras']['repository_key']
+ only_if { node['ceph']['extras_repo'] }
+end
--- /dev/null
+#
+# Author:: Kyle Bader <kyle.bader@dreamhost.com>
+# Cookbook Name:: ceph
+# Recipe:: cephfs
+#
+# Copyright 2011, DreamHost Web Hosting
+#
+# 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.
+
+requires_fuse =
+ case node['platform']
+ when 'debian'
+ node['platform_version'].to_f < 7.0
+ when 'ubuntu'
+ node['platform_version'].to_f < 12.04
+ when 'redhat'
+ node['platform_version'].to_f < 7.0
+ when 'fedora'
+ node['platform_version'].to_f < 17.0
+ else
+ true
+end
+
+ceph_cephfs node['ceph']['cephfs_mount'] do
+ use_fuse requires_fuse
+ action [:mount, :enable]
+end
--- /dev/null
+include_recipe 'ceph::_common_install'
+
+node['ceph']['cephfs']['packages'].each do |pck|
+ package pck
+end
--- /dev/null
+# fail 'mon_initial_members must be set in config' if node['ceph']['config']['mon_initial_members'].nil?
+
+unless node['ceph']['config']['fsid']
+ Chef::Log.warn('We are genereting a new uuid for fsid')
+ require 'securerandom'
+ node.set['ceph']['config']['fsid'] = SecureRandom.uuid
+ node.save
+end
+
+directory '/etc/ceph' do
+ owner 'root'
+ group 'root'
+ mode '0755'
+ action :create
+end
+
+template '/etc/ceph/ceph.conf' do
+ source 'ceph.conf.erb'
+ variables lazy {
+ {
+ :mon_addresses => mon_addresses,
+ :is_rgw => node['ceph']['is_radosgw']
+ }
+ }
+ mode '0644'
+end
--- /dev/null
+#
+# Author:: Kyle Bader <kyle.bader@dreamhost.com>
+# Cookbook Name:: ceph
+# Recipe:: default
+#
+# Copyright 2011, DreamHost Web Hosting
+#
+# 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.
+
+packages = []
+
+case node['platform_family']
+when 'debian'
+ packages = %w(
+ ceph
+ ceph-common
+ )
+
+ if node['ceph']['install_debug']
+ packages_dbg = %w(
+ ceph-dbg
+ ceph-common-dbg
+ )
+ packages += packages_dbg
+ end
+when 'rhel', 'fedora'
+ packages = %w(
+ ceph
+ )
+
+ if node['ceph']['install_debug']
+ packages_dbg = %w(
+ ceph-debug
+ )
+ packages += packages_dbg
+ end
+end
+
+packages.each do |pkg|
+ package pkg do
+ action :install
+ end
+end
--- /dev/null
+#
+# Author:: Kyle Bader <kyle.bader@dreamhost.com>
+# Cookbook Name:: ceph
+# Recipe:: mds
+#
+# Copyright 2011, DreamHost Web Hosting
+#
+# 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 'ceph::_common'
+include_recipe 'ceph::mds_install'
+include_recipe 'ceph::conf'
+
+cluster = 'ceph'
+
+directory "/var/lib/ceph/mds/#{cluster}-#{node['hostname']}" do
+ owner 'root'
+ group 'root'
+ mode 00755
+ recursive true
+ action :create
+end
+
+ceph_client 'mds' do
+ caps('osd' => 'allow *', 'mon' => 'allow rwx')
+ keyname "mds.#{node['hostname']}"
+ filename "/var/lib/ceph/mds/#{cluster}-#{node['hostname']}/keyring"
+end
+
+file "/var/lib/ceph/mds/#{cluster}-#{node['hostname']}/done" do
+ owner 'root'
+ group 'root'
+ mode 00644
+end
+
+service_type = node['ceph']['osd']['init_style']
+
+case service_type
+when 'upstart'
+ filename = 'upstart'
+else
+ filename = 'sysvinit'
+end
+file "/var/lib/ceph/mds/#{cluster}-#{node['hostname']}/#{filename}" do
+ owner 'root'
+ group 'root'
+ mode 00644
+end
+
+service 'ceph_mds' do
+ case service_type
+ when 'upstart'
+ service_name 'ceph-mds-all-starter'
+ provider Chef::Provider::Service::Upstart
+ else
+ service_name 'ceph'
+ end
+ action [:enable, :start]
+ supports :restart => true
+end
--- /dev/null
+include_recipe 'ceph::_common_install'
+
+node['ceph']['mds']['packages'].each do |pck|
+ package pck
+end
--- /dev/null
+# This recipe creates a monitor cluster
+#
+# You should never change the mon default path or
+# the keyring path.
+# Don't change the cluster name either
+# Default path for mon data: /var/lib/ceph/mon/$cluster-$id/
+# which will be /var/lib/ceph/mon/ceph-`hostname`/
+# This path is used by upstart. If changed, upstart won't
+# start the monitor
+# The keyring files are created using the following pattern:
+# /etc/ceph/$cluster.client.$name.keyring
+# e.g. /etc/ceph/ceph.client.admin.keyring
+# The bootstrap-osd and bootstrap-mds keyring are a bit
+# different and are created in
+# /var/lib/ceph/bootstrap-{osd,mds}/ceph.keyring
+
+node.default['ceph']['is_mon'] = true
+
+include_recipe 'ceph::_common'
+include_recipe 'ceph::mon_install'
+include_recipe 'ceph::conf'
+
+service_type = node['ceph']['mon']['init_style']
+
+directory '/var/run/ceph' do
+ owner 'root'
+ group 'root'
+ mode 00755
+ recursive true
+ action :create
+end
+
+directory "/var/lib/ceph/mon/ceph-#{node['hostname']}" do
+ owner 'root'
+ group 'root'
+ mode 00755
+ recursive true
+ action :create
+end
+
+# TODO: cluster name
+cluster = 'ceph'
+
+unless File.exist?("/var/lib/ceph/mon/ceph-#{node['hostname']}/done")
+ keyring = "#{Chef::Config[:file_cache_path]}/#{cluster}-#{node['hostname']}.mon.keyring"
+
+ execute 'format mon-secret as keyring' do
+ command lazy { "ceph-authtool '#{keyring}' --create-keyring --name=mon. --add-key='#{mon_secret}' --cap mon 'allow *'" }
+ creates "#{Chef::Config[:file_cache_path]}/#{cluster}-#{node['hostname']}.mon.keyring"
+ only_if { mon_secret }
+ end
+
+ execute 'generate mon-secret as keyring' do
+ command "ceph-authtool '#{keyring}' --create-keyring --name=mon. --gen-key --cap mon 'allow *'"
+ creates "#{Chef::Config[:file_cache_path]}/#{cluster}-#{node['hostname']}.mon.keyring"
+ not_if { mon_secret }
+ notifies :create, 'ruby_block[save mon_secret]', :immediately
+ end
+
+ ruby_block 'save mon_secret' do
+ block do
+ fetch = Mixlib::ShellOut.new("ceph-authtool '#{keyring}' --print-key --name=mon.")
+ fetch.run_command
+ key = fetch.stdout
+ node.set['ceph']['monitor-secret'] = key
+ node.save
+ end
+ action :nothing
+ end
+
+ execute 'ceph-mon mkfs' do
+ command "ceph-mon --mkfs -i #{node['hostname']} --keyring '#{keyring}'"
+ end
+
+ ruby_block 'finalise' do
+ block do
+ ['done', service_type].each do |ack|
+ ::File.open("/var/lib/ceph/mon/ceph-#{node['hostname']}/#{ack}", 'w').close
+ end
+ end
+ end
+end
+
+if service_type == 'upstart'
+ service 'ceph-mon' do
+ provider Chef::Provider::Service::Upstart
+ action :enable
+ end
+ service 'ceph-mon-all' do
+ provider Chef::Provider::Service::Upstart
+ supports :status => true
+ action [:enable, :start]
+ end
+end
+
+service 'ceph_mon' do
+ case service_type
+ when 'upstart'
+ service_name 'ceph-mon-all-starter'
+ provider Chef::Provider::Service::Upstart
+ else
+ service_name 'ceph'
+ end
+ supports :restart => true, :status => true
+ action [:enable, :start]
+end
+
+mon_addresses.each do |addr|
+ execute "peer #{addr}" do
+ command "ceph --admin-daemon '/var/run/ceph/ceph-mon.#{node['hostname']}.asok' add_bootstrap_peer_hint #{addr}"
+ ignore_failure true
+ end
+end
+
+# The key is going to be automatically created, We store it when it is created
+# If we're storing keys in encrypted data bags, then they've already been generated above
+if use_cephx? && !node['ceph']['encrypted_data_bags']
+ ruby_block 'get osd-bootstrap keyring' do
+ block do
+ run_out = ''
+ while run_out.empty?
+ run_out = Mixlib::ShellOut.new('ceph auth get-key client.bootstrap-osd').run_command.stdout.strip
+ sleep 2
+ end
+ node.set['ceph']['bootstrap_osd_key'] = run_out
+ node.save
+ end
+ not_if { node['ceph']['bootstrap_osd_key'] }
+ end
+end
--- /dev/null
+include_recipe 'ceph::_common_install'
+
+node['ceph']['mon']['packages'].each do |pck|
+ package pck
+end
--- /dev/null
+#
+# Author:: Kyle Bader <kyle.bader@dreamhost.com>
+# Cookbook Name:: ceph
+# Recipe:: osd
+#
+# Copyright 2011, DreamHost Web Hosting
+#
+# 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 allows bootstrapping new osds, with help from mon
+# Sample environment:
+# #knife node edit ceph1
+# "osd_devices": [
+# {
+# "device": "/dev/sdc"
+# },
+# {
+# "device": "/dev/sdd",
+# "dmcrypt": true,
+# "journal": "/dev/sdd"
+# }
+# ]
+
+include_recipe 'ceph::_common'
+include_recipe 'ceph::osd_install'
+include_recipe 'ceph::conf'
+
+package 'gdisk' do
+ action :upgrade
+end
+
+package 'cryptsetup' do
+ action :upgrade
+ only_if { node['dmcrypt'] }
+end
+
+service_type = node['ceph']['osd']['init_style']
+
+directory '/var/lib/ceph/bootstrap-osd' do
+ owner 'root'
+ group 'root'
+ mode '0755'
+end
+
+# TODO: cluster name
+cluster = 'ceph'
+
+execute 'format bootstrap-osd as keyring' do
+ command lazy { "ceph-authtool '/var/lib/ceph/bootstrap-osd/#{cluster}.keyring' --create-keyring --name=client.bootstrap-osd --add-key='#{osd_secret}'" }
+ creates "/var/lib/ceph/bootstrap-osd/#{cluster}.keyring"
+ only_if { osd_secret }
+end
+
+if crowbar?
+ node['crowbar']['disks'].each do |disk, _data|
+ execute "ceph-disk-prepare #{disk}" do
+ command "ceph-disk-prepare /dev/#{disk}"
+ only_if { node['crowbar']['disks'][disk]['usage'] == 'Storage' }
+ notifies :run, 'execute[udev trigger]', :immediately
+ end
+
+ ruby_block "set disk usage for #{disk}" do
+ block do
+ node.set['crowbar']['disks'][disk]['usage'] = 'ceph-osd'
+ node.save
+ end
+ end
+ end
+
+ execute 'udev trigger' do
+ command 'udevadm trigger --subsystem-match=block --action=add'
+ action :nothing
+ end
+else
+ # Calling ceph-disk-prepare is sufficient for deploying an OSD
+ # After ceph-disk-prepare finishes, the new device will be caught
+ # by udev which will run ceph-disk-activate on it (udev will map
+ # the devices if dm-crypt is used).
+ # IMPORTANT:
+ # - Always use the default path for OSD (i.e. /var/lib/ceph/
+ # osd/$cluster-$id)
+ # - $cluster should always be ceph
+ # - The --dmcrypt option will be available starting w/ Cuttlefish
+ if node['ceph']['osd_devices']
+ devices = node['ceph']['osd_devices']
+
+ devices = Hash[(0...devices.size).zip devices] unless devices.kind_of? Hash
+
+ devices.each do |index, osd_device|
+ unless osd_device['status'].nil?
+ Log.info("osd: osd_device #{osd_device} has already been setup.")
+ next
+ end
+
+ directory osd_device['device'] do # ~FC022
+ owner 'root'
+ group 'root'
+ recursive true
+ only_if { osd_device['type'] == 'directory' }
+ end
+
+ dmcrypt = osd_device['encrypted'] == true ? '--dmcrypt' : ''
+
+ execute "ceph-disk-prepare on #{osd_device['device']}" do
+ command "ceph-disk-prepare #{dmcrypt} #{osd_device['device']} #{osd_device['journal']}"
+ action :run
+ notifies :create, "ruby_block[save osd_device status #{index}]", :immediately
+ end
+
+ execute "ceph-disk-activate #{osd_device['device']}" do
+ only_if { osd_device['type'] == 'directory' }
+ end
+
+ # we add this status to the node env
+ # so that we can implement recreate
+ # and/or delete functionalities in the
+ # future.
+ ruby_block "save osd_device status #{index}" do
+ block do
+ node.normal['ceph']['osd_devices'][index]['status'] = 'deployed'
+ node.save
+ end
+ action :nothing
+ end
+ end
+ service 'ceph_osd' do
+ case service_type
+ when 'upstart'
+ service_name 'ceph-osd-all-starter'
+ provider Chef::Provider::Service::Upstart
+ else
+ service_name 'ceph'
+ end
+ action [:enable, :start]
+ supports :restart => true
+ end
+ else
+ Log.info('node["ceph"]["osd_devices"] empty')
+ end
+end
--- /dev/null
+include_recipe 'ceph::_common_install'
+
+node['ceph']['osd']['packages'].each do |pck|
+ package pck
+end
--- /dev/null
+#
+# Author:: Kyle Bader <kyle.bader@dreamhost.com>
+# Cookbook Name:: ceph
+# Recipe:: radosgw
+#
+# Copyright 2011, DreamHost Web Hosting
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+node.default['ceph']['is_radosgw'] = true
+
+include_recipe 'ceph::_common'
+include_recipe 'ceph::radosgw_install'
+include_recipe 'ceph::conf'
+
+if !::File.exist?("/var/lib/ceph/radosgw/ceph-radosgw.#{node['hostname']}/done")
+ if node['ceph']['radosgw']['webserver_companion']
+ include_recipe "ceph::radosgw_#{node['ceph']['radosgw']['webserver_companion']}"
+ end
+
+ ceph_client 'radosgw' do
+ caps('mon' => 'allow rw', 'osd' => 'allow rwx')
+ end
+
+ directory "/var/lib/ceph/radosgw/ceph-radosgw.#{node['hostname']}" do
+ recursive true
+ end
+
+ file "/var/lib/ceph/radosgw/ceph-radosgw.#{node['hostname']}/done" do
+ action :create
+ end
+
+ service 'radosgw' do
+ case node['ceph']['radosgw']['init_style']
+ when 'upstart'
+ service_name 'radosgw-all-starter'
+ provider Chef::Provider::Service::Upstart
+ else
+ if node['platform'] == 'debian'
+ service_name 'radosgw'
+ else
+ service_name 'ceph-radosgw'
+ end
+ end
+ supports :restart => true
+ action [:enable, :start]
+ end
+else
+ Log.info('Rados Gateway already deployed')
+end
--- /dev/null
+#
+# Author:: Kyle Bader <kyle.bader@dreamhost.com>
+# Cookbook Name:: ceph
+# Recipe:: radosgw_apache2
+#
+# Copyright 2011, DreamHost Web Hosting
+#
+# 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.
+
+# For EL, delete the current fastcgi configuration
+# and set the correct owners for dirs and logs
+# d_owner = d_group = 'root'
+# if node['platform_family'] == 'rhel'
+# file "#{node['apache']['dir']}/conf.d/fastcgi.conf" do
+# action :delete
+# backup false
+# end
+# d_owner = d_group = 'apache'
+# end
+
+# %W(/var/run/ceph
+# /var/lib/ceph/radosgw/ceph-radosgw.#{node['hostname']}
+# /var/lib/apache2/
+# ).each do |dir|
+# directory dir do
+# owner d_owner
+# group d_group
+# mode '0755'
+# recursive true
+# action :create
+# end
+# end
+
+include_recipe 'ceph::_common'
+include_recipe 'ceph::_common_install'
+include_recipe 'ceph::radosgw_apache2_repo'
+
+node['ceph']['radosgw']['apache2']['packages'].each do |pck|
+ package pck
+end
+
+include_recipe 'apache2'
+
+apache_module 'fastcgi' do
+ conf true
+end
+
+apache_module 'rewrite' do
+ conf false
+end
+
+web_app 'rgw' do
+ template 'rgw.conf.erb'
+ server_name node['ceph']['radosgw']['api_fqdn']
+ admin_email node['ceph']['radosgw']['admin_email']
+ ceph_rgw_addr node['ceph']['radosgw']['rgw_addr']
+end
+
+service 'apache2' do
+ action :restart
+end
+
+template '/var/www/s3gw.fcgi' do
+ source 's3gw.fcgi.erb'
+ owner 'root'
+ group 'root'
+ mode '0755'
+ variables(
+ :ceph_rgw_client => "client.radosgw.#{node['hostname']}"
+ )
+end
--- /dev/null
+if node['ceph']['radosgw']['use_apache_fork'] == true
+ if node.platform_family?('debian') &&
+ %w(precise quantal raring saucy trusty squeeze wheezy).include?(node['lsb']['codename'])
+ apt_repository 'ceph-apache2' do
+ repo_name 'ceph-apache2'
+ uri "http://gitbuilder.ceph.com/apache2-deb-#{node['lsb']['codename']}-x86_64-basic/ref/master"
+ distribution node['lsb']['codename']
+ components ['main']
+ key 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/autobuild.asc'
+ end
+ apt_repository 'ceph-modfastcgi' do
+ repo_name 'ceph-modfastcgi'
+ uri "http://gitbuilder.ceph.com/libapache-mod-fastcgi-deb-#{node['lsb']['codename']}-x86_64-basic/ref/master"
+ distribution node['lsb']['codename']
+ components ['main']
+ key 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/autobuild.asc'
+ end
+ elsif (node.platform_family?('fedora') && [18, 19].include?(node['platform_version'].to_i)) ||
+ (node.platform_family?('rhel') && [6].include?(node['platform_version'].to_i))
+ platform_family = node['platform_family']
+ platform_version = node['platform_version'].to_i
+ yum_repository 'ceph-apache2' do
+ baseurl "http://gitbuilder.ceph.com/apache2-rpm-#{node['platform']}#{platform_version}-x86_64-basic/ref/master"
+ gpgkey node['ceph'][platform_family]['dev']['repository_key']
+ end
+ yum_repository 'ceph-modfastcgi' do
+ baseurl "http://gitbuilder.ceph.com/mod_fastcgi-rpm-#{node['platform']}#{platform_version}-x86_64-basic/ref/master"
+ gpgkey node['ceph'][platform_family]['dev']['repository_key']
+ end
+ else
+ Log.info("Ceph's Apache and Apache FastCGI forks not available for this distribution")
+ end
+end
--- /dev/null
+include_recipe 'ceph::_common_install'
+
+node['ceph']['radosgw']['packages'].each do |pck|
+ package pck
+end
--- /dev/null
+case node['platform_family']
+when 'debian'
+ include_recipe 'ceph::apt'
+when 'rhel', 'suse', 'fedora'
+ include_recipe 'ceph::rpm'
+else
+ fail 'not supported'
+end
--- /dev/null
+platform_family = node['platform_family']
+
+case platform_family
+when 'rhel'
+ include_recipe 'yum-epel' if node['ceph']['el_add_epel']
+end
+
+branch = node['ceph']['branch']
+if branch == 'dev' && platform_family != 'centos' && platform_family != 'fedora'
+ fail "Dev branch for #{platform_family} is not yet supported"
+end
+
+yum_repository 'ceph' do
+ baseurl node['ceph'][platform_family][branch]['repository']
+ gpgkey node['ceph'][platform_family][branch]['repository_key']
+end
+
+yum_repository 'ceph-extra' do
+ baseurl node['ceph'][platform_family]['extras']['repository']
+ gpgkey node['ceph'][platform_family]['extras']['repository_key']
+ only_if { node['ceph']['extras_repo'] }
+end
+
+package 'parted' # needed by ceph-disk-prepare to run partprobe
+package 'hdparm' # used by ceph-disk activate
+package 'xfsprogs' # needed by ceph-disk-prepare to format as xfs
+if node['platform_family'] == 'rhel' && node['platform_version'].to_f > 6
+ package 'btrfs-progs' # needed to format as btrfs, in the future
+end
+if node['platform_family'] == 'rhel' && node['platform_version'].to_f < 7
+ package 'python-argparse'
+end
--- /dev/null
+#
+# Author:: Kyle Bader <kyle.bader@dreamhost.com>
+# Cookbook Name:: ceph
+# Recipe:: radosgw
+#
+# Copyright 2011, DreamHost Web Hosting
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+node.default['ceph']['extras_repo'] = true
+
+case node['platform_family']
+when 'debian'
+ packages = %w(
+ tgt
+ )
+when 'rhel', 'fedora'
+ packages = %w(
+ scsi-target-utils
+ )
+end
+
+packages.each do |pkg|
+ package pkg do
+ action :upgrade
+ end
+end
+
+include_recipe 'ceph::conf'
+# probably needs the key
+service 'tgt' do
+ if node['platform'] == 'ubuntu'
+ # The ceph version of tgt does not provide an Upstart script
+ provider Chef::Provider::Service::Init::Debian
+ service_name 'tgt'
+ else
+ service_name 'tgt'
+ end
+ supports :restart => true
+ action [:enable, :start]
+end
--- /dev/null
+actions :mount, :umount, :remount, :enable, :disable
+default_action :mount
+
+attribute :directory, :kind_of => String, :name_attribute => true, :required => true
+attribute :use_fuse, :kind_of => [TrueClass, FalseClass], :required => true, :default => true
+attribute :cephfs_subdir, :kind_of => String, :default => '/'
+
+def initialize(*args)
+ super
+ @action = :mount
+ @run_context.include_recipe 'ceph::_common'
+ @run_context.include_recipe 'ceph::cephfs_install'
+ @run_context.include_recipe 'ceph::conf'
+end
--- /dev/null
+actions :add
+default_action :add
+
+attribute :name, :kind_of => String, :name_attribute => true
+attribute :caps, :kind_of => Hash, :default => { 'mon' => 'allow r', 'osd' => 'allow r' }
+
+# Whether to store the secret in a keyring file or a plain secret file
+attribute :as_keyring, :kind_of => [TrueClass, FalseClass], :default => true
+
+# what the key should be called in the ceph cluster
+# defaults to client.#{name}.#{hostname}
+attribute :keyname, :kind_of => String
+
+# where the key should be saved
+# defaults to /etc/ceph/ceph.client.#{name}.#{hostname}.keyring if as_keyring
+# defaults to /etc/ceph/ceph.client.#{name}.#{hostname}.secret if not as_keyring
+attribute :filename, :kind_of => String
+
+# key file access creds
+attribute :owner, :kind_of => String, :default => 'root'
+attribute :group, :kind_of => String, :default => 'root'
+attribute :mode, :kind_of => [Integer, String], :default => '00640'
+
+attr_accessor :key, :caps_match
--- /dev/null
+{
+ "name": "ceph-mds",
+ "description": "Ceph Metadata Server",
+ "run_list": [
+ "recipe[ceph::repo]",
+ "recipe[ceph::mds]"
+ ]
+}
--- /dev/null
+{
+ "name": "ceph-mon",
+ "description": "Ceph Monitor",
+ "run_list": [
+ "recipe[ceph::repo]",
+ "recipe[ceph::mon]"
+ ]
+}
--- /dev/null
+{
+ "name": "ceph-osd",
+ "description": "Ceph Object Storage Device",
+ "run_list": [
+ "recipe[ceph::repo]",
+ "recipe[ceph::osd]"
+ ]
+}
--- /dev/null
+{
+ "name": "ceph-radosgw",
+ "description": "Ceph RADOS Gateway",
+ "run_list": [
+ "recipe[ceph::repo]",
+ "recipe[ceph::radosgw]"
+ ]
+}
--- /dev/null
+{
+ "name": "ceph-tgt",
+ "description": "Ceph iSCSI Target",
+ "run_list": [
+ "recipe[ceph::repo]",
+ "recipe[ceph::tgt]"
+ ]
+}
--- /dev/null
+[global]
+<% unless node['ceph']['config']['fsid'].nil? -%>
+ fsid = <%= node['ceph']['config']['fsid'] %>
+<% end -%>
+<% if node['ceph']['is_mon'] -%>
+ mon initial members = <%= node['ceph']['config']['mon_initial_members'] %>
+<% end -%>
+ mon host = <%= @mon_addresses.sort.join(', ') %>
+<% if (! node['ceph']['config']['global'].nil?) -%>
+ <% node['ceph']['config']['global'].sort.each do |k, v| %>
+ <%= k %> = <%= v %>
+ <% end %>
+<% end -%>
+
+<% if (! node['ceph']['config']['osd'].nil?) -%>
+[osd]
+ <% node['ceph']['config']['osd'].sort.each do |k, v| %>
+ <%= k %> = <%= v %>
+ <% end %>
+<% end -%>
+
+<% if (! node['ceph']['config']['mon'].nil?) -%>
+[mon]
+ <% node['ceph']['config']['mon'].sort.each do |k, v| %>
+ <%= k %> = <%= v %>
+ <% end %>
+<% end -%>
+
+<% if (! node['ceph']['config']['mds'].nil?) -%>
+[mds]
+ <% node['ceph']['config']['mds'].sort.each do |key, value| -%>
+ <%= key %> = <%= value %>
+ <% end -%>
+<% end -%>
+
+<% if (@is_rgw) -%>
+[client.radosgw.<%= node['hostname'] %>]
+ host = <%= node['hostname'] %>
+ rgw socket path = /var/run/ceph/radosgw.<%= node['hostname'] %>
+ keyring = /etc/ceph/ceph.client.radosgw.<%= node['hostname'] %>.keyring
+ log file = /var/log/ceph/radosgw.log
+<% if (! node['ceph']['config']['rgw'].nil?) -%>
+ <% node['ceph']['config']['rgw'].sort.each do |k, v| %>
+ <%= k %> = <%= v %>
+ <% end %>
+<% end -%>
+<% end -%>
+
+<% node['ceph']['config-sections'].sort.each do |name, sect| %>
+[<%= name %>]
+ <% sect.sort.each do |k, v| %>
+ <%= k %> = <%= v %>
+ <% end %>
+<% end %>
--- /dev/null
+<IfModule mod_fastcgi.c>
+ AddHandler fastcgi-script .fcgi
+ #FastCgiWrapper /usr/lib/apache2/suexec
+ FastCgiIpcDir /var/lib/apache2/fastcgi
+</IfModule>
--- /dev/null
+<% if node['ceph']['radosgw']['rgw_port'] -%>
+FastCgiExternalServer /var/www/s3gw.fcgi -host 127.0.0.1:<%= node['ceph']['radosgw']['rgw_port'] %>
+<% else -%>
+FastCgiExternalServer /var/www/s3gw.fcgi -socket /var/run/ceph/radosgw.<%= node['hostname'] %>
+<% end -%>
+
+LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" \"%{Host}i\"" proxy_combined
+LogFormat "%{X-Forwarded-For}i %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" \"%{Host}i\"" proxy_debug
+
+<VirtualHost <%= node['ceph']['radosgw']['rgw_addr'] %>>
+ ServerName <%= @params[:server_name] %>
+<% if node['ceph']['radosgw']['api_aliases'] -%>
+<% node['ceph']['radosgw']['api_aliases'].each do |api_alias| -%>
+ ServerAlias <%= api_alias %>
+<% end -%>
+<% end -%>
+ ServerAdmin <%= node["ceph"]["radosgw"]["admin_email"] %>
+ DocumentRoot /var/www/
+
+ RewriteEngine On
+ RewriteRule ^/([a-zA-Z0-9-_.]*)([/]?.*) /s3gw.fcgi?page=$1¶ms=$2&%{QUERY_STRING} [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
+
+ <IfModule mod_fastcgi.c>
+ <Directory /var/www/>
+ Options +ExecCGI
+ AllowOverride All
+ SetHandler fastcgi-script
+ Order allow,deny
+ Allow from all
+ AuthBasicAuthoritative Off
+ </Directory>
+ </IfModule>
+
+ AllowEncodedSlashes On
+
+ ErrorLog /var/log/<%= node['apache']['package'] %>/error.log
+ CustomLog /var/log/<%= node['apache']['package'] %>/rgw-access.log proxy_combined
+ ServerSignature Off
+</VirtualHost>
--- /dev/null
+#!/bin/sh
+exec /usr/bin/radosgw -c /etc/ceph/ceph.conf -n <%= @ceph_rgw_client %>
--- /dev/null
+ceph_test CHANGELOG
+===================
+
+This file is used to list changes made in each version of the ceph_test cookbook.
+
+0.1.0
+-----
+- [your_name] - Initial release of ceph_test
+
+- - -
+Check the [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) for help with Markdown.
+
+The [Github Flavored Markdown page](http://github.github.com/github-flavored-markdown/) describes the differences between markdown on github and standard markdown.
--- /dev/null
+ceph_test Cookbook
+==================
+TODO: Enter the cookbook description here.
+
+e.g.
+This cookbook makes your favorite breakfast sandwich.
+
+Requirements
+------------
+TODO: List your cookbook requirements. Be sure to include any requirements this cookbook has on platforms, libraries, other cookbooks, packages, operating systems, etc.
+
+e.g.
+#### packages
+- `toaster` - ceph_test needs toaster to brown your bagel.
+
+Attributes
+----------
+TODO: List your cookbook attributes here.
+
+e.g.
+#### ceph_test::default
+<table>
+ <tr>
+ <th>Key</th>
+ <th>Type</th>
+ <th>Description</th>
+ <th>Default</th>
+ </tr>
+ <tr>
+ <td><tt>['ceph_test']['bacon']</tt></td>
+ <td>Boolean</td>
+ <td>whether to include bacon</td>
+ <td><tt>true</tt></td>
+ </tr>
+</table>
+
+Usage
+-----
+#### ceph_test::default
+TODO: Write usage instructions for each cookbook.
+
+e.g.
+Just include `ceph_test` in your node's `run_list`:
+
+```json
+{
+ "name":"my_node",
+ "run_list": [
+ "recipe[ceph_test]"
+ ]
+}
+```
+
+Contributing
+------------
+TODO: (optional) If this is a public cookbook, detail the process for contributing. If this is a private cookbook, remove this section.
+
+e.g.
+1. Fork the repository on Github
+2. Create a named feature branch (like `add_component_x`)
+3. Write your change
+4. Write tests for your change (if applicable)
+5. Run the tests, ensuring they all pass
+6. Submit a Pull Request using Github
+
+License and Authors
+-------------------
+Authors: TODO: List authors
--- /dev/null
+default['ceph']['cephfs_mount'] = '/recipe_ceph'
--- /dev/null
+name 'ceph_test'
+maintainer 'Kyle Bader'
+maintainer_email 'kyle.bader@dreamhost.com'
+license 'Apache 2.0'
+description 'Installs/Configures ceph_test'
+long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
+version '0.1.0'
+depends 'ceph'
--- /dev/null
+#
+# Author:: Kyle Bader <kyle.bader@dreamhost.com>
+# Cookbook Name:: ceph_test
+# Recipe:: cephfs
+#
+# Copyright 2011, DreamHost Web Hosting
+#
+# 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.
+
+requires_fuse =
+ case node['platform']
+ when 'debian'
+ node['platform_version'].to_f < 7.0
+ when 'ubuntu'
+ node['platform_version'].to_f < 12.04
+ when 'redhat'
+ node['platform_version'].to_f < 7.0
+ when 'fedora'
+ node['platform_version'].to_f < 17.0
+ else
+ true
+end
+
+ceph_cephfs '/ceph' do
+ use_fuse requires_fuse
+ action [:mount, :enable]
+end
+ceph_cephfs '/ceph.fuse' do
+ use_fuse true
+ action [:mount]
+end
+ceph_cephfs '/ceph.fstab' do
+ use_fuse true
+ action [:mount, :enable]
+end
+directory '/ceph/subdir'
+file '/ceph/subdir/file' do
+ content "It works\n"
+end
+
+unless requires_fuse
+ ceph_cephfs '/subceph' do
+ use_fuse false
+ cephfs_subdir '/subdir'
+ action [:mount]
+ end
+end
--- /dev/null
+Vagrant.configure("2") do |config|
+ config.vm.box = "<%= config[:box] %>"
+ config.vm.box_url = "<%= config[:box_url ]%>"
+ (0..2).each do |d|
+ config.vm.provider :virtualbox do |vb|
+ vb.customize [ "createhd", "--filename", "disk-#{d}", "--size", "1000" ]
+ vb.customize [ "storageattach", :id, "--storagectl", "IDE Controller", "--device", (1+d)/2, "--port", (1+d)%2, "--type", "hdd", "--medium", "disk-#{d}.vdi" ]
+ end
+ end
+end
--- /dev/null
+@test "ceph is running" {
+ ceph -s | grep HEALTH
+}
+
+@test "ceph is healthy" {
+ ceph -s | grep HEALTH_OK
+}
+
+@test "cephfs is mounted" {
+ mount | grep 'type ceph'
+}
+
+@test "radosgw is running" {
+ ps auxwww | grep radosg[w]
+}
+
+@test "apache is running and listening" {
+ netstat -ln | grep -E '^\S+\s+\S+\s+\S+\s+\S+:80\s+'
+}
--- /dev/null
+@test "/recipe_ceph is mounted" {
+ grep -q -E '^\S+\s+/recipe_ceph\s+' /proc/mounts
+}
+
+@test "/ceph is mounted" {
+ grep -q -E '^\S+\s+/ceph\s+' /proc/mounts
+}
+@test "/ceph.fuse is mounted" {
+ grep -q -E '^\S+\s+/ceph\.fuse\s+fuse' /proc/mounts
+}
+@test "/ceph.fstab is mounted" {
+ grep -q -E '^\S+\s+/ceph\.fstab\s+fuse' /proc/mounts
+}
+
+@test "/ceph is in fstab" {
+ grep -q -E '^\S+\s+/ceph\s+\S+\s+\S*_netdev\S*\s' /etc/fstab
+}
+@test "/ceph.fuse is NOT in fstab" {
+ grep -v -q -E '^\S+\s+/ceph.fuse\s+' /etc/fstab
+}
+@test "/ceph.fstab is in fstab" {
+ grep -q -E '^\S+\s+/ceph.fstab\s+\S+\s+\S*_netdev\S*\s' /etc/fstab
+}
+
+@test "test file exists in /ceph" {
+ test -e /ceph/subdir/file
+ grep -q 'It works' /ceph/subdir/file
+}
+@test "test file exists in /ceph.fuse" {
+ test -e /ceph.fuse/subdir/file
+ grep -q 'It works' /ceph.fuse/subdir/file
+}
+
+# if we are using kernel cephfs
+if grep -q -E '^\S+\s+/ceph\s+ceph' /proc/mounts; then
+ @test "/subceph is mounted" {
+ grep -q -E '^\S+\s+/subceph\s+ceph' /proc/mounts
+ }
+ @test "/subceph is NOT in fstab" {
+ grep -v -q -E '^\S+\s+/subceph\s+' /etc/fstab
+ }
+ @test "test file exists in /subceph" {
+ test -e /subceph/file
+ grep -q 'It works' /subceph/file
+ }
+fi
+
--- /dev/null
+@test "ceph is installed from the official repo" {
+ cephversion=`apt-cache policy ceph | grep -B 1 ceph.com | head -n 1 | sed 's/^[^0-9]\+\([^ ]\+\).*/\1/'`
+ installedversion=`apt-cache policy ceph | grep 'Installed:' | awk '{print $2}'`
+ test "$cephversion" = "$installedversion"
+}
+