thinp_xml 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NThkNzE3MWRlMTA1YjQxZDUyOTQ2NWQ3Yjk2YTU1ZWExYjRkNGIyZA==
4
+ ZTdjOGQ5NDUyYzJiYzY2MzExMjU4MzhjNWZkMjg5MzcyOTVlZDU1ZA==
5
5
  data.tar.gz: !binary |-
6
- MjZiNDliOTczYjEyNTk3NTA4YjhmNTBkYmJmY2EwMmIxY2M1OTVlMw==
6
+ ODNkZmU4YzE2OTQxZThjYmFlZjkxZTZjOTdkMTBhOWYyOTgwNmY0Mw==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZGE1ZWU2YWIxZDI3ZDNmODAzMDc4YjJjMTk2YWQ5ZTMyMzM3MmMyZWQzNDBl
10
- MTlhZTZiYzBhZDc1NmVjMDc4OWY4ODAwZTQzNzMzOWJhNzE3MGQ3ODI1NTgz
11
- YWE1YmRkN2VkOTU0YTE2ZWYyOWY3MzEwNmMwMjdkNzkzMGZiZTk=
9
+ ZWI5OWZjNTEzZjUyYzQ5YzM1YTViYmIxMzcyMjZjYTg1ZDU4MzYyNjc3MzI4
10
+ NTAwNDU4ZjYzMGNhOGU2Njg0MWYzZjQxZWU1OWUyYjhmZTBkYWY4YzBjMjNh
11
+ ODJmNzAxMmYyOWY4ZDVjYTEwMTZkMGU5NGJkMWIzY2U0ZGE1MGM=
12
12
  data.tar.gz: !binary |-
13
- YTM5Yjk2ZjkyZmY3YzY3ZWM2ZTFkZmM1ZDEzNWE1ZjRhMmZiNGMxOTNkY2Nl
14
- OGY4NjQyMzE2NzliNWRkZjZkYjNjNzY4Njg5ZjE3YzIzNWRiMzFlZWQ3MmYw
15
- MzVhYzJlOWYyZjEzYjg4MjhmODcyZGQ5YWVlYzg2ZjJmYTljM2U=
13
+ NTNlOGY3NDU4MWYzYWY3YjViNjJkYjE5ZTNiMmM2Y2NkZmM5NGYwMTdhZjRj
14
+ NzdjMTI1NjM1NWU1ZDI3ODU3ZjkxZGYzZGUyN2RlMjMwNmM3YTg4Mjg3NWQ4
15
+ NDBkYWZlMjA5NmU3MmFhOGE3Nzk2NzZlNGM0YTljOTZjNTgwMzc=
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/bin/cache_xml ADDED
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thinp_xml/cache_xml'
4
+ require 'thinp_xml/distribution'
5
+ require 'ejt_command_line'
6
+
7
+ include ThinpXML
8
+
9
+ #----------------------------------------------------------------
10
+
11
+ CacheCommandLine = CommandLine::Parser.new do
12
+ value_type :string do |str|
13
+ str
14
+ end
15
+
16
+ value_type :int do |str|
17
+ Integer(str)
18
+ end
19
+
20
+ value_type :distribution do |str|
21
+ parse_distribution(str)
22
+ end
23
+
24
+ value_type :layout do |str|
25
+ case str
26
+ when 'random'
27
+ :random
28
+
29
+ when 'linear'
30
+ :linear
31
+
32
+ else
33
+ raise "unknown mapping layout '#{str}'"
34
+ end
35
+ end
36
+
37
+ value_type :percentage do |str|
38
+ i = Integer(str)
39
+ raise "invalid percentage value (#{i})" if i < 0 || i > 100
40
+ i
41
+ end
42
+
43
+ simple_switch :help, '--help', '-h'
44
+ value_switch :uuid, :string, '--uuid'
45
+ value_switch :block_size, :int, '--block-size'
46
+ value_switch :nr_cache_blocks, :distribution, '--nr-cache-blocks'
47
+ value_switch :nr_mappings, :distribution, '--nr-mappings'
48
+ value_switch :mapping_policy, :layout, '--layout'
49
+ value_switch :dirty_percentage, :percentage, '--dirty-percent'
50
+
51
+ global do
52
+ switches :help
53
+ end
54
+
55
+ command :create do
56
+ switches :nr_cache_blocks, :uuid, :block_size, :nr_mappings, :mapping_policy, :dirty_percentage
57
+ end
58
+ end
59
+
60
+ #--------------------------------
61
+
62
+ class Dispatcher
63
+ include CacheXML
64
+
65
+ def global_command(opts, args)
66
+ if args.size > 0
67
+ die "unknown command '#{args[0]}'"
68
+ else
69
+ if opts[:help]
70
+ help(STDOUT)
71
+ else
72
+ die "no command given"
73
+ end
74
+ end
75
+ end
76
+
77
+ def create(opts, args)
78
+ b = Builder.new
79
+ b.uuid = opts.fetch(:uuid, '')
80
+ b.nr_cache_blocks = opts.fetch(:nr_cache_blocks, 0).to_i
81
+ b.block_size = opts.fetch(:block_size, 128)
82
+ b.nr_mappings = opts.fetch(:nr_mappings, 0)
83
+ b.mapping_policy = opts.fetch(:mapping_policy, :random)
84
+ b.dirty_percentage = opts.fetch(:dirty_percentage, 0)
85
+ md = b.generate
86
+ write_xml(md, STDOUT)
87
+ end
88
+
89
+ private
90
+ def die(msg)
91
+ STDERR.puts msg
92
+ exit(1)
93
+ end
94
+
95
+ def help(out)
96
+ out.write <<EOF
97
+ Manipulation of cache xml format metadata
98
+ --help, -h: Show this message
99
+ EOF
100
+ end
101
+ end
102
+
103
+ def parse_command_line(dispatcher, *args)
104
+ CacheCommandLine.parse(dispatcher, *args)
105
+ end
106
+
107
+ def top_level_handler(&block)
108
+ begin
109
+ block.call
110
+ rescue => e
111
+ STDERR.puts e.message
112
+ exit 1
113
+ end
114
+
115
+ exit 0
116
+ end
117
+
118
+ #----------------------------------------------------------------
119
+
120
+ top_level_handler do
121
+ dispatcher = Dispatcher.new
122
+ parse_command_line(dispatcher, *ARGV)
123
+ end
124
+
125
+ #----------------------------------------------------------------
data/bin/thinp_xml CHANGED
@@ -4,6 +4,8 @@ require 'thinp_xml'
4
4
  require 'thinp_xml/distribution'
