diff --git a/.kitchen.yml b/.kitchen.yml index ae7e508..2e78206 100644 --- a/.kitchen.yml +++ b/.kitchen.yml @@ -65,3 +65,4 @@ suites: run_list: - recipe[ceph::all_in_one] - recipe[ceph_test::cephfs] + - recipe[ceph_test::rbd] \ No newline at end of file diff --git a/README.md b/README.md index f7a6e7c..37558f6 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,24 @@ It assumes that connectivity to the cluster is setup and that admin credentials - :create_options - arguments for pool creation (optional) - :force - force the deletion of an exiting pool along with any data that is stored in it +### ceph\_rbd (Rados Block Device) + +The ceph\_rbd LWRP provides an easy way to create, delete, attach and detach rados block devices. + +#### Actions + +- :crete - creates an rbd with the given parameters +- :delete - deletes an rbd +- :attach - attach an existing rbd +- :detach - detach rbd + +#### Parameters + +- :name - the name of the new rbd +- :size - the size of the new rbd +- :id - the id of the client (e.g admin) +- :keyring - the path of the keyring file (e.g /etc/ceph/ceph.client.admin.keyring) + ## DEVELOPING ### Style Guide diff --git a/providers/rbd.rb b/providers/rbd.rb new file mode 100644 index 0000000..0469f7d --- /dev/null +++ b/providers/rbd.rb @@ -0,0 +1,131 @@ +# +# Cookbook Name:: ceph +# Provider:: rbd +# +# Authors:: Sandor Acs , Krisztian Gacsal +# + +def whyrun_supported? + true +end + +use_inline_resources + +action :attach do + if @current_resource.attached + Chef::Log.info "#{@new_resource} already attached - nothing to do." + else + converge_by("Attach RBD: #{@new_resource.name}") do + attach_rbd_image(@new_resource.pool, @new_resource.name, @new_resource.id, @new_resource.keyring) + end + end +end + +action :detach do + if ! @current_resource.attached + Chef::Log.info "#{@new_resource} does not attached - nothing to do." + else + converge_by("Detach RBD: #{@new_resource.name}") do + detach_rbd_image(@new_resource.pool, @new_resource.name, @new_resource.id, @new_resource.keyring) + end + end +end + +action :create do + if @current_resource.exists + Chef::Log.info "#{@new_resource} already created - nothing to do." + else + converge_by("Create RBD: #{@new_resource.name}") do + create_rbd_image(@new_resource.pool, @new_resource.name, @new_resource.size, @new_resource.id, @new_resource.keyring) + end + end +end + +action :delete do + if ! @current_resource.exists + Chef::Log.info "#{@new_resource} does not exist - nothing to do." + else + converge_by("Delete RBD: #{@new_resource.name}") do + delete_rbd_image(@new_resource.pool, @new_resource.name, @new_resource.id, @new_resource.keyring) + end + end +end + +def load_current_resource + @current_resource = Chef::Resource::CephRbd.new(@new_resource.name) + @current_resource.name(@new_resource.name) + @current_resource.pool(@new_resource.pool) + @current_resource.id(@new_resource.id) + @current_resource.keyring(@new_resource.keyring) + @current_resource.size(@new_resource.size) + @current_resource.exists = rbd_exists?(@current_resource.pool, @current_resource.name, @current_resource.id, @current_resource.keyring) + @current_resource.attached = rbd_attached?(@current_resource.pool, @current_resource.name, @current_resource.id, @current_resource.keyring) +end + +def rbd_exists?(pool, image, id, keyring) + cmd = Mixlib::ShellOut.new("rbd info #{pool}/#{image} --id #{id} --keyring=#{keyring}") + cmd.run_command + cmd.error! + Chef::Log.debug "RBD exists: #{cmd.stdout}" + true +rescue + Chef::Log.debug "RBD doesn't seem to exist: #{cmd.stderr}" + false +end + +def rbd_attached?(pool, image, id, keyring) + cmd = Mixlib::ShellOut.new("rbd showmapped --id #{id} --keyring=#{keyring}|grep '#{pool} #{image}'") + cmd.run_command + cmd.error! + Chef::Log.debug "RBD attached: #{cmd.stdout}" + true +rescue + Chef::Log.debug "RBD doesn't seem to exist: #{cmd.stderr}" + false +end + +def attach_rbd_image(pool, image, id, keyring) + ruby_block 'Add RBD to rbdmap' do + block do + rc = Chef::Util::FileEdit.new('/etc/ceph/rbdmap') + rc.insert_line_if_no_match(%r{#{pool}\/#{image}\tid=#{id},keyring=#{keyring}}, "%r{#{pool}/#{image}\tid=#{id},keyring=#{keyring}}") + rc.write_file + end + only_if { rbd_exists?(pool, image, id, keyring) } + end + + execute 'attach_rbd' do + command "rbd map #{pool}/#{image} --id #{id} --keyring #{keyring}" + only_if { rbd_exists?(pool, image, id, keyring) } + end +end + +def detach_rbd_image(pool, image, id, keyring) + execute 'detach_rbd' do + command "rbd unmap /dev/rbd/#{pool}/#{image} --id #{id} --keyring=#{keyring}" + end + + ruby_block 'Remove RBD from rbdmap' do + block do + rc = Chef::Util::FileEdit.new('/etc/ceph/rbdmap') + rc.search_file_delete_line(%r{#{pool}\/#{image}\tid=#{id},keyring=#{keyring}}) + rc.write_file + end + end +end + +def create_rbd_image(pool, image, size, id, keyring) + cmd_text = "rbd create #{image} --size #{size} --pool #{pool} --id #{id} --keyring=#{keyring}" + cmd = Mixlib::ShellOut.new(cmd_text) + cmd.run_command + cmd.error! + Chef::Log.debug "RBD image created: #{cmd.stdout}" +end + +def delete_rbd_image(pool, image, id, keyring) + cmd_text = "rbd rm #{image} --pool #{pool} --id #{id} --keyring=#{keyring}" + cmd = Mixlib::ShellOut.new(cmd_text) + cmd.run_command + cmd.error! + Chef::Log.debug "RBD image deleted: #{cmd.stdout}" +end diff --git a/recipes/rbd_images.rb b/recipes/rbd_images.rb new file mode 100644 index 0000000..a9fd233 --- /dev/null +++ b/recipes/rbd_images.rb @@ -0,0 +1,26 @@ +# +# Cookbook Name:: ceph +# Provider:: rbd +# +# Authors:: Sandor Acs , Krisztian Gacsal +# + +id = "rbd.#{node['hostname']}" +filename = "/etc/ceph/ceph.client.#{id}.secret" + +ceph_client 'rbd' do + filename filename + caps('mon' => 'allow r', 'osd' => 'allow * pool=rbd') +end + +if node['ceph']['user_rbd_images'] + node['ceph']['user_rbd_images'].each do |image| + # Create user-defined images + ceph_rbd image['name'] do + size image['size'] + id id + keyring filename + action :create + end + end +end diff --git a/resources/rbd.rb b/resources/rbd.rb new file mode 100644 index 0000000..02975d3 --- /dev/null +++ b/resources/rbd.rb @@ -0,0 +1,24 @@ +# +# Cookbook Name:: ceph +# Provider:: rbd +# +# Authors:: Sandor Acs , Krisztian Gacsal +# +actions :attach, :detach, :create, :delete +default_action :attach + +attribute :name, :name_attribute => true, :kind_of => String, :required => true + +# Name of the pool that stores the image +attribute :pool, :kind_of => String, :default => 'rbd' + +# Size of the image +attribute :size, :kind_of => [Integer, String], :default => '1024' + +# Client ID +attribute :id, :kind_of => String, :required => true + +# Client keyring +attribute :keyring, :kind_of => String, :required => true + +attr_accessor :exists, :attached diff --git a/test/cookbooks/ceph_test/attributes/ceph_rbd.rb b/test/cookbooks/ceph_test/attributes/ceph_rbd.rb new file mode 100644 index 0000000..a7ba12e --- /dev/null +++ b/test/cookbooks/ceph_test/attributes/ceph_rbd.rb @@ -0,0 +1 @@ +default['ceph']['user_rbd_images'] = [{ 'name' => 'rbd_test', 'size' => '128' }] diff --git a/test/cookbooks/ceph_test/recipes/rbd.rb b/test/cookbooks/ceph_test/recipes/rbd.rb new file mode 100644 index 0000000..765382c --- /dev/null +++ b/test/cookbooks/ceph_test/recipes/rbd.rb @@ -0,0 +1,48 @@ +# +# Cookbook Name:: ceph +# Provider:: rbd +# +# Authors:: Sandor Acs , Krisztian Gacsal +# + +execute 'start rbd kernel module' do + command 'modprobe rbd' + user 'root' + group 'root' +end + +include_recipe 'ceph::rbd_images' + +id = "rbd.#{node['hostname']}" +filename = "/etc/ceph/ceph.client.#{id}.secret" + +ceph_rbd 'rbd_test' do + id id + keyring filename + action :attach +end + +ceph_rbd 'full_test' do + size '128' + id id + keyring filename + action :create +end + +ceph_rbd 'full_test' do + id id + keyring filename + action :attach +end + +ceph_rbd 'full_test' do + id id + keyring filename + action :detach +end + +ceph_rbd 'full_test' do + id id + keyring filename + action :delete +end diff --git a/test/integration/aio/bats/ceph_rbd.bats b/test/integration/aio/bats/ceph_rbd.bats new file mode 100644 index 0000000..418b881 --- /dev/null +++ b/test/integration/aio/bats/ceph_rbd.bats @@ -0,0 +1,11 @@ +@test "test rbd is created" { + rbd info rbd/rbd_test --id=rbd.vagrant --keyring=/etc/ceph/ceph.client.rbd.vagrant.secret +} + +@test "test rbd is in rbdmap" { + grep rbd_test /etc/ceph/rbdmap +} + +@test "test rbd is attached" { + rbd showmapped --id=rbd.vagrant --keyring=/etc/ceph/ceph.client.rbd.vagrant.secret |grep 'rbd rbd_test' +}