thinp_xml 0.0.7 → 0.0.8

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.
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
- #----------------------------------------------------------------