5
5
  require 'ejt_command_line'
6
6
 
7
+ include ThinpXML
8
+
7
9
  #----------------------------------------------------------------
8
10
 
9
11
  XMLCommandLine = CommandLine::Parser.new do
@@ -15,15 +17,8 @@ XMLCommandLine = CommandLine::Parser.new do
15
17
  Integer(str)
16
18
  end
17
19
 
18
- UNIFORM_REGEX = /uniform\[(\d+)..(\d+)\]/
19
-
20
20
  value_type :int_distribution do |str|
21
- m = UNIFORM_REGEX.match(str)
22
- if m
23
- ThinpXML::UniformDistribution.new(m[1].to_i, m[2].to_i + 1)
24
- else
25
- Integer(str)
26
- end
21
+ parse_distribution(str)
27
22
  end
28
23
 
29
24
  simple_switch :help, '--help', '-h'
@@ -41,6 +36,8 @@ XMLCommandLine = CommandLine::Parser.new do
41
36
  end
42
37
  end
43
38
 
39
+ #--------------------------------
40
+
44
41
  class Dispatcher
45
42
  include ThinpXML
46
43
 
@@ -0,0 +1,65 @@
1
+ Feature: I can create new metadata
2
+
3
+ Scenario: Create a valid superblock with no mappings
4
+ When I cache_xml create
5
+ Then the stdout should contain:
6
+ """
7
+ <superblock uuid="" block_size="128" nr_cache_blocks="0">
8
+ </superblock>
9
+ """
10
+
11
+ Scenario: Create a valid superblock with specified uuid
12
+ When I cache_xml create --uuid 'one two three'
13
+ Then the stdout should contain:
14
+ """
15
+ <superblock uuid="one two three" block_size="128" nr_cache_blocks="0">
16
+ </superblock>
17
+ """
18
+
19
+ Scenario: Create a valid superblock with specified block size
20
+ When I cache_xml create --block-size 512
21
+ Then the stdout should contain:
22
+ """
23
+ <superblock uuid="" block_size="512" nr_cache_blocks="0">
24
+ </superblock>
25
+ """
26
+
27
+ Scenario: Take an integer for the number of cached blocks
28
+ When I cache_xml create --nr-cache-blocks 345
29
+ Then the stdout should contain:
30
+ """
31
+ <superblock uuid="" block_size="128" nr_cache_blocks="345">
32
+ </superblock>
33
+ """
34
+
35
+ Scenario: Take a uniform distribution for the number of cached blocks
36
+ When I cache_xml create --nr-cache-blocks uniform[123..124]
37
+ Then the stdout should contain:
38
+ """
39
+ <superblock uuid="" block_size="128" nr_cache_blocks="123">
40
+ </superblock>
41
+ """
42
+
43
+ Scenario: Take an integer for the number of mappings
44
+ When I cache_xml create --nr-cache-blocks 3 --nr-mappings 3 --layout linear
45
+ Then the stdout should contain:
46
+ """
47
+ <superblock uuid="" block_size="128" nr_cache_blocks="3">
48
+ <mapping cache_block="0" origin_block="0" dirty="false"/>
49
+ <mapping cache_block="1" origin_block="1" dirty="false"/>
50
+ <mapping cache_block="2" origin_block="2" dirty="false"/>
51
+ </superblock>
52
+ """
53
+
54
+ Scenario: Take a percentage for the number of dirty mappings
55
+ When I cache_xml create --nr-cache-blocks 3 --nr-mappings 3 --layout linear --dirty-percent 100
56
+ Then the stdout should contain:
57
+ """
58
+ <superblock uuid="" block_size="128" nr_cache_blocks="3">
59
+ <mapping cache_block="0" origin_block="0" dirty="true"/>
60
+ <mapping cache_block="1" origin_block="1" dirty="true"/>
61
+ <mapping cache_block="2" origin_block="2" dirty="true"/>
62
+ </superblock>
63
+ """
64
+
65
+
@@ -0,0 +1,17 @@
1
+ Feature: The tool should be helpful
2
+
3
+ Scenario: --help prints usage to stdout
4
+ When I cache_xml --help
5
+ Then the stdout should contain:
6
+ """
7
+ Manipulation of cache xml format metadata
8
+ --help, -h: Show this message
9
+ """
10
+
11
+ Scenario: Unknown sub commands cause fail
12
+ When I cache_xml unleashtheearwigs
13
+ Then it should fail
14
+ And the stderr should contain:
15
+ """
16
+ unknown command 'unleashtheearwigs'
17
+ """
@@ -2,6 +2,10 @@ When(/^I thinp_xml (.*)$/) do |cmd|
2
2
  run_simple(unescape("thinp_xml #{cmd}"), false)
