Skip to content

Instantly share code, notes, and snippets.

@tnine
Created August 3, 2011 22:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tnine/1123963 to your computer and use it in GitHub Desktop.
Save tnine/1123963 to your computer and use it in GitHub Desktop.
include Opscode::Aws::Ec2
# Auto locates and attached ebs devices based on the data bag they reside in. The following test cases need to be performed. Currently limited to 15 drives for the raid due to the drive mount point seek iteration logic. This could be fixed to include more than 15 mount points.
# Create multiple resources with new node: PASS
#
# Re-attach multiple resources after a reboot: PASS
#
# Create resources across 2 runs. First run creates first raid set, second run re-attaches then creates: PASS
#
# Re-attach a different nodes data after terminating the original creator: PASS
# Installation:
# Copy into the providers directory of your cookbook. In this example, I have chosen "brisk" as the cookbook
#
# Usage:
# Creates a 1 TB raid 0 drive with 10 drives at 100 GB each
# brisk_ebs_raid "create data dir /mnt/cassdata" do
# mount_point /mnt/cassdata
# disk_count 10
# disk_size 100
# action [:auto_attach]
# notifies :restart, resources(:service => "cassandra")
# end
#
# Author: Todd Nine
#
action :auto_attach do
Chef::Log.info("Executing the autolocate task")
#Make sure the mdadm package is installed
package "mdadm" do
action :install
end
package "xfsprogs" do
action :install
end
#we're done we successfully located what we needed
if !locate_and_mount(@new_resource.mount_point)
#If we get here, we couldn't auto attach, nor re-allocate an existing set of disks to ourselves. Auto create the md devices
#Find our first raid device
number=0
dir = "/dev/md#{number}"
#TODO, this won't work with more than 10 md devices
begin
dir = "/dev/md#{number}"
Chef::Log.info("md pre trim #{dir}")
number +=1
end while ::File.exists?(dir)
dir = dir[5, dir.length]
Chef::Log.debug("raid device is #{dir}")
sd_dev = "sdh"
begin
sd_dev = sd_dev.next
base_device = "/dev/#{sd_dev}1"
Chef::Log.info("dev pre trim #{base_device}")
end while ::File.exists?(base_device)
Chef::Log.debug("sd device is #{sd_dev}")
create_raid_disks(@new_resource.mount_point, dir, sd_dev, @new_resource.disk_count, @new_resource.disk_size)
@new_resource.updated_by_last_action(true)
end
end
private
#Attempt to find an unused data bag and mount all the EBS volumes to our system
def locate_and_mount(mount_path )
if node[:aws].nil? || node[:aws][:raid].nil? || node[:aws][:raid][mount_path].nil?
Chef::Log.info("No mount point found '#{mount_path}' for node")
return false;
end
raid_dev = node[:aws][:raid][mount_path][:raid_dev]
raid_dev = "/dev/#{raid_dev}"
Chef::Log.info("Raid device is #{raid_dev} and mount path is #{mount_path}")
assemble_raid(raid_dev, mount_path)
#Now mount the drive
mount_device(raid_dev, mount_path)
return true
end
##
#Assembles the raid if it doesn't already exist
##
def assemble_raid(raid_dev, mount_path)
#D
if ::File.exists?(raid_dev)
Chef::Log.info("Device #{raid_dev} exists skipping")
return
end
Chef::Log.info("Raid device #{raid_dev} does not exist re-assembling")
device_vol_map = node[:aws][:raid][mount_path][:devices]
Chef::Log.debug("Devices for mount #{mount_path} are #{device_vol_map.pretty_inspect}")
device_string = ""
device_vol_map.keys.sort.each do |dev_device|
attach_volume(dev_device, device_vol_map[dev_device])
device_string += "/dev/#{dev_device} "
end
#Wait until all volumes are mounted
ruby_block "wait" do
block do
true
end
end
#Wait until all volumes are mounted
ruby_block "wait" do
block do
Chef::Log.info("sleeping 10 seconds until EBS volumes have re-attached")
sleep 10
end
end
#Now that attach is done we re-build the md device
execute "re-attaching raid device" do
command "mdadm --assemble #{raid_dev} #{device_string}"
creates raid_dev
not_if "test -d #{raid_dev}"
# notifies :run, "ruby_block[setchanged]", :immediately
end
end
def mount_device(raid_dev, mnt_point)
#Create the mount point
directory mnt_point do
owner "root"
group "nogroup"
mode 0755
recursive true
action :create
not_if "test -d #{mnt_point}"
# notifies :run, "ruby_block[setchanged]", :immediately
end
#Mount the device
mount mnt_point do
device raid_dev
fstype "xfs"
options "rw,noatime,inode64"
action :mount
# notifies :run, "ruby_block[setchanged]", :immediately
end
end
#Attach all existing ami instances if they exist on this node, if not, we want an error to occur Detects disk from node information
def attach_volume(disk_dev, volume_id)
disk_dev_path = "/dev/#{disk_dev}"
aws = data_bag_item("deployment", "aws")
Chef::Log.info("Attaching existing ebs volume id #{volume_id} for device #{disk_dev_path}")
aws_ebs_volume "#{disk_dev_path}" do
aws_access_key aws['accesskey']
aws_secret_access_key aws['secretkey']
device disk_dev_path
name disk_dev
volume_id volume_id
action [:attach]
provider "aws_ebs_volume"
end
end
#Mount point for where to mount I.E /mnt/filesystem
#Raid dev. I.E. md0
#Diskset I.E sdi (which creates sdi1-sdi<n>
#Raid size. The total size of the array
def create_raid_disks(mnt_point, raid_dev, disk_dev, num_disks, disk_size)
#Create the mount point data bag
devices = {}
#For each volume add information to the mount metadata
(1..num_disks).each do |i|
disk_dev_path = "#{disk_dev}#{i}"
aws = data_bag_item("deployment", "aws")
aws_ebs_volume "#{disk_dev_path}" do
aws_access_key aws['accesskey']
aws_secret_access_key aws['secretkey']
size disk_size
device "/dev/#{disk_dev_path}"
name disk_dev_path
action [:create, :attach]
provider "aws_ebs_volume"
#set up our data bag info
devices[disk_dev_path] = "pending"
Chef::Log.info("creating ebs volume for device #{disk_dev_path} with size #{disk_size}")
end
end
devices_string = ""
#Blocking task
devices.keys.sort.each do |k|
devices_string += "/dev/#{k} "
end
Chef::Log.debug("finsihed sorting devices #{devices_string}")
Chef::Log.debug("sleeping 10 seconds to let drives attach")
sleep 10
#Create the raid device on our system
execute "creating raid device" do
Chef::Log.info("creating raid device /dev/#{raid_dev} with raid devices #{devices_string}")
command "mdadm --create /dev/#{raid_dev} --level=0 --raid-devices=#{devices.size} #{devices_string}"
creates "/dev/#{raid_dev}"
end
#Format the device
execute "formatting device" do
command "mkfs.xfs /dev/#{raid_dev}"
only_if "xfs_admin -l /dev/#{raid_dev} 2>&1 | grep -qx 'xfs_admin: /dev/#{raid_dev} is not a valid XFS filesystem (unexpected SB magic number 0x00000000)'"
end
#Mount the device
mount_device("/dev/#{raid_dev}", mnt_point)
#Not invoked until the volumes have been successfully created and attached
ruby_block "databagsetup" do
block do
Chef::Log.info("finished creating disks")
devices.each_pair do |key, value|
value = node[:aws][:ebs_volume][key][:volume_id]
devices[key] = value
Chef::Log.info("value is #{value}")
end
#Assemble all the data bag meta data
mount_meta = {
"raid_dev" => raid_dev,
"devices" => devices
}
raid_map = node[:aws][:raid]
if raid_map.nil?
node[:aws][:raid] = {}
end
node[:aws][:raid][mnt_point] = mount_meta
node.save
end
end
#Now format and create the device
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment