ruby-lvm 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 0.0.1 / 2008-05-28
2
+
3
+ * Initial release
4
+
5
+ * Birthday!
6
+ * Test release.
data/Manifest.txt ADDED
@@ -0,0 +1,27 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ Todo.txt
6
+ examples/create_snapshot.rb
7
+ examples/error_handling.rb
8
+ examples/show_volumes.rb
9
+ helpers/generate_field_data.rb
10
+ lib/lvm.rb
11
+ lib/lvm/attrib/lvs.yaml
12
+ lib/lvm/attrib/pvs.yaml
13
+ lib/lvm/attrib/vgs.yaml
14
+ lib/lvm/external.rb
15
+ lib/lvm/lv.rb
16
+ lib/lvm/lvmanager.rb
17
+ lib/lvm/lvs.rb
18
+ lib/lvm/lvseg.rb
19
+ lib/lvm/proc.rb
20
+ lib/lvm/pv.rb
21
+ lib/lvm/pvmanager.rb
22
+ lib/lvm/pvs.rb
23
+ lib/lvm/pvseg.rb
24
+ lib/lvm/vg.rb
25
+ lib/lvm/vgmanager.rb
26
+ lib/lvm/vgs.rb
27
+ test/test_lvm.rb
data/README.txt ADDED
@@ -0,0 +1,59 @@
1
+ = ruby-lvm
2
+
3
+ * http://ruby-lvm.rubyforge.org
4
+ * mailto:matt@bravenet.com
5
+
6
+ == DESCRIPTION:
7
+
8
+ This is a wrapper for the LVM2 administration utility, lvm. Its primary
9
+ function it to convert physical volumes, logical volumes and volume groups
10
+ into easy to use ruby objects. It also provides a simple wrapper for typical
11
+ create/delete/etc operations.
12
+
13
+ Unfortunately due to it's lack of a proper api this is as good as it gets for
14
+ ruby integration for the forseeable future.
15
+
16
+ See this thread
17
+ http://www.redhat.com/archives/libvir-list/2007-March/msg00192.html for a
18
+ similar discussion.
19
+
20
+ == FEATURES/PROBLEMS:
21
+
22
+ * Not finished, is that a problem?
23
+
24
+ == SYNOPSIS:
25
+
26
+ FIX (code sample of usage)
27
+
28
+ == REQUIREMENTS:
29
+
30
+ * popen4
31
+
32
+ == INSTALL:
33
+
34
+ * sudo gem install ruby-lvm
35
+
36
+ == LICENSE:
37
+
38
+ (The MIT License)
39
+
40
+ Copyright (c) 2008 Matthew Kent, Bravenet Web Services Inc.
41
+
42
+ Permission is hereby granted, free of charge, to any person obtaining
43
+ a copy of this software and associated documentation files (the
44
+ 'Software'), to deal in the Software without restriction, including
45
+ without limitation the rights to use, copy, modify, merge, publish,
46
+ distribute, sublicense, and/or sell copies of the Software, and to
47
+ permit persons to whom the Software is furnished to do so, subject to
48
+ the following conditions:
49
+
50
+ The above copyright notice and this permission notice shall be
51
+ included in all copies or substantial portions of the Software.
52
+
53
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
54
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
55
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
56
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
57
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
58
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
59
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ $:.unshift(File.dirname(__FILE__) + "/lib")
6
+ require 'lvm'
7
+
8
+ Hoe.new('ruby-lvm', LVMWrapper::VERSION) do |p|
9
+ p.developer('Matthew Kent', 'matt@bravenet.com')
10
+ p.extra_deps << ['open4', '>= 0.9.6']
11
+ p.remote_rdoc_dir = ''
12
+ end
13
+
14
+ # vim: syntax=Ruby
data/Todo.txt ADDED
@@ -0,0 +1,10 @@
1
+ = to do
2
+
3
+ == 0.1.0
4
+ * improve auto generation of attributes, clean it up
5
+ * don't bother wrapping lvm create/delete etc commands, too complex, just
6
+ provide a standard lvm.raw('lvcreate foo'), that traps errors to do work at
7
+ this level for now
8
+ * proper documentation
9
+ * basic test suite
10
+ * compatibility check and warning, pass flag to shutup
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/ruby
2
+
3
+ $: << File.dirname(__FILE__) + "/../lib"
4
+
5
+ require 'lvm'
6
+ require 'lvm/lvmanager'
7
+ require 'lvm/pvmanager'
8
+ require 'lvm/vgmanager'
9
+
10
+ lvm = LVMWrapper::LVM.new(:command => "/usr/bin/sudo /sbin/lvm")
11
+ lv = LVMWrapper::LogicalVolumeManager.new(lvm)
12
+
13
+ lv.remove(:vgname => "sys.vg", :name => "demo_snap")
14
+ lv.snapshot(:origin => "sys.vg/tmp.lv", :name => "demo_snap", :size => "10k")
15
+
16
+ p lv.list("sys.vg/demo_snap")
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/ruby
2
+
3
+ $: << File.dirname(__FILE__) + "/../lib"
4
+
5
+ require 'lvm'
6
+
7
+ lvm = LVMWrapper::LVM.new(:command => "/usr/bin/sudo /sbin/lvm", :debug => true)
8
+
9
+ lvm.raw("--blah blah")
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/ruby
2
+
3
+ $: << File.dirname(__FILE__) + "/../lib"
4
+
5
+ require 'lvm'
6
+ require 'lvm/lvmanager'
7
+ require 'lvm/pvmanager'
8
+ require 'lvm/vgmanager'
9
+
10
+ lvm = LVMWrapper::LVM.new(:command => "/usr/bin/sudo /sbin/lvm")
11
+ lv = LVMWrapper::LogicalVolumeManager.new(lvm)
12
+ vg = LVMWrapper::VolumeGroupManager.new(lvm)
13
+ pv = LVMWrapper::PhysicalVolumeManager.new(lvm)
14
+
15
+ pv.list.each do |p|
16
+ puts "- pv #{p.name}"
17
+ end
18
+
19
+ puts ""
20
+
21
+ vg.list.each do |v|
22
+ puts "- vg #{v.name}"
23
+ lv.list(v.name).each do |l|
24
+ puts "-- lv #{l.name}"
25
+ end
26
+ end
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # collect all lvm reporting columns and format to our liking for inclusion in
4
+ # the library
5
+ #
6
+ # ruby generate_field_data.rb ~/iscsi_work/LVM2.2.02.36
7
+
8
+ require 'yaml'
9
+
10
+ LVM_COLUMNS = "/lib/report/columns.h"
11
+
12
+ CONVERSION_MAP = {
13
+ # Only types we can really trust
14
+ "uint32" => "Integer",
15
+ "int32" => "Integer",
16
+ # These were determined by reading the code, they invoke _(u)int32_disp right away
17
+ "pvmdas" => "Integer",
18
+ "vgmdas" => "Integer",
19
+ "lvcount" => "Integer",
20
+ "lvsegcount" => "Integer",
21
+ "segstartpe" => "Integer",
22
+ # listed to return STR?
23
+ "lvkmaj" => "Integer",
24
+ "lvkmin" => "Integer",
25
+ "snpercent" => "Float",
26
+ "copypercent" => "Float",
27
+ # size32/64, these do unit formatting unless overridden on command line. We
28
+ # typically want them in bytes so we can convert them to Integers safely
29
+ "size32" => "Integer",
30
+ "size64" => "Integer",
31
+ # These types return size32/size64 as well
32
+ "lvkreadahead" => "Integer",
33
+ "pvsize" => "Integer",
34
+ "devsize" => "Integer",
35
+ "pvfree" => "Integer",
36
+ "pvused" => "Integer",
37
+ "pvmdafree" => "Integer",
38
+ "vgsize" => "Integer",
39
+ "vgfree" => "Integer",
40
+ "vgmda_free" => "Integer",
41
+ "chunksize" => "Integer",
42
+ "segstart" => "Integer",
43
+ "segsize" => "Integer",
44
+ "vgmdafree" => "Integer",
45
+ # Weird one, can be "auto" or size32
46
+ "lvreadahead" => "String"
47
+ }
48
+
49
+ lvm_source = ARGV[0]
50
+
51
+ lvs = []
52
+ vgs = []
53
+ pvs = []
54
+ File.readlines(lvm_source + LVM_COLUMNS).each do |line|
55
+ chunked = line.split(", ")
56
+
57
+ app = chunked[0]
58
+ general_type = chunked[2]
59
+ specific_type = chunked[6]
60
+ field = chunked[7]
61
+ # description = chunked[8]
62
+
63
+ attribute = field
64
+ attribute_type = nil
65
+
66
+ if app.nil? || general_type.nil? || specific_type.nil? || field.nil?
67
+ next
68
+ end
69
+
70
+ attribute.gsub!('"', '')
71
+
72
+ if general_type == "NUM"
73
+ attribute_type = CONVERSION_MAP[specific_type]
74
+ if attribute_type.nil?
75
+ puts "oops, missing hanlding of attribute '#{attribute}' which says its going to return a '#{specific_type}'"
76
+ exit 1
77
+ end
78
+ else
79
+ attribute_type = "String"
80
+ end
81
+ #XXX man page says we can skip the lv_ prefixes! and seg_!
82
+ if app == "FIELD(LVS" || app == "FIELD(SEGS"
83
+ lvs << { :option => attribute, :type_hint => attribute_type, :method => attribute.sub(%r{^lv_}, '') }
84
+ elsif app == "FIELD(PVS" || app == "FIELD(PVSEGS"
85
+ pvs << { :option => attribute, :type_hint => attribute_type, :method => attribute.sub(%r{^pv_}, '') }
86
+ elsif app == "FIELD(VGS"
87
+ vgs << { :option => attribute, :type_hint => attribute_type, :method => attribute.sub(%r{^vg_}, '') }
88
+ end
89
+ end
90
+
91
+ lvs.sort! {|x,y| x[:option] <=> y[:option]}
92
+ pvs.sort! {|x,y| x[:option] <=> y[:option]}
93
+ vgs.sort! {|x,y| x[:option] <=> y[:option]}
94
+
95
+ File.open("lvs.yaml", "w") do |f|
96
+ f.write "# these are column to object attribute mappings \n"
97
+ f.write "# generated by #{$0} based on \n"
98
+ f.write "# #{lvm_source}/lib/report/columns.h\n"
99
+ f.write(lvs.to_yaml)
100
+ end
101
+ puts "Wrote data to lvs.yaml"
102
+
103
+ File.open("pvs.yaml", "w") do |f|
104
+ f.write "# these are column to object attribute mappings \n"
105
+ f.write "# generated by #{$0} based on \n"
106
+ f.write "# #{lvm_source}/lib/report/columns.h\n"
107
+ f.write(pvs.to_yaml)
108
+ end
109
+ puts "Wrote data to pvs.yaml"
110
+
111
+ File.open("vgs.yaml", "w") do |f|
112
+ f.write "# these are column to object attribute mappings \n"
113
+ f.write "# generated by #{$0} based on \n"
114
+ f.write "# #{lvm_source}/lib/report/columns.h\n"
115
+ f.write(vgs.to_yaml)
116
+ end
117
+ puts "Wrote data to vgs.yaml"
@@ -0,0 +1,88 @@
1
+ # these are column to object attribute mappings
2
+ # generated by generate_field_data.rb based on
3
+ # /home/iscsidev/LVM2.2.02.26/lib/report/columns.h
4
+ ---
5
+ - :method: chunk_size
6
+ :option: chunk_size
7
+ :type_hint: Integer
8
+ - :method: chunksize
9
+ :option: chunksize
10
+ :type_hint: Integer
11
+ - :method: copy_percent
12
+ :option: copy_percent
13
+ :type_hint: Float
14
+ - :method: devices
15
+ :option: devices
16
+ :type_hint: String
17
+ - :method: attr
18
+ :option: lv_attr
19
+ :type_hint: String
20
+ - :method: kernel_major
21
+ :option: lv_kernel_major
22
+ :type_hint: String
23
+ - :method: kernel_minor
24
+ :option: lv_kernel_minor
25
+ :type_hint: String
26
+ - :method: major
27
+ :option: lv_major
28
+ :type_hint: Integer
29
+ - :method: minor
30
+ :option: lv_minor
31
+ :type_hint: Integer
32
+ - :method: name
33
+ :option: lv_name
34
+ :type_hint: String
35
+ - :method: size
36
+ :option: lv_size
37
+ :type_hint: Integer
38
+ - :method: tags
39
+ :option: lv_tags
40
+ :type_hint: String
41
+ - :method: uuid
42
+ :option: lv_uuid
43
+ :type_hint: String
44
+ - :method: mirror_log
45
+ :option: mirror_log
46
+ :type_hint: String
47
+ - :method: modules
48
+ :option: modules
49
+ :type_hint: String
50
+ - :method: move_pv
51
+ :option: move_pv
52
+ :type_hint: String
53
+ - :method: origin
54
+ :option: origin
55
+ :type_hint: String
56
+ - :method: region_size
57
+ :option: region_size
58
+ :type_hint: Integer
59
+ - :method: regionsize
60
+ :option: regionsize
61
+ :type_hint: Integer
62
+ - :method: seg_count
63
+ :option: seg_count
64
+ :type_hint: Integer
65
+ - :method: seg_size
66
+ :option: seg_size
67
+ :type_hint: Integer
68
+ - :method: seg_start
69
+ :option: seg_start
70
+ :type_hint: Integer
71
+ - :method: seg_tags
72
+ :option: seg_tags
73
+ :type_hint: String
74
+ - :method: segtype
75
+ :option: segtype
76
+ :type_hint: String
77
+ - :method: snap_percent
78
+ :option: snap_percent
79
+ :type_hint: Float
80
+ - :method: stripe_size
81
+ :option: stripe_size
82
+ :type_hint: Integer
83
+ - :method: stripes
84
+ :option: stripes
85
+ :type_hint: Integer
86
+ - :method: stripesize
87
+ :option: stripesize
88
+ :type_hint: Integer
@@ -0,0 +1,46 @@
1
+ # these are column to object attribute mappings
2
+ # generated by generate_field_data.rb based on
3
+ # /home/iscsidev/LVM2.2.02.26/lib/report/columns.h
4
+ ---
5
+ - :method: dev_size
6
+ :option: dev_size
7
+ :type_hint: Integer
8
+ - :method: pe_start
9
+ :option: pe_start
10
+ :type_hint: Integer
11
+ - :method: attr
12
+ :option: pv_attr
13
+ :type_hint: String
14
+ - :method: fmt
15
+ :option: pv_fmt
16
+ :type_hint: String
17
+ - :method: free
18
+ :option: pv_free
19
+ :type_hint: Integer
20
+ - :method: name
21
+ :option: pv_name
22
+ :type_hint: String
23
+ - :method: pe_alloc_count
24
+ :option: pv_pe_alloc_count
25
+ :type_hint: Integer
26
+ - :method: pe_count
27
+ :option: pv_pe_count
28
+ :type_hint: Integer
29
+ - :method: size
30
+ :option: pv_size
31
+ :type_hint: Integer
32
+ - :method: tags
33
+ :option: pv_tags
34
+ :type_hint: String
35
+ - :method: used
36
+ :option: pv_used
37
+ :type_hint: Integer
38
+ - :method: uuid
39
+ :option: pv_uuid
40
+ :type_hint: String
41
+ - :method: pvseg_size
42
+ :option: pvseg_size
43
+ :type_hint: Integer
44
+ - :method: pvseg_start
45
+ :option: pvseg_start
46
+ :type_hint: Integer
@@ -0,0 +1,55 @@
1
+ # these are column to object attribute mappings
2
+ # generated by generate_field_data.rb based on
3
+ # /home/iscsidev/LVM2.2.02.26/lib/report/columns.h
4
+ ---
5
+ - :method: lv_count
6
+ :option: lv_count
7
+ :type_hint: Integer
8
+ - :method: max_lv
9
+ :option: max_lv
10
+ :type_hint: Integer
11
+ - :method: max_pv
12
+ :option: max_pv
13
+ :type_hint: Integer
14
+ - :method: pv_count
15
+ :option: pv_count
16
+ :type_hint: Integer
17
+ - :method: snap_count
18
+ :option: snap_count
19
+ :type_hint: Integer
20
+ - :method: attr
21
+ :option: vg_attr
22
+ :type_hint: String
23
+ - :method: extent_count
24
+ :option: vg_extent_count
25
+ :type_hint: Integer
26
+ - :method: extent_size
27
+ :option: vg_extent_size
28
+ :type_hint: Integer
29
+ - :method: fmt
30
+ :option: vg_fmt
31
+ :type_hint: String
32
+ - :method: free
33
+ :option: vg_free
34
+ :type_hint: Integer
35
+ - :method: free_count
36
+ :option: vg_free_count
37
+ :type_hint: Integer
38
+ - :method: name
39
+ :option: vg_name
40
+ :type_hint: String
41
+ - :method: seqno
42
+ :option: vg_seqno
43
+ :type_hint: Integer
44
+ - :method: size
45
+ :option: vg_size
46
+ :type_hint: Integer
47
+ - :method: sysid
48
+ :option: vg_sysid
49
+ :type_hint: String
50
+ - :method: tags
51
+ :option: vg_tags
52
+ :type_hint: String
53
+ - :method: uuid
54
+ :option: vg_uuid
55
+ :type_hint: String
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ require 'open4'
3
+
4
+ module LVMWrapper
5
+ class External
6
+ class ExternalFailure < RuntimeError; end
7
+
8
+ class<<self
9
+ # Execute a command, returning the resulting String of standard output.
10
+ #
11
+ # The block is optional. If given, it will be invoked for each line of
12
+ # output.
13
+ def cmd(cmd)
14
+ output = []
15
+ error = nil
16
+ stat = Open4::popen4(cmd) do |pid, stdin, stdout, stderr|
17
+ while line = stdout.gets
18
+ output << line
19
+ end
20
+ error = stderr.read.strip
21
+ end
22
+ if stat.exited?
23
+ if stat.exitstatus > 0
24
+ raise ExternalFailure, "Fatal error, `#{cmd}` returned #{stat.exitstatus} with '#{error}'"
25
+ end
26
+ elsif stat.signaled?
27
+ raise ExternalFailure, "Fatal error, `#{cmd}` got signal #{stat.termsig} and terminated"
28
+ elsif stat.stopped?
29
+ raise ExternalFailure, "Fatal error, `#{cmd}` got signal #{stat.stopsig} and is stopped"
30
+ end
31
+
32
+ if block_given?
33
+ return output.each { |l| yield l }
34
+ else
35
+ return output.join
36
+ end
37
+ end
38
+ end
39
+
40
+ end # class
41
+ end # module
data/lib/lvm/lv.rb ADDED
@@ -0,0 +1,19 @@
1
+ module LVMWrapper
2
+ class LogicalVolume
3
+ def initialize(args)
4
+ @attributes = args
5
+
6
+ # so many attributes.. we let the caller define them :)
7
+ meta = class << self; self; end
8
+ args.each_key do |name|
9
+ meta.send(:define_method, name) { @attributes[name] }
10
+ end
11
+ end
12
+
13
+ # helper methods
14
+ def open?
15
+ @attributes[:device_open]
16
+ end
17
+
18
+ end # class
19
+ end # module
@@ -0,0 +1,95 @@
1
+ require 'lvm/lv'
2
+ require 'lvm/lvs'
3
+
4
+ module LVMWrapper
5
+ class LogicalVolumeManager
6
+
7
+ def initialize(lvm)
8
+ @lvm = lvm
9
+ end
10
+
11
+ # lvchange Change the attributes of logical volume(s)
12
+ # lvconvert Change logical volume layout
13
+ # lvdisplay Display information about a logical volume
14
+ # lvextend Add space to a logical volume
15
+ #*lvmchange With the device mapper, this is obsolete and
16
+ # does nothing.
17
+ #*lvmdiskscan List devices that may be used as physical
18
+ # volumes
19
+ #*lvmsadc Collect activity data
20
+ #*lvmsar Create activity report
21
+ # lvreduce Reduce the size of a logical volume
22
+ # lvrename Rename a logical volume
23
+ # lvresize Resize a logical volume
24
+ # lvscan List all logical volumes in
25
+ # all volume groups
26
+
27
+ # We take the options listed by 'lvm <cmd>' as symbols and pass them as is
28
+ # to lvm. We let LVM opt parsing handle any issues.
29
+ def create(args)
30
+ vgname = args[:vgname] || nil
31
+ pvpath = args[:pvpath]
32
+
33
+ raise ArgumentError if vgname.nil?
34
+
35
+ args.delete(:vgname)
36
+ args.delete(:pvpath)
37
+
38
+ @lvm.cmd("lvcreate #{args_to_long_opts(args)} #{vgname} #{pvpath}")
39
+ end
40
+
41
+ def remove(args)
42
+ lvname = args[:name] || nil
43
+ vgname = args[:vgname] || nil
44
+
45
+ raise ArgumentError if (lvname.nil? or vgname.nil?)
46
+
47
+ args.delete(:name)
48
+ args.delete(:vgname)
49
+
50
+ @lvm.cmd("lvremove --force #{args_to_long_opts(args)} #{vgname}/#{lvname}")
51
+ end
52
+
53
+ def snapshot(args)
54
+ origin = args[:origin] || nil
55
+
56
+ raise ArgumentError if origin.nil?
57
+
58
+ args.delete(:origin)
59
+
60
+ @lvm.cmd("lvcreate --snapshot #{args_to_long_opts(args)} #{origin}")
61
+ end
62
+
63
+ # ruby hash of key values to long options
64
+ def args_to_long_opts(args)
65
+ opts = []
66
+ args.each do |key,value|
67
+ opts << "--#{key.to_s} #{value}"
68
+ end
69
+ opts.join(" ")
70
+ end
71
+
72
+ def lookup(lvname,vgname)
73
+ raw_data = @lvm.cmd("#{LVS.command} #{vgname}/#{lvname}")
74
+ LVS.parse(raw_data)[0]
75
+ end
76
+
77
+ def [](lvname,vgname)
78
+ lookup(lvname,vgname)
79
+ end
80
+
81
+ def list(vgname)
82
+ raw_data = @lvm.cmd("#{LVS.command} #{vgname}")
83
+ lvs = LVS.parse(raw_data)
84
+ if block_given?
85
+ lvs.each do |lv|
86
+ yield lv
87
+ end
88
+ else
89
+ lvs
90
+ end
91
+ end
92
+ alias lvs list
93
+
94
+ end # class
95
+ end # module