3
3
  end
4
4
 
5
+ When(/^I cache_xml (.*)$/) do |cmd|
6
+ run_simple(unescape("cache_xml #{cmd}"), false)
7
+ end
8
+
5
9
  Then(/^it should pass$/) do
6
10
  assert_success(true)
7
11
  end
File without changes
File without changes
@@ -0,0 +1,92 @@
1
+ require 'thinp_xml/cache/metadata'
2
+
3
+ #----------------------------------------------------------------
4
+
5
+ module CacheXML
6
+ class Builder
7
+ attr_accessor :uuid, :block_size, :nr_cache_blocks, :policy_name
8
+ attr_reader :mapping_policy, :nr_mappings, :dirty_percentage
9
+
10
+ def initialize
11
+ @uuid = ''
12
+ @block_size = 128
13
+ @nr_cache_blocks = 0
14
+ @policy_name = 'mq'
15
+ @mapping_policy = :random
16
+ @nr_mappings = 0
17
+ @dirty_percentage = 0
18
+ end
19
+
20
+ def generate
21
+ superblock = Superblock.new(@uuid, @block_size.to_i, @nr_cache_blocks.to_i, @policy_name)
22
+ mappings = []
23
+
24
+ case @mapping_policy
25
+ when :linear
26
+ cb = 0
27
+ ob = safe_rand(@nr_cache_blocks - @nr_mappings)
28
+
29
+ @nr_mappings.times do
30
+ dirty = safe_rand(100) < @dirty_percentage
31
+ mappings << Mapping.new(cb, ob, dirty)
32
+ cb += 1
33
+ ob += 1
34
+ end
35
+
36
+ when :random
37
+ origin_blocks = []
38
+
39
+ ob = 0
40
+ @nr_mappings.times do
41
+ origin_blocks << ob
42
+ ob += 1
43
+ end
44
+
45
+ 0.upto(@nr_mappings - 1) do |n|
46
+ tmp = origin_blocks[n]
47
+
48
+ index = n + rand(@nr_mappings - n)
49
+ origin_blocks[n] = origin_blocks[index]
50
+ origin_blocks[index] = tmp
51
+
52
+ dirty = safe_rand(100) < @dirty_percentage
53
+ mappings << Mapping.new(n, origin_blocks[n], dirty)
54
+ end
55
+
56
+ else
57
+ raise "unknown mapping policy"
58
+ end
59
+
60
+ hints = []
61
+
62
+ Metadata.new(superblock, mappings, hints)
63
+ end
64
+
65
+ def mapping_policy=(sym)
66
+ raise "mapping policy must be :random or :linear" unless valid_policy(sym)
67
+ @mapping_policy = sym
68
+ end
69
+
70
+ def nr_mappings=(n)
71
+ n = n.to_i
72
+ #raise "nr_mappings must not exceed nr_cache_blocks" if n > @nr_cache_blocks
73
+ @nr_mappings = n
74
+ end
75
+
76
+ def dirty_percentage=(n)
77
+ raise "invalid percentage (#{n})" if n < 0 || n > 100
78
+ @dirty_percentage = n
79
+ end
80
+
81
+ private
82
+ def valid_policy(sym)
83
+ [:random, :linear].member?(sym)
84
+ end
85
+
86
+ def safe_rand(n)
87
+ n == 0 ? 0 : rand(n)
88
+ end
89
+ end
90
+ end
91
+
92
+ #----------------------------------------------------------------
@@ -0,0 +1,43 @@
1
+ require 'thinp_xml/cache/metadata'
2
+ require 'thinp_xml/emitter'
3
+
4
+ #----------------------------------------------------------------
5
+
6
+ module CacheXML
7
+ module CacheEmitterDetail
8
+ class CacheEmitter
9
+ def initialize(out)
10
+ @e = ThinpXML::Base::Emitter.new(out)
11
+ end
12
+
13
+ def emit_superblock(sb, &block)
14
+ @e.emit_tag(sb, 'superblock', :uuid, :block_size, :nr_cache_blocks, &block)
15
+ end
16
+
17
+ def emit_mapping(m)
18
+ @e.emit_tag(m, 'mapping', :cache_block, :origin_block, :dirty)
19
+ end
20
+
21
+ def emit_hint(h)
22
+ @e.emit_line("<hint cache_block=\"#{m.cache_block}\" data=\"#{h.encoded_data}\"\>")
23
+ end
24
+ end
25
+ end
26
+
27
+ #--------------------------------
28
+
29
+ def write_xml(metadata, io)
30
+ e = CacheEmitterDetail::CacheEmitter.new(io)
31
+ e.emit_superblock(metadata.superblock) do
32
+ metadata.mappings.each do |m|
33
+ e.emit_mapping(m)
34
+ end
35
+
36
+ metadata.hints.each do |h|
37
+ e.emit_hint(h)
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ #----------------------------------------------------------------
@@ -0,0 +1,22 @@
1
+ module CacheXML
2
+ SUPERBLOCK_FIELDS = [[:uuid, :string],
3
+ [:block_size, :int],
4
+ [:nr_cache_blocks, :int],
5
+ [:policy_name, :string]]
6
+
7
+ MAPPING_FIELDS = [[:cache_block, :int],
8
+ [:origin_block, :int],
9
+ [:dirty, :bool]]
10
+
11
+ HINT_FIELDS = [[:cache_block, :int],
12
+ [:encoded_data, :string]]
13
+
14
+ def self.field_names(flds)
15
+ flds.map {|p| p[0]}
16
+ end
17
+
18
+ Superblock = Struct.new(*field_names(SUPERBLOCK_FIELDS))
19
+ Mapping = Struct.new(*field_names(MAPPING_FIELDS))
20
+ Hint = Struct.new(*field_names(HINT_FIELDS))
21
+ Metadata = Struct.new(:superblock, :mappings, :hints)
22
+ end
@@ -0,0 +1,53 @@
1
+ require 'thinp_xml/listener'
2
+ require 'thinp_xml/cache/metadata'
3
+
4
+ require 'rexml/document'
5
+ require 'rexml/streamlistener'
6
+
7
+ #----------------------------------------------------------------
8
+
9
+ module ThinpXML
10
+ module CacheParseDetail
11
+ class Listener
12
+ include Base::ListenerUtils
13
+ include REXML::StreamListener
14
+
15
+ attr_reader :metadata
16
+
17
+ def initialize
18
+ @metadata = Metdadata.new(nil, [], [])
19
+ end
20
+
21
+ def tag_start(tag, args)
22
+ attr = to_hash(args)
23
+
24
+ case tag
25
+ when 'superblock'
26
+ @metadata.superblock = Superblock.new(*get_fields(attr, SUPERBLOCK_FIELDS))
27
+
28
+ when 'mapping'
29
+ m = Mapping.new(*get_fields(attr, MAPPING_FIELDS))
30
+ @metadata.mappings << m
31
+
32
+ when 'hint'
33
+ h = Hint.new(*get_fields(attr, HINT_FIELDS))
34
+ @metadata.hints << h
35
+
36
+ else
37
+ raise "unhandled tag '#{tag} #{attr.map {|x| x.inspect}.join(' ')}'"
38
+ end
39
+ end
40
+
41
+ def tag_end(tag)
42
+ end
43
+ end
44
+ end
45
+
46
+ def read_xml(io)
47
+ l = CacheParseDetail::Listener.new
48
+ REXML::Document.parse_stream(io, l)
49
+ l.metadata
50
+ end
51
+ end
52
+
53
+ #----------------------------------------------------------------
@@ -0,0 +1,5 @@
1
+ require 'thinp_xml/cache/builder'
2
+ require 'thinp_xml/cache/emit'
3
+ require 'thinp_xml/cache/metadata'
4
+ require 'thinp_xml/cache/parse'
5
+ require 'thinp_xml/version'
@@ -7,10 +7,23 @@ module ThinpXML
7
7
  end
