storcs 0.0.1 → 0.0.2
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/.gitignore +2 -0
- data/README.textile +68 -1
- data/lib/storcs/device.rb +8 -17
- data/lib/storcs/parsers/ibm.rb +17 -0
- data/lib/storcs/summable_sizes.rb +33 -0
- data/lib/storcs/version.rb +1 -1
- data/lib/storcs.rb +1 -0
- data/spec/device_spec.rb +6 -0
- data/spec/formatter_spec.rb +2 -0
- data/spec/parsers/df_nas_spec.rb +3 -0
- data/spec/parsers/equalogic_spec.rb +1 -0
- data/spec/parsers/ibm_spec.rb +5 -4
- metadata +4 -3
data/README.textile
CHANGED
@@ -9,6 +9,8 @@ Storcs (STORage Calculation Service) is a gem designed to help you manage centra
|
|
9
9
|
|
10
10
|
Those calulcations are performed on a state file for each device you have. The way you generate the state file depends on the type of the storage system.
|
11
11
|
|
12
|
+
*Calculations are performed in bytes*.
|
13
|
+
|
12
14
|
For the moment, it has only been tested on the following devices :
|
13
15
|
* IBM DS4000 series (tested with a DS3400, a DS4100, a DS4300 and a DS4500)
|
14
16
|
* Equalogic PS5000 and PS6000 series (tested with a PS5000XV and a PS6000)
|
@@ -23,11 +25,72 @@ gem install storcs-*.gem
|
|
23
25
|
|
24
26
|
h2. Parsers
|
25
27
|
|
26
|
-
A parser is an adapter specific to your device. Parser implementation depends on the way you retrieve your storage informations. Basically, it takes a name and a file,
|
28
|
+
A parser is an adapter specific to your device. Parser implementation depends on the way you retrieve your storage informations. Basically, it takes a name and a file, parses the file, and builds a Storcs::Device object based on the parsed informations. For instance:
|
27
29
|
|
28
30
|
bc. parsed = Storcs::Parsers::DfNas.new("my-shiny-nas.example.com", "/path/to/df-k_output.txt")
|
29
31
|
device = parsed.device
|
30
32
|
|
33
|
+
h2. Sizes
|
34
|
+
|
35
|
+
Now that you have a "device", you can use the common methods to retrieve informations on this device :
|
36
|
+
|
37
|
+
bc. device.name
|
38
|
+
=> "my-shiny-nas.example.com"
|
39
|
+
device.used
|
40
|
+
=> 612376576
|
41
|
+
device.size
|
42
|
+
=> 814572544
|
43
|
+
device.free
|
44
|
+
=> 202195968
|
45
|
+
|
46
|
+
ZOMG, a sum and a percentage !!
|
47
|
+
|
48
|
+
Well, ok, that's not so impressive. The interesting part comes when you have nested " " " devices" " ", like in most storage devices on the market : SAN devices have enclosures, and / or raid groups, and / or arrays of disks, etc.
|
49
|
+
|
50
|
+
Let's take an example without parser (parsers do all this magic for you) :
|
51
|
+
|
52
|
+
bc. dev = Storcs::Device.new("my-device")
|
53
|
+
=> #<Storcs::Device:0x9050a9c @name="my-device", @children=[]>
|
54
|
+
array1 = Storcs::Device.new("array1"); array1.real_size=100; array1.real_used=50; array1
|
55
|
+
=> #<Storcs::Device:0x98ee258 @name="array1", @children=[], @real_size=100, @real_used=50>
|
56
|
+
array2 = Storcs::Device.new("array2"); array2.real_size=100; array2.real_used=25; array2
|
57
|
+
=> #<Storcs::Device:0x98e3c7c @name="array2", @children=[], @real_size=100, @real_used=25>
|
58
|
+
dev.children << array1; dev.children << array2
|
59
|
+
=> ...
|
60
|
+
dev.size
|
61
|
+
=> 200
|
62
|
+
dev.used
|
63
|
+
=> 75
|
64
|
+
dev.percent_used
|
65
|
+
=> 37.5
|
66
|
+
|
67
|
+
Imagine you have nested storage "devices", with 3, 4 or 5 levels of recursions, you probably don't want to screw your app with all those calculations.
|
68
|
+
|
69
|
+
One more thing, some parsers may parse and thus provide extra attributes which are not standard / available in others. For instance, IBM parser provides ability to know the RAID type of an array, and the size of unassigned disks:
|
70
|
+
|
71
|
+
bc. ibm_device.childrens.map(&:raid)
|
72
|
+
=> ["5", "1", "5", "5"]
|
73
|
+
ibm_device.unassigned
|
74
|
+
=> 3142530966
|
75
|
+
|
76
|
+
h2. Helper
|
77
|
+
|
78
|
+
storcs gem comes with a helper method to help you format sizes, since everything is calculated in bytes internally and you probably don't want that in your final rendering.
|
79
|
+
|
80
|
+
bc. include Storcs::Formatter
|
81
|
+
pretty_size(1)
|
82
|
+
=> "1.0bytes"
|
83
|
+
pretty_size(12345)
|
84
|
+
=> "12.1Kb"
|
85
|
+
pretty_size(825388561334)
|
86
|
+
=> "768.7Gb"
|
87
|
+
pretty_size(12285713148014)
|
88
|
+
=> "11.2Tb"
|
89
|
+
|
90
|
+
More options can be added on the formatter side on demand.
|
91
|
+
|
92
|
+
h2. Available parsers
|
93
|
+
|
31
94
|
h3. df -k
|
32
95
|
|
33
96
|
This is the simplest parser. It analyzes the output of a @df -Pk@ command on a mounted filesystem, possibly a local filesystem or a NAS attached with NFS or CIFS:
|
@@ -45,3 +108,7 @@ bc. SMcli 192.0.0.15 -c 'show storagesubsystem profile;' > file.txt
|
|
45
108
|
h3. Equalogic PS5000 and PS6000
|
46
109
|
|
47
110
|
It parses the output of a 'show' command in the shell. Unfortunately, I wasn't able to run it remotely since my network administrator didn't allow me. So I have a script based on the "expect" tool with the @show@ command inside.
|
111
|
+
|
112
|
+
h3. Yours!
|
113
|
+
|
114
|
+
If you have a storage device you'd like to see in this list, feel free to open an issue here with at least an example 'profile' file (this can be the output of a command, a plain text or structured file) and the result I should find (used size/total size at least). Maybe somebody else (or me) will be interested in building a parser for your device.
|
data/lib/storcs/device.rb
CHANGED
@@ -1,24 +1,13 @@
|
|
1
1
|
module Storcs
|
2
2
|
class Device
|
3
|
-
|
3
|
+
extend SummableSizes
|
4
|
+
|
5
|
+
attr_accessor :name, :children, :raid
|
6
|
+
summable_sizes :size, :used, :unassigned
|
4
7
|
|
5
8
|
def initialize(name)
|
6
9
|
@name = name
|
7
10
|
@children = []
|
8
|
-
@real_used = nil
|
9
|
-
@real_size = nil
|
10
|
-
end
|
11
|
-
|
12
|
-
def size
|
13
|
-
real_size || children.inject(0) do |memo,child|
|
14
|
-
memo + child.size
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def used
|
19
|
-
real_used || children.inject(0) do |memo,child|
|
20
|
-
memo + child.used
|
21
|
-
end
|
22
11
|
end
|
23
12
|
|
24
13
|
def free
|
@@ -26,11 +15,13 @@ module Storcs
|
|
26
15
|
end
|
27
16
|
|
28
17
|
def percent_used
|
29
|
-
|
18
|
+
return 0 unless size.integer? && size > 0
|
19
|
+
(100 * used.to_f / size).round(2)
|
30
20
|
end
|
31
21
|
|
32
22
|
def percent_free
|
33
|
-
|
23
|
+
return 0 unless size.integer? && size > 0
|
24
|
+
(100 * free.to_f / size).round(2)
|
34
25
|
end
|
35
26
|
end
|
36
27
|
end
|
data/lib/storcs/parsers/ibm.rb
CHANGED
@@ -12,6 +12,7 @@ module Storcs::Parsers
|
|
12
12
|
|
13
13
|
def parse!(content)
|
14
14
|
@device.children = arrays
|
15
|
+
@device.real_unassigned = unassigned
|
15
16
|
end
|
16
17
|
|
17
18
|
def sections
|
@@ -54,5 +55,21 @@ module Storcs::Parsers
|
|
54
55
|
end.compact
|
55
56
|
@arrays
|
56
57
|
end
|
58
|
+
|
59
|
+
def unassigned
|
60
|
+
return @unassigned if @unassigned
|
61
|
+
@unassigned = 0
|
62
|
+
in_unassigned = false
|
63
|
+
sections[:drives].map do |line|
|
64
|
+
if line.match /Unassigned/
|
65
|
+
in_unassigned = true
|
66
|
+
elsif line.match /^\s*$/
|
67
|
+
in_unassigned = false
|
68
|
+
elsif in_unassigned && line.match(/Usable capacity:\s+(.+)$/)
|
69
|
+
@unassigned += parse_size($1)
|
70
|
+
end
|
71
|
+
end.compact
|
72
|
+
@unassigned
|
73
|
+
end
|
57
74
|
end
|
58
75
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Storcs
|
2
|
+
module SummableSizes
|
3
|
+
# Avoids defining recursively summable columns again
|
4
|
+
# and again. Usage:
|
5
|
+
#
|
6
|
+
# class Device
|
7
|
+
# summable_sizes :size, :used, :blah
|
8
|
+
# ...
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# For instance, here's the result for 'used' size:
|
12
|
+
#
|
13
|
+
# def used
|
14
|
+
# real_used || children.inject(0) do |memo,child|
|
15
|
+
# memo + child.used
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
def summable_sizes(*columns)
|
20
|
+
columns.each do |column|
|
21
|
+
class_eval <<-EOF
|
22
|
+
attr_accessor :real_#{column}
|
23
|
+
def #{column}
|
24
|
+
real_#{column} || children.inject(0) do |memo,child|
|
25
|
+
memo + child.#{column}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
EOF
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
data/lib/storcs/version.rb
CHANGED
data/lib/storcs.rb
CHANGED
data/spec/device_spec.rb
CHANGED
@@ -26,4 +26,10 @@ describe Storcs::Device do
|
|
26
26
|
@bay.percent_used.should == 55
|
27
27
|
@bay.percent_free.should == 45
|
28
28
|
end
|
29
|
+
|
30
|
+
it "ensures a device with no size doesn't screw up on calculations" do
|
31
|
+
device = Storcs::Device.new("bay")
|
32
|
+
device.percent_used.should == 0
|
33
|
+
device.percent_free.should == 0
|
34
|
+
end
|
29
35
|
end
|
data/spec/formatter_spec.rb
CHANGED
data/spec/parsers/df_nas_spec.rb
CHANGED
@@ -8,5 +8,8 @@ describe Storcs::Parsers::DfNas do
|
|
8
8
|
parsed.device.used.should == 612376576
|
9
9
|
parsed.device.size.should == 814572544
|
10
10
|
parsed.device.free.should == 202195968
|
11
|
+
parsed.device.unassigned.should == 0
|
12
|
+
parsed.device.percent_free.should == 24.82
|
13
|
+
parsed.device.percent_used.should == 75.18
|
11
14
|
end
|
12
15
|
end
|
data/spec/parsers/ibm_spec.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
2
2
|
|
3
3
|
describe Storcs::Parsers::Ibm do
|
4
|
-
models = {"DS4500" => { :num_arrays => 15, :num_sections => 11,
|
5
|
-
:
|
4
|
+
models = {"DS4500" => { :num_arrays => 15, :num_sections => 11, :bay_size => 12285713148014,
|
5
|
+
:bay_used => 11460324586680, :bay_free => 825388561334, :bay_unassigned => 731389980835,
|
6
6
|
:first_array_name => "ARRAY 1", :first_array_raid => "5", :first_array_ld_num => 17,
|
7
7
|
:ld_name => "array1_LUN0_app01_5G", :ld_size => 5368709120,
|
8
8
|
:array_size => 1316496596795, :array_used => 655826473713 },
|
9
|
-
"DS3400" => { :num_arrays => 1, :num_sections => 9,
|
10
|
-
:
|
9
|
+
"DS3400" => { :num_arrays => 1, :num_sections => 9, :bay_size => 2395702692938,
|
10
|
+
:bay_used => 2395702692938, :bay_free => 0, :bay_unassigned => 0,
|
11
11
|
:first_array_name => "ARRAY 1", :first_array_raid => "5", :first_array_ld_num => 2,
|
12
12
|
:ld_name => "SAN01_LUN1", :ld_size => 1099511627776,
|
13
13
|
:array_size => 2395702692938, :array_used => 2395702692938 }
|
@@ -29,6 +29,7 @@ describe Storcs::Parsers::Ibm do
|
|
29
29
|
bay.size.should == @expected[:bay_size]
|
30
30
|
bay.used.should == @expected[:bay_used]
|
31
31
|
bay.free.should == @expected[:bay_free]
|
32
|
+
bay.unassigned.should == @expected[:bay_unassigned]
|
32
33
|
end
|
33
34
|
|
34
35
|
it "divides a profile file into small sections" do
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Jean-Baptiste Barth
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-
|
17
|
+
date: 2011-03-29 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -52,6 +52,7 @@ files:
|
|
52
52
|
- lib/storcs/parsers/equalogic.rb
|
53
53
|
- lib/storcs/parsers/ibm.rb
|
54
54
|
- lib/storcs/parsers/utils.rb
|
55
|
+
- lib/storcs/summable_sizes.rb
|
55
56
|
- lib/storcs/version.rb
|
56
57
|
- spec/data/df_nas.txt
|
57
58
|
- spec/data/equalogic_PS5000XV.txt
|