ruby-lvm 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/lvm/lvs.rb ADDED
@@ -0,0 +1,143 @@
1
+ module LVMWrapper
2
+ class LVS
3
+ require 'yaml'
4
+
5
+ BASE_COMMAND = 'lvs --verbose --separator=, --noheadings --nosuffix --units=b --unbuffered --options %s'
6
+ #,vg_uuid'
7
+ ATTRIBUTES = 'attrib/lvs.yaml'
8
+
9
+ EMPTY = '-'
10
+
11
+ # lv_attr attribute handling constants
12
+ # roughly by order referenced in lib/report/report.c:292 (_lvstatus_disp)
13
+ #
14
+ VOLUME_TYPE = {
15
+ 'p' => :pvmove,
16
+ 'c' => :conversion,
17
+ 'M' => :mirror_not_synced,
18
+ 'm' => :mirror,
19
+ 'i' => :mirror_image,
20
+ 'I' => :mirror_image_not_synced,
21
+ 'l' => :mirror_log,
22
+ 'v' => :virtual,
23
+ 'o' => :origin,
24
+ 's' => :snapshot,
25
+ 'S' => :invalid_snapshot,
26
+ # custom, empty is a standard volume
27
+ '-' => :normal
28
+ }
29
+ PERMISSIONS = {
30
+ 'w' => :writeable,
31
+ 'r' => :readonly,
32
+ # custom, from reading source
33
+ '-' => :locked_by_pvmove,
34
+ }
35
+ ALLOCATION_POLICY = {
36
+ 'c' => :contiguous,
37
+ 'l' => :cling,
38
+ 'n' => :normal,
39
+ 'a' => :anywhere,
40
+ 'i' => :inherited,
41
+ 'C' => :contiguous_locked,
42
+ 'L' => :cling_locked,
43
+ 'N' => :normal_locked,
44
+ 'A' => :anywhere_locked,
45
+ 'I' => :inherited_locked
46
+ }
47
+ FIXED_MINOR = {
48
+ # code says its a boolean
49
+ 'm' => true
50
+ }
51
+ STATE = {
52
+ 's' => :suspended,
53
+ 'a' => :active,
54
+ 'i' => :inactive_with_table,
55
+ 'd' => :inactive_without_table,
56
+ 'S' => :suspended_snapshot,
57
+ 'I' => :invalid_snapshot
58
+ }
59
+ DEVICE_OPEN = {
60
+ # code says its a boolean
61
+ 'o' => true
62
+ }
63
+
64
+ def self.command
65
+ opts = []
66
+ attributes = YAML.load_file(File.dirname(__FILE__) + '/' + ATTRIBUTES)
67
+ attributes.each do |a|
68
+ opts << a[:option]
69
+ end
70
+
71
+ BASE_COMMAND % opts.join(",")
72
+ end
73
+
74
+ def self.parse_lv_attr(lv_attr)
75
+ ret = {}
76
+ # translate them into nice symbols and a couple booleans
77
+ ret[:volume_type] = VOLUME_TYPE[lv_attr[0].chr]
78
+ ret[:permissions] = PERMISSIONS[lv_attr[1].chr]
79
+ ret[:allocation_policy] = ALLOCATION_POLICY[lv_attr[2].chr]
80
+ ret[:fixed_minor] = FIXED_MINOR[lv_attr[3].chr] ? true : false
81
+ ret[:state] = STATE[lv_attr[4].chr]
82
+ ret[:device_open] = DEVICE_OPEN[lv_attr[5].chr] ? true : false
83
+ ret
84
+ end
85
+
86
+ # Parses the output of self.command
87
+ def self.parse(output)
88
+ volumes = []
89
+
90
+ # lvs columns will become lv attributes
91
+ attributes = YAML.load_file(File.dirname(__FILE__) + '/' + ATTRIBUTES)
92
+
93
+ output.split("\n").each do |line|
94
+ line.strip!
95
+ # each line of output is comma separated values
96
+ values = line.split(",")
97
+ # empty values to nil
98
+ values.map! do |value|
99
+ if value.empty?
100
+ nil
101
+ else
102
+ value
103
+ end
104
+ end
105
+
106
+ args = {}
107
+ # match up attribute => value
108
+ values.size.times do |i|
109
+ method = attributes[i][:method].to_sym
110
+ value = values[i]
111
+ # use our hints first for type conversion
112
+ if attributes[i][:type_hint] == "Integer"
113
+ value = value.to_i
114
+ elsif attributes[i][:type_hint] == "Float"
115
+ value = value.to_f
116
+ end
117
+ args[method] = value
118
+ end
119
+
120
+ # LVSEG
121
+
122
+ # now we force some types to ints since we've requested them as bytes
123
+ # without a suffix
124
+ args[:size] = args[:size].to_i
125
+
126
+ # we resolve the attributes line to nicer symbols
127
+ args.merge!(parse_lv_attr(args[:attr]))
128
+
129
+ # finally build our object
130
+ volume = LogicalVolume.new(args)
131
+
132
+ if block_given?
133
+ yield volume
134
+ else
135
+ volumes << volume
136
+ end
137
+ end
138
+
139
+ volumes
140
+ end # parse
141
+
142
+ end # class
143
+ end # module
data/lib/lvm/lvseg.rb ADDED
@@ -0,0 +1,14 @@
1
+ module LVMWrapper
2
+ class PhysicalVolumeSegment
3
+ attr_reader :start, :size, :finish
4
+
5
+ def initialize(args)
6
+ @start = args[:start]
7
+ @size = args[:size]
8
+
9
+ @finish = @start + @size
10
+ end
11
+
12
+ # helper methods
13
+ end # class
14
+ end # module
data/lib/lvm/proc.rb ADDED
@@ -0,0 +1,39 @@
1
+ module LVMWrapper
2
+ class External
3
+ class ExternalFailure < RuntimeError; end
4
+
5
+ # Class Methods
6
+ #
7
+ class<<self
8
+ # Return output of external command
9
+ # Yield each line or return entire output
10
+ #
11
+ def cmd(cmd)
12
+ output = []
13
+ IO.popen(cmd, "r") do |p|
14
+ while line = p.gets
15
+ if defined? yield
16
+ yield line
17
+ else
18
+ output << line
19
+ end
20
+ end
21
+ end
22
+ stat = $?
23
+ if stat.exited?
24
+ if stat.exitstatus > 0
25
+ raise ExternalFailure, "Fatal error, `#{cmd}` returned #{stat.exitstatus}"
26
+ end
27
+ elsif stat.signaled?
28
+ raise ExternalFailure, "Fatal error, `#{cmd}` got signal #{stat.termsig} and terminated"
29
+ elsif stat.stopped?
30
+ raise ExternalFailure, "Fatal error, `#{cmd}` got signal #{stat.stopsig} and is stopped"
31
+ end
32
+ output.join
33
+ end
34
+ end
35
+
36
+ # Instance Methods
37
+ #
38
+ end # class
39
+ end # module
data/lib/lvm/pv.rb ADDED
@@ -0,0 +1,15 @@
1
+ module LVMWrapper
2
+ class PhysicalVolume
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
+ end # class
15
+ end # module
@@ -0,0 +1,22 @@
1
+ require 'lvm/pv'
2
+ require 'lvm/pvs'
3
+ require 'lvm/pvseg'
4
+
5
+ module LVMWrapper
6
+ class PhysicalVolumeManager
7
+
8
+ def initialize(lvm)
9
+ @lvm = lvm
10
+ end
11
+
12
+ def create(args)
13
+ end
14
+
15
+ def list
16
+ data = @lvm.cmd(PVS.command)
17
+ PVS.parse(data)
18
+ end
19
+ alias pvs list
20
+
21
+ end # class
22
+ end # module
data/lib/lvm/pvs.rb ADDED
@@ -0,0 +1,109 @@
1
+ module LVMWrapper
2
+ class PVS
3
+ require 'yaml'
4
+
5
+ BASE_COMMAND = 'pvs --verbose --separator=, --noheadings --nosuffix --units=b --unbuffered --options %s'
6
+ ATTRIBUTES = 'attrib/pvs.yaml'
7
+
8
+ EMPTY = '-'
9
+
10
+ # pv_attr attribute handling constants
11
+ # roughly by order referenced in lib/report/report.c:360 (_pvstatus_disp)
12
+ #
13
+ ALLOCATABLE = {
14
+ # code says its a boolean
15
+ 'a' => true
16
+ }
17
+ EXPORTED = {
18
+ # code says its a boolean
19
+ 'x' => true
20
+ }
21
+
22
+ def self.command
23
+ opts = []
24
+ attributes = YAML.load_file(File.dirname(__FILE__) + '/' + ATTRIBUTES)
25
+ attributes.each do |a|
26
+ opts << a[:option]
27
+ end
28
+
29
+ BASE_COMMAND % opts.join(",")
30
+ end
31
+
32
+ def self.parse_pv_attr(pv_attr)
33
+ ret = {}
34
+ # translate them into nice symbols and a couple booleans
35
+ ret[:allocatable] = ALLOCATABLE[pv_attr[0].chr] ? true : false
36
+ ret[:exported] = EXPORTED[pv_attr[1].chr] ? true : false
37
+ ret
38
+ end
39
+
40
+ # Parses the output of self.command
41
+ def self.parse(output)
42
+ volumes = []
43
+ segs = []
44
+
45
+ # lvs columns will become lv attributes
46
+ attributes = YAML.load_file(File.dirname(__FILE__) + '/' + ATTRIBUTES)
47
+
48
+ output.split("\n").each do |line|
49
+ line.strip!
50
+ # each line of output is comma separated values
51
+ values = line.split(",")
52
+ # empty values to nil
53
+ values.map! do |value|
54
+ if value.empty?
55
+ nil
56
+ else
57
+ value
58
+ end
59
+ end
60
+
61
+ args = {}
62
+ # match up attribute => value
63
+ values.size.times do |i|
64
+ method = attributes[i][:method].to_sym
65
+ value = values[i]
66
+ # use our hints first for type conversion
67
+ if attributes[i][:type_hint] == "Integer"
68
+ value = value.to_i
69
+ elsif attributes[i][:type_hint] == "Float"
70
+ value = value.to_f
71
+ end
72
+ args[method] = value
73
+ end
74
+
75
+ # pvs produces duplicate lines while elaborating on the segments being
76
+ # used, collect the segment data
77
+ segs << PhysicalVolumeSegment.new(:start => args[:pvseg_start], :size => args[:pvseg_size])
78
+ if args[:pvseg_start] == args[:pe_alloc_count]
79
+ args[:segments] = segs.dup
80
+ # already have em
81
+ args.delete(:pvseg_start)
82
+ args.delete(:pvseg_size)
83
+ segs.clear
84
+ else
85
+ next
86
+ end
87
+
88
+ # now we force some types to ints since we've requested them as bytes
89
+ # without a suffix
90
+ args[:size] = args[:size].to_i
91
+
92
+ # we resolve the attributes line to nicer symbols
93
+ args.merge!(parse_pv_attr(args[:attr]))
94
+
95
+ # finally build our object
96
+ volume = PhysicalVolume.new(args)
97
+
98
+ if block_given?
99
+ yield volume
100
+ else
101
+ volumes << volume
102
+ end
103
+ end
104
+
105
+ volumes
106
+ end # parse
107
+
108
+ end # class
109
+ end # module
data/lib/lvm/pvseg.rb ADDED
@@ -0,0 +1,14 @@
1
+ module LVMWrapper
2
+ class PhysicalVolumeSegment
3
+ attr_reader :start, :size, :finish
4
+
5
+ def initialize(args)
6
+ @start = args[:start]
7
+ @size = args[:size]
8
+
9
+ @finish = @start + @size
10
+ end
11
+
12
+ # helper methods
13
+ end # class
14
+ end # module
data/lib/lvm/vg.rb ADDED
@@ -0,0 +1,15 @@
1
+ module LVMWrapper
2
+ class VolumeGroup
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
+ end # class
15
+ end # module
@@ -0,0 +1,68 @@
1
+ require 'lvm/vg'
2
+ require 'lvm/vgs'
3
+
4
+ module LVMWrapper
5
+ class VolumeGroupManager
6
+
7
+ def initialize(lvm)
8
+ @lvm = lvm
9
+ end
10
+
11
+ # vgcfgbackup Backup volume group configuration(s)
12
+ # vgcfgrestore Restore volume group configuration
13
+ # vgchange Change volume group attributes
14
+ # vgck Check the consistency of volume group(s)
15
+ # vgconvert Change volume group metadata format
16
+ # vgcreate Create a volume group
17
+ # vgdisplay Display volume group information
18
+ # vgexport Unregister volume group(s) from the system
19
+ # vgextend Add physical volumes to a volume group
20
+ # vgimport Register exported volume group with system
21
+ # vgmerge Merge volume groups
22
+ # vgmknodes Create the special files for volume group devices in /dev
23
+ # vgreduce Remove physical volume(s) from a volume group
24
+ # vgremove Remove volume group(s)
25
+ # vgrename Rename a volume group
26
+ # vgs Display information about volume groups
27
+ # vgscan Search for all volume groups
28
+ # vgsplit Move physical volumes into a new volume group
29
+
30
+ def create(args)
31
+ end
32
+
33
+ def change(args)
34
+ vgname = args[:vgname] || nil
35
+
36
+ raise ArgumentError if vgname.nil?
37
+
38
+ args.delete(:vgname)
39
+
40
+ @lvm.cmd("vgchange #{args_to_long_opts(args)} #{vgname}")
41
+ end
42
+
43
+ # ruby hash of key values to long options
44
+ def args_to_long_opts(args)
45
+ opts = []
46
+ args.each do |key,value|
47
+ opts << "--#{key.to_s} #{value}"
48
+ end
49
+ opts.join(" ")
50
+ end
51
+
52
+ def lookup(vgname)
53
+ raw_data = @lvm.cmd("#{VGS.command} #{vgname}")
54
+ VGS.parse(raw_data)[0]
55
+ end
56
+
57
+ def [](vgname)
58
+ lookup(vgname)
59
+ end
60
+
61
+ def list
62
+ data = @lvm.cmd(VGS.command)
63
+ VGS.parse(data)
64
+ end
65
+ alias vgs list
66
+
67
+ end # class
68
+ end # module
data/lib/lvm/vgs.rb ADDED
@@ -0,0 +1,123 @@
1
+ module LVMWrapper
2
+ class VGS
3
+ require 'yaml'
4
+
5
+ BASE_COMMAND = 'vgs --verbose --separator=, --noheadings --nosuffix --units=b --unbuffered --options %s'
6
+ ATTRIBUTES = 'attrib/vgs.yaml'
7
+
8
+ EMPTY = '-'
9
+
10
+ # vg_attr attribute handling constants
11
+ # roughly by order referenced in lib/report/report.c:360 (_vgstatus_disp)
12
+ #
13
+ PERMISSIONS = {
14
+ 'w' => :writeable,
15
+ 'r' => :readonly,
16
+ }
17
+ RESIZEABLE = {
18
+ # code says its a boolean
19
+ 'z' => true
20
+ }
21
+ EXPORTED = {
22
+ # code says its a boolean
23
+ 'x' => true
24
+ }
25
+ PARTIAL = {
26
+ # code says its a boolean
27
+ 'p' => true
28
+ }
29
+ ALLOCATION_POLICY = {
30
+ 'c' => :contiguous,
31
+ 'l' => :cling,
32
+ 'n' => :normal,
33
+ 'a' => :anywhere,
34
+ 'i' => :inherited,
35
+ 'C' => :contiguous_locked,
36
+ 'L' => :cling_locked,
37
+ 'N' => :normal_locked,
38
+ 'A' => :anywhere_locked,
39
+ 'I' => :inherited_locked
40
+ }
41
+ CLUSTERED = {
42
+ # code says its a boolean
43
+ 'c' => true
44
+ }
45
+
46
+ def self.command
47
+ opts = []
48
+ attributes = YAML.load_file(File.dirname(__FILE__) + '/' + ATTRIBUTES)
49
+ attributes.each do |a|
50
+ opts << a[:option]
51
+ end
52
+
53
+ BASE_COMMAND % opts.join(",")
54
+ end
55
+
56
+ def self.parse_vg_attr(vg_attr)
57
+ ret = {}
58
+ # translate them into nice symbols and a couple booleans
59
+ ret[:permissions] = PERMISSIONS[vg_attr[0].chr]
60
+ ret[:resizeable] = RESIZEABLE[vg_attr[1].chr] ? true : false
61
+ ret[:exported] = EXPORTED[vg_attr[2].chr] ? true : false
62
+ ret[:partial] = PARTIAL[vg_attr[3].chr] ? true : false
63
+ ret[:allocation_policy] = ALLOCATION_POLICY[vg_attr[4].chr]
64
+ ret[:clustered] = CLUSTERED[vg_attr[5].chr] ? true : false
65
+ ret
66
+ end
67
+
68
+ # Parses the output of self.command
69
+ def self.parse(output)
70
+ volumes = []
71
+
72
+ # lvs columns will become lv attributes
73
+ attributes = YAML.load_file(File.dirname(__FILE__) + '/' + ATTRIBUTES)
74
+
75
+ output.split("\n").each do |line|
76
+ line.strip!
77
+ # each line of output is comma separated values
78
+ values = line.split(",")
79
+ # empty values to nil
80
+ values.map! do |value|
81
+ if value.empty?
82
+ nil
83
+ else
84
+ value
85
+ end
86
+ end
87
+
88
+ args = {}
89
+ # match up attribute => value
90
+ values.size.times do |i|
91
+ method = attributes[i][:method].to_sym
92
+ value = values[i]
93
+ # use our hints first for type conversion
94
+ if attributes[i][:type_hint] == "Integer"
95
+ value = value.to_i
96
+ elsif attributes[i][:type_hint] == "Float"
97
+ value = value.to_f
98
+ end
99
+ args[method] = value
100
+ end
101
+
102
+ # now we force some types to ints since we've requested them as bytes
103
+ # without a suffix
104
+ args[:size] = args[:size].to_i
105
+
106
+ # we resolve the attributes line to nicer symbols
107
+ args.merge!(parse_vg_attr(args[:attr]))
108
+
109
+ # finally build our object
110
+ volume = VolumeGroup.new(args)
111
+
112
+ if block_given?
113
+ yield volume
114
+ else
115
+ volumes << volume
116
+ end
117
+ end
118
+
119
+ volumes
120
+ end # parse
121
+
122
+ end # class
123
+ end # module
data/lib/lvm.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'lvm/external'
2
+
3
+ module LVMWrapper
4
+ VERSION = '0.0.1'
5
+ COMPAT_VERSION = '2.02.26-RHEL5'
6
+
7
+ class LVM
8
+ class LVM < Exception; end
9
+
10
+ attr_accessor :command
11
+
12
+ DEFAULT_COMMAND = '/sbin/lvm'
13
+
14
+ def initialize(args = {})
15
+ @command = args[:command] || DEFAULT_COMMAND
16
+ @debug = args[:debug] || false
17
+ end
18
+
19
+ def raw(args) #:nodoc:
20
+ External.cmd(args)
21
+ end
22
+
23
+ def cmd(args)
24
+ to_exec = "#{@command} #{args}"
25
+ puts "Going to execute `#{to_exec}`" if @debug
26
+ raw(to_exec)
27
+ end
28
+
29
+ class<<self
30
+ def raw(args)
31
+ new(args).raw(args)
32
+ end
33
+ def cmd(args)
34
+ new(args).raw(args)
35
+ end
36
+ end # self
37
+
38
+ end # class
39
+ end # module
data/test/test_lvm.rb ADDED
File without changes
data.tar.gz.sig ADDED
Binary file