8
8
  end
9
9
 
10
- class UniformDistribution
10
+ class ConstDistribution < Distribution
11
+ def initialize(v)
12
+ raise "ConstDistribution must be constructed with an integer" if !v.kind_of?(Integer)
13
+ @v = v
14
+ end
15
+
16
+ def generate
17
+ @v
18
+ end
19
+ end
20
+
21
+ class UniformDistribution < Distribution
11
22
  attr_accessor :start, :stop
12
23
 
13
24
  def initialize(start, stop)
25
+ raise "invalid range [#{start}..#{stop})" unless start < stop
26
+
14
27
  @start = start
15
28
  @stop = stop
16
29
  end
@@ -18,9 +31,18 @@ module ThinpXML
18
31
  def generate
19
32
  @start + rand(@stop - @start)
20
33
  end
34
+ end
21
35
 
22
- def to_i
23
- generate
36
+ #--------------------------------
37
+
38
+ UNIFORM_REGEX = /uniform\[(\d+)..(\d+)\]/
39
+
40
+ def parse_distribution(str)
41
+ m = UNIFORM_REGEX.match(str)
42
+ if m
43
+ ThinpXML::UniformDistribution.new(m[1].to_i, m[2].to_i)
44
+ else
45
+ ConstDistribution.new(Integer(str))
24
46
  end
25
47
  end
26
48
  end
@@ -0,0 +1,40 @@
1
+ #----------------------------------------------------------------
2
+
3
+ module ThinpXML
4
+ module Base
5
+ class Emitter
6
+ def initialize(out)
7
+ @out = out
8
+ @indent = 0
9
+ end
10
+
11
+ def emit_tag(obj, tag, *fields, &block)
12
+ expanded = fields.map {|fld| "#{fld}=\"#{obj.send(fld)}\""}
13
+ if block.nil?
14
+ emit_line "<#{tag} #{expanded.join(' ')}/>"
15
+ else
16
+ emit_line "<#{tag} #{expanded.join(' ')}>"
17
+ push
18
+ yield unless block.nil?
19
+ pop
20
+ emit_line "</#{tag}>"
21
+ end
22
+ end
23
+
24
+ private
25
+ def emit_line(str)
26
+ @out.puts((' ' * @indent) + str)
27
+ end
28
+
29
+ def push
30
+ @indent += 2
31
+ end
32
+
33
+ def pop
34
+ @indent -= 2
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ #----------------------------------------------------------------
@@ -0,0 +1,51 @@
1
+ #----------------------------------------------------------------
2
+
3
+ module ThinpXML
4
+ module Base
5
+ module ListenerUtils
6
+ def to_hash(pairs)
7
+ r = Hash.new
8
+ pairs.each do |p|
9
+ r[p[0].intern] = p[1]
10
+ end
11
+ r
12
+ end
13
+
14
+ def get_fields(attr, flds)
15
+ flds.map do |n,t|
16
+ case t
17
+ when :int
18
+ attr[n].to_i
19
+
20
+ when :bool
21
+ case attr[n]
22
+ when 'true'
23
+ true
24
+ when 'false'
25
+ false
26
+ else
27
+ raise "bad boolean value '#{attr[n]}'"
28
+ end
29
+
30
+ when :string
31
+ attr[n]
32
+
33
+ when :object
34
+ attr[n]
35
+
36
+ else
37
+ raise "unknown field type"
38
+ end
39
+ end
40
+ end
41
+
42
+ def text(data)
43
+ return if data =~ /^\w*$/ # ignore whitespace
44
+ abbrev = data[0..40] + (data.length > 40 ? "..." : "")
45
+ puts " text : #{abbrev.inspect}"
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ #----------------------------------------------------------------
File without changes
@@ -1,4 +1,4 @@
1
- require 'thinp_xml/metadata'
1
+ require 'thinp_xml/thinp/metadata'
2
2
 
