ruby-lvm 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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