Hello,

On Mon, Feb 15, 2010 at 09:30:04AM -0800, Daniel Kerwin wrote:
> i tried to write my first type and provider that should create logical
> volumes. Seems like i'm missing something as i get nothing when i use
> it: No errors and no logical volume :-(

This is not exactly what you're looking for, but I've written a simple
LVM type and an associated provider a long time ago, I'm attaching it
here together with a somewhat related "filesystem" type and provider.

I've been meaning to clean them up, write tests and properly contribute
to the project for literally years now. I guess if I don't take the
opportunity to send it out now, the code might never see daylight.

I hope someone finds it useful, and perhaps even give it some love.
It's been quite a while since I used them, so I'm not sure if they work
with modern puppet.

-- 
Marcin Owsiany <mar...@owsiany.pl>              http://marcin.owsiany.pl/
GnuPG: 1024D/60F41216  FE67 DA2D 0ACA FC5E 3F75  D6F6 3A0D 8AA0 60F4 1216
 
"Every program in development at MIT expands until it can read mail."
                                                              -- Unknown

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Users" group.
To post to this group, send email to puppet-us...@googlegroups.com.
To unsubscribe from this group, send email to 
puppet-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/puppet-users?hl=en.

# Logical Volume type
# Copyright 2007-2010 Marcin Owsiany <mar...@owsiany.pl>
# License: LGPL
require 'puppet'

Puppet::Type.newtype(:lv) do
        @doc = "Logical volume device."
        ensurable

        newparam(:name) do
                isnamevar
                desc "The name of the logical volume."
        end

        newparam(:vgname) do
                desc "The name of the volume group containing this volume."
                isrequired
        end

        newproperty(:size) do
                desc "Size of the volume, in megabytes for now."
                isrequired
                validate do |value|
                        unless value =~ /^[1-9]\d*$/
                                raise ArgumentError, "\"#{value}\" is not a 
valid size."
                        end
                end
        end
end

# LVM 2 provider for the Logical volume type.
# Copyright 2007-2010 Marcin Owsiany <mar...@owsiany.pl>
# License: LGPL
require 'puppet'
require 'puppet/provider/parsedfile'

Puppet::Type.type(:lv).provide(:lvm2
) do
        desc "Provides LVM2 backend for the logical volume type. Very
        simplistic so far."

        commands :lvs => "/sbin/lvs"
        commands :lvcreate => "/sbin/lvcreate"
        commands :lvremove => "/sbin/lvremove"
        commands :lvresize => "/sbin/lvresize"
        commands :lvreduce => "/sbin/lvreduce"

        mk_resource_methods

        def self.prefetch(resources)
                instances.each do |provider|
                        if lv = resources[provider.name] and lv[:vgname] == 
provider.vgname
                                lv.provider = provider
                        end
                end
        end

        def self.instances
                providers = []
                # XXX should set locale to C on invocation - how to do this in 
a generic and elegant way?
                cmd = "#{command(:lvs)} --noheadings --nosuffix --options 
vg_name,lv_name,lv_size --units m --separator :"
                execpipe(cmd) do |process|
                        process.each do |line|
                                vals = line.split(':').collect { |val| 
val.strip.sub(/,..$/, '') }
                                p = new(:name => vals[1], :vgname => vals[0], 
:size => vals[2], :ensure => :present)
                                providers << p
                        end
                end
                providers
        end

        def exists?
                @property_hash[:ensure] == :present
        end

        def create
                lvcreate '--size', "#...@resource.should(:size)}m", '-n', 
@resource[:name], @resource[:vgname]
        end

        def destroy
                lvremove '--force', 
"#...@resource[:vgname]}/#...@resource[:name]}"
        end

        def size=(size)
                # Looks like you need to use two different utilities to allow 
noninteractive size manipulation
                if size > self.size
                        lvresize            '--size', "#{size}m", 
"#...@resource[:vgname]}/#...@resource[:name]}"
                else
                        lvreduce '--force', '--size', "#{size}m", 
"#...@resource[:vgname]}/#...@resource[:name]}"
                end
        end
end

# Filesystem type
# Copyright 2007-2010 Marcin Owsiany <mar...@owsiany.pl>
# License: LGPL
require 'puppet'
require 'pathname'

Puppet::Type.newtype(:filesystem) do
        @doc = "Filesystem."
        ensurable

        newparam(:device) do
                isnamevar
                desc "Device name - absolute path or relative to /dev/."
                munge do |value|
                        Pathname.new(value).absolute? ? value : '/dev/' + value
                end
        end

        newproperty(:type) do
                desc "Type of the filesystem. Supported so far: ext2, ext3, 
xfs."
                isrequired
                newvalue(:ext2)
                newvalue(:ext3)
                newvalue(:xfs)
        end

        newparam(:destroy) do
                desc "Whether to allow destructive filesystem conversions."
                newvalues(:never, :ok)
                defaultto(:never)
        end
end

# mkfs provider for the filesystem type
# Copyright 2007-2010 Marcin Owsiany <mar...@owsiany.pl>
# License: LGPL
require 'puppet'

Puppet::Type.type(:filesystem).provide(:mkfs
) do
        desc "Provides a mkfs-based backend for the filesystem type. Very
        simple so far - does not support any filesystem options. Knows how to
        handle the following filesystems: ext2 ext3 xfs. Knows how to
        non-destructively convert between ext2 and ext3 and vice-versa."

        # HOWTO add new filesystem support:
        # - adjust the INITIAL_* constants if necessary
        # - add a new elsif to fs_name()
        # - add new elsif to type=() and probably some optional commands IFF
        #   non-destructive type conversion is supported
        # - update embedded type and provider documentation
        # - add a new value to type property in filesystem.rb

        # Required:
        commands :mkfs => "/sbin/mkfs", :dd => "/bin/dd"
        # Only the ones for filesystems actually in use are required:
        commands :tune2fs => "/sbin/tune2fs"

        # The following constants depend on the maximum offset of supported
        # filesystems' marker stucture.
        #
        # Whatever amount of bytes the magic number checks will require.
        INITIAL_BYTES = 0x45f
        # How many 512-byte sectors to wipe when destroying a filesystem, and
        # require when prefetching.
        INITIAL_SECTORS = (INITIAL_BYTES.to_f / 512).ceil

        mk_resource_methods

        # 
        # Helper methods.. may qualify for moving into util?
        #

        def self.fs_name(device)
                data = File.new(device).read(INITIAL_BYTES)
                if data == nil
                        raise Puppet::Error, "reading #{device} returned EOF"
                elsif data.length != INITIAL_BYTES
                        raise Puppet::Error, "reading #{INITIAL_BYTES} bytes of 
#{device} resulted in short read"
                end

                # The following magic number checks are based on GNU
                # file 4.17 rules database
                debug sprintf("#{device} linux offset: [%02X%02X]", 
data[0x438], data[0x439])
                debug sprintf("#{device} XFS offset: [%02X%02X%02X%02X]", 
data[0], data[1], data[2], data[3])
                if data[0x438] == 0x53 and data[0x439] == 0xEF
                        debug sprintf("#{device} ext[23] offset: 
[%02X%02X%02X%02X]", data[0x45c], data[0x45d], data[0x45e], data[0x45f])
                        return (data[0x45c] & 4) == 0 ? :ext2 : :ext3

                elsif  data[0] == 0x58 and data[1] == 0x46 and data[2] == 0x53 
and data[3] == 0x42
                        return :xfs

                else
                        return nil
                end
        end

        def self.make_fs(type, device)
                mkfs '-t', type, device
        end

        def self.wipe_fs(device)
                dd "if=/dev/zero", "bs=512", "count=#{INITIAL_SECTORS}", 
"of=#{device}"
        end


        def self.create_hash(path)
                h = { :device => path, :ensure => :present }
                begin
                        h[:type] = fs_name(path)
                rescue Exception => e
                        notice "fetching filesystem type of device \"#{path}\" 
failed: #{e}"
                        puts e.backtrace if Puppet[:trace]
                        return nil
                end
                h
        end


        # I'm not sure it's a good idea to try and read ALL such devices, even
        # if they are not defined, as it may cause delays and possibly problems
        # with network block devices, floppies, solid state devices, etc..
        #
        # But apparently this method is required, so the best I can think of is
        # make some blacklist, etc.
        def self.instances
                providers = []
                lines = File.new('/proc/partitions').readlines.reject { |l| 
l.strip.empty? }
                lines[1..-1].each do |line|
                        fields = line.split.collect { |v| v.strip }
                        path = "/dev/#{fields[3]}"
                        if fields[2].to_i < INITIAL_SECTORS
                                debug "Device \"#{path}\" is too short, 
ignoring"
                                next
                        end
                        providers << new(create_hash(path))
                end
                providers
        end

        def self.prefetch(resources)
                # Assign any providers we know of to matching resources
                instances.each do |provider|
                        if fs = resources[provider.device]
                                fs.provider = provider
                        end
                end
                # Fetch providers for resources which are defined but
                # instances() does not know about them
                resources.each do |name,resource|
                        unless resource.provider.exists?
                                if h = create_hash(resource[:device])
                                        resource.provider = new(h)
                                end
                        end
                end
        end

        def exists?
                @property_hash[:ensure] == :present
        end

        def create
                self.class.make_fs(@resource.should(:type), @resource[:device])
        end

        def destroy
                self.class.wipe_fs(@resource[:device])
        end

        def type=(type)
                path = @resource[:device]
                debug "type is: #{self.type}; should be: #{type}"
                if self.type == :ext2 and type == :ext3
                        tune2fs '-O', 'has_journal', path
                elsif self.type == :ext3 and type == :ext2
                        tune2fs '-O', '^has_journal', path
                else
                        raise Puppet::Error, "Destructive changing filesystem 
type \"#{self.type}\" to \"#{type}\" avoided (destroy...@resource[:destroy]})" 
unless @resource[:destroy] == :ok
                        self.class.wipe_fs(path)
                        self.class.make_fs(type, path)
                end
        end
end

Reply via email to