3
3
  #----------------------------------------------------------------
4
4
 
@@ -0,0 +1,48 @@
1
+ require 'thinp_xml/thinp/metadata'
2
+ require 'thinp_xml/emitter'
3
+
4
+ #----------------------------------------------------------------
5
+
6
+ module ThinpXML
7
+ module EmitterDetail
8
+ class ThinpEmitter
9
+ def initialize(out)
10
+ @e = ThinpXML::Base::Emitter.new(out)
11
+ end
12
+
13
+ def emit_superblock(sb, &block)
14
+ @e.emit_tag(sb, 'superblock', :uuid, :time, :transaction, :data_block_size, :nr_data_blocks, &block)
15
+ end
16
+
17
+ def emit_device(dev, &block)
18
+ @e.emit_tag(dev, 'device', :dev_id, :mapped_blocks, :transaction, :creation_time, :snap_time, &block)
19
+ end
20
+
21
+ def emit_mapping(m)
22
+ if m.length == 1
23
+ @e.emit_line("<single_mapping origin_block=\"#{m.origin_begin}\" data_block=\"#{m.data_begin}\" time=\"#{m.time}\"/>")
24
+ else
25
+ @e.emit_tag(m, 'range_mapping', :origin_begin, :data_begin, :length, :time)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ #--------------------------------
32
+
33
+ def write_xml(metadata, io)
34
+ e = EmitterDetail::ThinpEmitter.new(io)
35
+
36
+ e.emit_superblock(metadata.superblock) do
37
+ metadata.devices.each do |dev|
38
+ e.emit_device(dev) do
39
+ dev.mappings.each do |m|
40
+ e.emit_mapping(m)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ #----------------------------------------------------------------
File without changes
@@ -1,12 +1,17 @@
1
- require 'thinp_xml/metadata'
1
+ require 'thinp_xml/thinp/metadata'
2
+ require 'thinp_xml/listener'
3
+
2
4
  require 'rexml/document'
3
5
  require 'rexml/streamlistener'
4
6
 
7
+ #----------------------------------------------------------------
8
+
5
9
  module ThinpXML
6
10
  module ParseDetail
7
11
  include REXML
8
12
 
9
13
  class Listener
14
+ include Base::ListenerUtils
10
15
  include REXML::StreamListener
11
16
 
12
17
  attr_reader :metadata
@@ -15,31 +20,6 @@ module ThinpXML
15
20
  @metadata = Metadata.new(nil, Array.new)
16
21
  end
17
22
 
18
- def to_hash(pairs)
19
- r = Hash.new
20
- pairs.each do |p|
21
- r[p[0].intern] = p[1]
22
- end
23
- r
24
- end
25
-
26
- def get_fields(attr, flds)
27
- flds.map do |n,t|
28
- case t
29
- when :int
30
- attr[n].to_i
31
-
32
- when :string
33
- attr[n]
34
-
35
- when :object
36
- attr[n]
37
-
38
- else
39
- raise "unknown field type"
40
- end
41
- end
42
- end
43
23
 
44
24
  def tag_start(tag, args)
45
25
  attr = to_hash(args)
@@ -60,18 +40,12 @@ module ThinpXML
60
40
  @current_device.mappings << Mapping.new(*get_fields(attr, MAPPING_FIELDS))
61
41
 
62
42
  else
63
- puts "unhandled tag '#{tag} #{attr.map {|x| x.inspect}.join(', ')}'"
43
+ raise "unhandled tag '#{tag} #{attr.map {|x| x.inspect}.join(', ')}'"
64
44
  end
65
45
  end
66
46
 
67
47
  def tag_end(tag)
68
48
  end
69
-
70
- def text(data)
71
- return if data =~ /^\w*$/ # ignore whitespace
72
- abbrev = data[0..40] + (data.length > 40 ? "..." : "")
73
- puts " text : #{abbrev.inspect}"
74
- end
75
49
  end
76
50
  end
77
51
 
@@ -1,4 +1,4 @@
1
- require 'thinp_xml/metadata'
1
+ require 'thinp_xml/thinp/metadata'
2
2
 
3
3
  #----------------------------------------------------------------
4
4
 
@@ -1,3 +1,3 @@
1
1
  module ThinpXml
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.8"
3
3
  end
data/lib/thinp_xml.rb CHANGED
@@ -10,9 +10,9 @@ unless ThinpXML::tools_are_installed
10
10
  raise "please install the thin provisioning tools version #{THINP_TOOLS_VERSION}"
11
11
  end
12
12
 
13
- require 'thinp_xml/builder'
14
- require 'thinp_xml/emit'
15
- require 'thinp_xml/metadata'
16
- require 'thinp_xml/parse'
17
- require 'thinp_xml/utils'
13
+ require 'thinp_xml/thinp/builder'
14
+ require 'thinp_xml/thinp/emit'
15
+ require 'thinp_xml/thinp/metadata'
16
+ require 'thinp_xml/thinp/parse'
17
+ require 'thinp_xml/thinp/utils'
18
18
  require 'thinp_xml/version'
data/spec/builder_spec.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'thinp_xml/builder'
1
+ require 'thinp_xml/thinp/builder'
2
2
 
3
3
  #----------------------------------------------------------------
4
4
 
@@ -0,0 +1,240 @@
1
+ require 'thinp_xml/cache/builder'
2
+
3
+ #----------------------------------------------------------------
4
+
5
+ describe "CacheXML::Builder" do
6
+ def as_percentage(n, tot)
7
+ (n * 100) / tot
8
+ end
9
+
10
+ def approx_percentage(n, tot, target)
11
+ actual = as_percentage(n, tot)
12
+ (actual - target).abs < 3
13
+ end
14
+
15
+ before :each do
16
+ @b = CacheXML::Builder.new
17
+ end
18
+
19
+ describe "uuid" do
20
+ it "should be an empty string by default" do
21
+ @b.uuid.should == ''
22
+ end
23
+
24
+ it "should reflect any changes" do
25
+ uuid = 'one two three'
26
+ @b.uuid = uuid
27
+ @b.uuid.should == uuid
28
+ end
29
+
30
+ it "should generate the correct uuid" do
31
+ uuid = 'one two three'
32
+ @b.uuid = uuid
33
+ md = @b.generate
34
+ md.superblock.uuid.should == uuid
35
+ end
36
+ end
37
+
38
+ describe "block_size" do
39
+ it "should be 128 by default" do
40
+ @b.block_size.should == 128
41
+ end
42
+
43
+ it "should reflect any changes" do
44
+ @b.block_size = 512
45
+ @b.block_size.should == 512
46
+ end
47
+
48
+ it "should generate the correct block size" do
49
+ bs = 1024
50
+ @b.block_size = bs
51
+ md = @b.generate
52
+ md.superblock.block_size.should == bs
53
+ end
54
+ end
55
+
56
+ describe "nr cache blocks" do
57
+ it "should be zero by default" do
58
+ @b.nr_cache_blocks.should == 0
59
+ @b.generate.should have(0).mappings
60
+ end
61
+
62
+ it "should reflect any changes" do
63
+ n = rand(1000)
64
+ @b.nr_cache_blocks = n
65
+ @b.nr_cache_blocks.should == n
66
+ @b.generate.superblock.nr_cache_blocks.should == n
67
+ end
68
+ end
69
+
70
+ describe "policy name" do
71
+ it "should be 'mq' by default" do
72
+ @b.policy_name.should == 'mq'
73
+ @b.generate.superblock.policy_name.should == 'mq'
74
+ end
75
+
76
+ it "should reflect changes" do
77
+ n = 'most_recently_not_used_least'
78
+ @b.policy_name = n
79
+ @b.policy_name.should == n
80
+ @b.generate.superblock.policy_name.should == n
81
+ end
82
+ end
83
+
84
+ describe "mappings" do
85
+ describe "mapping policy" do
86
+ it "should default to :random" do
87
+ @b.mapping_policy.should == :random
88
+ end
89
+
90
+ it "should only accept :random, or :linear" do
91
+ [:foo, :bar, :jumpy].each do |p|
92
+ expect {@b.mapping_policy = p}.to raise_error
93
+ end
94
+ end
95
+ end
96
+
97
+ describe "nr mappings" do
98
+ it "should default to zero" do
99
+ @b.nr_mappings.should == 0
100
+ end
101
+
102
+ it "should never be bigger than the nr_cache_blocks" do
103
+ 100.times do
104
+ nr_cache = rand(1000)
105
+ @b.nr_cache_blocks = nr_cache
106
+ expect {@b.nr_mappings = nr_cache + rand(20) + 1}.to raise_error
107
+ end
108
+ end
109
+
110
+ it "should generate the given number of mappings" do
111
+ 100.times do
112
+ @b.nr_cache_blocks = rand(1000)
113
+ @b.nr_mappings = rand(@b.nr_cache_blocks)
114
+ @b.generate.should have(@b.nr_mappings).mappings
115
+ end
116
+ end
117
+ end
118
+
119
+ describe "dirty percentage" do
120
+ it "should default to zero" do
121
+ @b.dirty_percentage.should == 0
122
+ end
123
+
124
+ it "should throw if set to a negative number" do
125
+ expect do
126
+ @b.dirty_percentage = -20
127
+ end.to raise_error(RuntimeError, /-20/)
128
+ end
129
+
130
+ it "should throw if set to a > 100 number" do
131
+ expect do
132
+ @b.dirty_percentage = 101
133
+ end.to raise_error(RuntimeError, /101/)
134
+ end
135
+
136
+ it "should accept a valid value" do
137
+ [0, 1, 34, 78, 99, 100].each do |valid_value|
138
+ @b.dirty_percentage = valid_value
139
+ @b.dirty_percentage.should == valid_value
140
+ end
141
+ end
142
+
143
+ it "should generate the required percentage" do
144
+ n_mappings = 10000
145
+ @b.nr_cache_blocks = n_mappings
146
+ @b.nr_mappings = n_mappings
147
+
148
+ [0, 1, 34, 78, 99, 100].each do |p|
149
+ @b.dirty_percentage = p
150
+ metadata = @b.generate
151
+ nr_dirty = 0
152
+ metadata.mappings.each do |m|
153
+ nr_dirty += 1 if m.dirty
154
+ end
155
+
156
+ approx_percentage(nr_dirty, n_mappings, p).should be_true
157
+ end
158
+ end
159
+ end
160
+
161
+ describe "linear layout" do
162
+ before :each do
163
+ @b.mapping_policy = :linear
164
+ end
165
+
166
+ it "should allocate cache blocks in order" do
167
+ @b.nr_cache_blocks = 100
168
+ @b.nr_mappings = rand(@b.nr_cache_blocks)
169
+ mappings = @b.generate.mappings
170
+
171
+ cb = 0
172
+ mappings.each do |m|
173
+ m.cache_block.should == cb
174
+ cb += 1
175
+ end
176
+ end
177
+
178
+ it "should allocate origin blocks in order" do
179
+ @b.nr_cache_blocks = 100
180
+ @b.nr_mappings = 100
181
+ mappings = @b.generate.mappings
182
+
183
+ b = nil
184
+ mappings.each do |m|
185
+ if b.nil?
186
+ b = m.origin_block
187
+ else
188
+ m.origin_block.should == b
189
+ end
190
+
191
+ b += 1
192
+ end
193
+ end
194
+ end
195
+
196
+ describe "random mapping" do
197
+ before :each do
198
+ @b.mapping_policy = :random
199
+ end
200
+
201
+ it "should allocate cache blocks in order" do
202
+ @b.nr_cache_blocks = 100
203
+ @b.nr_mappings = rand(@b.nr_cache_blocks)
204
+ mappings = @b.generate.mappings
205
+
206
+ cb = 0
207
+ mappings.each do |m|
208
+ m.cache_block.should == cb
209
+ cb += 1
210
+ end
211
+ end
212
+
213
+ it "should allocate origin blocks out of order" do
214
+ @b.nr_cache_blocks = 100
215
+ @b.nr_mappings = 100
216
+ mappings = @b.generate.mappings
217
+
218
+ in_order = true
219
+ b = nil
220
+ mappings.each do |m|
221
+ if b.nil?
222
+ b = m.origin_block
223
+ else
224
+ if m.origin_block != b
225
+ in_order = false
226
+ end
227
+ end
228
+
229
+ b += 1
230
+ end
231
+
232
+ in_order.should be_false
233
+ end
234
+
235
+ end
236
+ end
237
+ end
238
+
239
+ #----------------------------------------------------------------
240
+
@@ -20,12 +20,52 @@ end
20
20
 
21
21
  #----------------------------------------------------------------
22
22
 
23
- describe "random distributions" do
23
+ describe "integer distributions" do
24
+ describe ConstDistribution do
25
+ it "should be constructed with a single value" do
26
+ expect do
27
+ dist = ConstDistribution.new
28
+ end.to raise_error
29
+
30
+ expect do
31
+ dist = ConstDistribution.new(1, 2)
32
+ end.to raise_error
33
+ end
34
+
35
+ it "should always return it's given value" do
36
+ dist = ConstDistribution.new(5)
37
+ 10.times do
38
+ dist.generate.should == 5
39
+ end
40
+
41
+ dist = ConstDistribution.new(11)
42
+ 10.times do
43
+ dist.generate.should == 11
44
+ end
45
+ end
46
+
47
+ it "should be passed an integer" do
48
+ expect do
49
+ dist = ConstDistribution.new('fish')
50
+ end.to raise_error
51
+ end
52
+ end
53
+
54
+ #--------------------------------
55
+
24
56
  describe UniformDistribution do
25
57
  it "should be constructed with a range of values" do
26
58
  dist = UniformDistribution.new(1, 10)
27
59
  end
28
60
 
61
+ it "should only accept start and stop if they're different" do
62
+ expect {UniformDistribution.new(45, 45)}.to raise_error
63
+ end
64
+
65
+ it "should only accept start if it's lower than stop" do
66
+ expect {UniformDistribution.new(45, 35)}.to raise_error
67
+ end
68
+
29
69
  it "should generate a number from this range every time generate is called" do
30
70
  dist = UniformDistribution.new(1, 10)
31
71
 
@@ -39,6 +79,7 @@ describe "random distributions" do
39
79
  buckets[10].should == 0
40
80
 
41
81
  1.upto(9) do |n|
82
+ buckets[n].should_not == 0
42
83
  buckets[n].should be_approx(1000, 200)
43
84
  end
44
85
  end
@@ -58,6 +99,51 @@ describe "random distributions" do
58
99
  $called456.should be_true
59
100
  end
60
101
  end
102
+
103
+ #--------------------------------
104
+
105
+ describe "parsing" do
106
+ describe "constant expression" do
107
+ it "should parse a const expression" do
108
+ 10.times do
109
+ n = rand(50)
110
+ parse_distribution(n.to_s).to_i.should == n
111
+ parse_distribution(n.to_s).to_i.should == n
112
+ end
113
+ end
114
+ end
115
+
116
+ describe "uniform expression" do
117
+ def check_range(b, e, n)
118
+ n.should >= b
119
+ n.should < e
120
+ end
121
+
122
+ it "should accept well formed expressions" do
123
+ 10.times do
124
+ b, e = [rand(100), rand(100)].sort
125
+ e += 1
126
+ d = parse_distribution("uniform[#{b}..#{e}]")
127
+
128
+ 100.times do
129
+ check_range(b, e, d.to_i)
130
+ end
131
+ end
132
+ end
133
+
134
+ it "should reject badly formed expressions" do
135
+ bad = ['fred[1..20]',
136
+ 'uniform[1..]',
137
+ 'uniform[..20]',
138
+ 'uniform 1..20',
139
+ 'uniform[1..20',
140
+ 'uniform1..20]']
141
+ bad.each do |b|
142
+ expect {parse_distribution(b)}.to raise_error
143
+ end
144
+ end
145
+ end
146
+ end
61
147
  end
62
148
 
63
149
  #----------------------------------------------------------------
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thinp_xml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Thornber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-06 00:00:00.000000000 Z
11
+ date: 2013-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ejt_command_line
@@ -114,32 +114,45 @@ email:
114
114
  - ejt@redhat.com
115
115
  executables:
116
116
  - analyse_metadata
117
+ - cache_xml
117
118
  - thinp_xml
118
119
  extensions: []
119
120
  extra_rdoc_files: []
120
121
  files:
121
122
  - .gitignore
123
+ - .rspec
122
124
  - Gemfile
123
125
  - LICENSE.txt
124
126
  - README.md
125
127
  - Rakefile
126
128
  - bin/analyse_metadata
129
+ - bin/cache_xml
127
130
  - bin/thinp_xml
128
- - features/create.feature
131
+ - features/cache_create.feature
132
+ - features/cache_usage.feature
129
133
  - features/step_definitions/thinp_xml.rb
130
134
  - features/support/env.rb
131
- - features/usage.feature
135
+ - features/thinp_create.feature
136
+ - features/thinp_usage.feature
132
137
  - lib/thinp_xml.rb
133
- - lib/thinp_xml/analysis.rb
134
- - lib/thinp_xml/builder.rb
138
+ - lib/thinp_xml/cache/builder.rb
139
+ - lib/thinp_xml/cache/emit.rb
140
+ - lib/thinp_xml/cache/metadata.rb
141
+ - lib/thinp_xml/cache/parse.rb
142
+ - lib/thinp_xml/cache_xml.rb
135
143
  - lib/thinp_xml/distribution.rb
136
- - lib/thinp_xml/emit.rb
137
- - lib/thinp_xml/metadata.rb
138
- - lib/thinp_xml/parse.rb
139
- - lib/thinp_xml/utils.rb
144
+ - lib/thinp_xml/emitter.rb
145
+ - lib/thinp_xml/listener.rb
146
+ - lib/thinp_xml/thinp/analysis.rb
147
+ - lib/thinp_xml/thinp/builder.rb
148
+ - lib/thinp_xml/thinp/emit.rb
149
+ - lib/thinp_xml/thinp/metadata.rb
150
+ - lib/thinp_xml/thinp/parse.rb
151
+ - lib/thinp_xml/thinp/utils.rb
152
+ - lib/thinp_xml/thinp/xml_format.rb
140
153
  - lib/thinp_xml/version.rb
141
- - lib/thinp_xml/xml_format.rb
142
154
  - spec/builder_spec.rb
155
+ - spec/cache_builder_spec.rb
143
156
  - spec/distribution_spec.rb
144
157
  - thinp_xml.gemspec
145
158
  homepage: ''
@@ -167,9 +180,12 @@ signing_key:
167
180
  specification_version: 4
168
181
  summary: Thin provisioning xml
169
182
  test_files:
170
- - features/create.feature
183
+ - features/cache_create.feature
184
+ - features/cache_usage.feature
171
185
  - features/step_definitions/thinp_xml.rb
172
186
  - features/support/env.rb
173
- - features/usage.feature
187
+ - features/thinp_create.feature
188
+ - features/thinp_usage.feature
174
189
  - spec/builder_spec.rb
190
+ - spec/cache_builder_spec.rb
175
191
  - spec/distribution_spec.rb
@@ -1,69 +0,0 @@
1
- require 'thinp_xml/metadata'
2
-
3
- #----------------------------------------------------------------
4
-
5
- module ThinpXML
6
- class Emitter
7
- def initialize(out)
8
- @out = out
9
- @indent = 0
10
- end
11
-
12
- def emit_tag(obj, tag, *fields, &block)
13
- expanded = fields.map {|fld| "#{fld}=\"#{obj.send(fld)}\""}
14
- if block.nil?
15
- emit_line "<#{tag} #{expanded.join(' ')}/>"
16
- else
17
- emit_line "<#{tag} #{expanded.join(' ')}>"
18
- push
19
- yield unless block.nil?
20
- pop
21
- emit_line "</#{tag}>"
22
- end
23
- end
24
-
25
- def emit_line(str)
26
- @out.puts((' ' * @indent) + str)
27
- end
28
-
29
- def push
30
- @indent += 2
31
- end
32
-
33
- def pop
34
- @indent -= 2
35
- end
36
- end
37
-
38
- def emit_superblock(e, sb, &block)
39
- e.emit_tag(sb, 'superblock', :uuid, :time, :transaction, :data_block_size, :nr_data_blocks, &block)
40
- end
41
-
42
- def emit_device(e, dev, &block)
43
- e.emit_tag(dev, 'device', :dev_id, :mapped_blocks, :transaction, :creation_time, :snap_time, &block)
44
- end
45
-
46
- def emit_mapping(e, m)
47
- if m.length == 1
48
- e.emit_line("<single_mapping origin_block=\"#{m.origin_begin}\" data_block=\"#{m.data_begin}\" time=\"#{m.time}\"/>")
49
- else
50
- e.emit_tag(m, 'range_mapping', :origin_begin, :data_begin, :length, :time)
51
- end
52
- end
53
-
54
- def write_xml(metadata, io)
55
- e = Emitter.new(io)
56
-
57
- emit_superblock(e, metadata.superblock) do
58
- metadata.devices.each do |dev|
59
- emit_device(e, dev) do
60
- dev.mappings.each do |m|
61
- emit_mapping(e, m)
62
- end
63
- end
64
- end
65
- end
66
- end
67
- end
68
-
69
- #----------------------------------------------------------------