uricp 0.0.17 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ Then(/^a ceph entry named "(.*?)" should exist$/) do |arg1|
2
+ pending # express the regexp above with the code you wish you had
3
+ end
4
+
5
+ Then(/^a ceph snapshot named "(.*?)" should exist$/) do |arg1|
6
+ pending # express the regexp above with the code you wish you had
7
+ end
8
+
@@ -1,8 +1,10 @@
1
1
  Then(/^the file named "(.*?)" should have a file format of "(.*?)"$/) do |filename, format|
2
2
  case format
3
3
  when 'lz4'
4
- assert_exact_output([0x184D2204].pack('V'),
5
- File.open(filename, 'rb') {|f| f.read(4) }.to_s)
4
+ assert_exact_output(
5
+ [0x184D2204].pack('V'),
6
+ File.open(filename, 'rb') { |f| f.read(4) } || String.new
7
+ )
6
8
  when 'qcow2v3', 'qcow3'
7
9
  steps %{
8
10
  When I successfully run `qemu-img info #{filename}`
@@ -10,8 +12,10 @@ Then(/^the file named "(.*?)" should have a file format of "(.*?)"$/) do |filena
10
12
  And the output from "qemu-img info #{filename}" should contain "compat: 1.1"
11
13
  }
12
14
  else
13
- assert_no_partial_output([0x184D2204].pack('V'),
14
- File.open(filename, 'rb') {|f| f.read(4) }.to_s)
15
+ assert_no_partial_output(
16
+ [0x184D2204].pack('V'),
17
+ File.open(filename, 'rb') { |f| f.read(4) } || String.new
18
+ )
15
19
  steps(%{
16
20
  When I successfully run `qemu-img info #{filename}`
17
21
  Then the output from "qemu-img info #{filename}" should contain "file format: #{format}"
data/focal/Dockerfile ADDED
@@ -0,0 +1,28 @@
1
+ FROM ubuntu:focal
2
+ MAINTAINER support@brightbox.co.uk
3
+
4
+ RUN echo "gem: --no-ri --no-rdoc" >> "$HOME/.gemrc"
5
+
6
+ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq software-properties-common
7
+
8
+ #RUN apt-add-repository ppa:brightbox/ruby-ng
9
+
10
+ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
11
+ build-essential \
12
+ git \
13
+ qemu-utils \
14
+ liblz4-tool \
15
+ curl \
16
+ ruby \
17
+ ruby-dev \
18
+ python3-swiftclient \
19
+ ruby-bundler \
20
+ ceph-common
21
+
22
+ RUN gem install bundler -v '~> 1.7'
23
+ RUN mkdir -p /etc/ceph
24
+ ARG RBD_USR=libvirt
25
+ ARG RBD_PSW
26
+ ARG CEPH_MON
27
+ RUN /bin/echo -e "[client.$RBD_USR]\nkey = $RBD_PSW\n" > /etc/ceph/ceph.client.$RBD_USR.keyring
28
+ RUN /bin/echo -e "[global]\nmon_host = $CEPH_MON\n" > /etc/ceph/ceph.conf
@@ -15,8 +15,26 @@ module Uricp::Strategy
15
15
  'no cache name found'
16
16
  end
17
17
 
18
+ def with_active_cache
19
+ if cache_root
20
+ yield
21
+ else
22
+ debug "#{self.class.name}: no cacheing requested"
23
+ false
24
+ end
25
+ end
26
+
27
+ def without_active_cache
28
+ unless cache_root
29
+ yield
30
+ else
31
+ debug "#{self.class.name}: cache active - not appropriate"
32
+ false
33
+ end
34
+ end
35
+
18
36
  def in_cache?
19
- File.readable? cache_file
37
+ File.readable?(cache_file) || options['dry-cache']
20
38
  end
21
39
 
22
40
  def cache_root
@@ -6,15 +6,13 @@ module Uricp::Strategy
6
6
  include Uricp::Strategy::CacheCommon
7
7
 
8
8
  def appropriate?
9
- if cache_root
9
+ with_active_cache do
10
10
  validate_cache!
11
11
  return proposal if in_cache? || file_source?
12
12
 
13
13
  debug "#{self.class.name}: no cache entry for #{options['from_uri']}"
14
- else
15
- debug "#{self.class.name}: not appropriate"
14
+ false
16
15
  end
17
- false
18
16
  end
19
17
 
20
18
  def command
@@ -24,10 +22,15 @@ module Uricp::Strategy
24
22
  def proposal
25
23
  @proposed_options = options.dup
26
24
  @proposed_options['from_uri'] = URI.join('file:///', cache_file) unless file_source?
25
+ if to.scheme == 'rbd'
26
+ @proposed_options['rbd_cache_name'] = rbd_cache_image_spec(to)
27
+ end
27
28
  @proposed_options.delete('cache')
28
29
  @proposed_options.delete('cache_name')
29
30
  if conversion_required?
30
- unless dry_run?
31
+ if dry_run?
32
+ @proposed_options['source-format'] = @proposed_options['target-format']
33
+ else
31
34
  @proposed_options['source-format'] =
32
35
  File.open(@proposed_options['from_uri'].path) { |f| encoding(f) }
33
36
  end
@@ -91,6 +91,15 @@ module Uricp::Strategy
91
91
  end
92
92
 
93
93
  PIPE_URI = URI('pipe:/')
94
+ DRY_SNAP = 'uricp_snap'.freeze
95
+
96
+ def rbd_base_name
97
+ 'base'.freeze
98
+ end
99
+
100
+ def rbd_snapshot_name
101
+ @rbd_snapshot_name ||= dry_run? ? DRY_SNAP : SecureRandom.uuid
102
+ end
94
103
 
95
104
  def get_temp_filename(base_dir)
96
105
  t = Time.now.strftime('%Y%m%d')
@@ -109,12 +118,56 @@ module Uricp::Strategy
109
118
  options['source-format'] && !lz4_source?
110
119
  end
111
120
 
112
- def rbd_target(uri)
121
+ def rbd_image_spec(uri)
113
122
  uri.path[1..-1]
114
123
  end
115
124
 
125
+ def rbd_cache_image_spec(uri)
126
+ File.join(File.dirname(uri.path)[1..-1], options['cache_name'])
127
+ end
128
+
129
+ def rbd_cache_name
130
+ options['rbd_cache_name']
131
+ end
132
+
133
+ def rbd_clone_snapshot(cache=rbd_cache_name)
134
+ "#{cache}@#{rbd_base_name}"
135
+ end
136
+
137
+ def rbd_uri(image_spec)
138
+ URI.join('rbd:///', image_spec)
139
+ end
140
+
141
+ def rbd_sequence_complete?
142
+ from.path.include?(to.path)
143
+ end
144
+
116
145
  def rbd_id
117
146
  'libvirt'
118
147
  end
148
+
149
+ def in_rbd_cache?
150
+ options['in_rbd_cache']
151
+ end
152
+
153
+ def not_in_rbd_cache?
154
+ options['in_rbd_cache'] == false
155
+ end
156
+
157
+ def rbd_snapshot_spec?(uri)
158
+ uri.scheme == 'rbd' && uri.path.include?('@')
159
+ end
160
+
161
+ def in_rbd_cache(target)
162
+ result = false
163
+ if dry_run?
164
+ result = options['dry-cache'] && options['cache_name'] !~ /srv-...../
165
+ else
166
+ sh "rbd snap ls --id #{rbd_id} --format json #{target} 2>/dev/null" do |stdout|
167
+ result = JSON.parse(stdout).any? { |x| x['name'] == rbd_base_name }
168
+ end
169
+ end
170
+ result && rbd_clone_snapshot(target)
171
+ end
119
172
  end
120
173
  end
@@ -32,7 +32,7 @@ module Uricp::Strategy
32
32
  end
33
33
 
34
34
  def new_qemu?
35
- @new_qemu ||= !!(`qemu-img convert -O qcow2 -o ? a b` =~ /^compat/m)
35
+ @new_qemu ||= !!(`qemu-img convert -O qcow2 -o ? a b` =~ /\bcompat\b/m)
36
36
  end
37
37
 
38
38
  def supported_conversion?
@@ -6,17 +6,15 @@ module Uricp::Strategy
6
6
  include Uricp::Strategy::CacheCommon
7
7
 
8
8
  def appropriate?
9
- if cache_root
9
+ with_active_cache do
10
10
  case from.scheme
11
11
  when 'pipe'
12
12
  validate_cache!
13
13
  return proposal
14
14
  end
15
15
  debug "#{self.class.name}: not appropriate"
16
- else
17
- debug "#{self.class.name}: no cacheing requested"
16
+ false
18
17
  end
19
- false
20
18
  end
21
19
 
22
20
  def command
@@ -26,6 +24,9 @@ module Uricp::Strategy
26
24
  def proposal
27
25
  @proposed_options = options.dup
28
26
  @proposed_options['sweep'] = [temp_cache_file, cache_file]
27
+ if to.scheme == 'rbd'
28
+ @proposed_options['rbd_cache_name'] = rbd_cache_image_spec(to)
29
+ end
29
30
  @proposed_options.delete('cache')
30
31
  @proposed_options.delete('cache_name')
31
32
  self
@@ -2,45 +2,29 @@ require 'json'
2
2
  module Uricp::Strategy
3
3
  class PipedRbdGet
4
4
  include Uricp::Strategy::Common
5
+ include Uricp::Strategy::CacheCommon
5
6
  include Methadone::SH
6
7
 
7
8
  def appropriate?
8
- case from.scheme
9
- when 'rbd'
10
- return proposal unless sequence_complete? || snap_in_progress?
9
+ without_active_cache do
10
+ if from.scheme == 'rbd' &&
11
+ rbd_snapshot_spec?(from) &&
12
+ to.scheme != 'rbd'
13
+ return proposal unless sequence_complete?
14
+ end
11
15
  end
12
16
  debug "#{self.class.name}: not appropriate"
13
17
  false
14
18
  end
15
19
 
16
20
  def command
17
- "rbd snap create --id #{rbd_id} '#{rbd_snap_target(from)}' && " \
18
- "rbd export --no-progress --id #{rbd_id} '#{rbd_snap_target(from)}' - |"
21
+ "rbd export --no-progress --id #{rbd_id} '#{rbd_image_spec(from)}' - |"
19
22
  end
20
23
 
21
24
  def proposal
22
25
  @proposed_options = options.dup
23
- @proposed_options['rbd_snapshot'] = rbd_snap_target(from)
24
26
  @proposed_options['from_uri'] = PIPE_URI
25
- @proposed_options.delete('cache')
26
- @proposed_options.delete('cache_name')
27
27
  self
28
28
  end
29
-
30
- RBD_SNAPSHOT = 'uricp_snap'.freeze
31
-
32
- def rbd_snap_target(uri)
33
- "#{rbd_target(uri)}@#{RBD_SNAPSHOT}"
34
- end
35
-
36
- def snap_in_progress?
37
- return false if dry_run?
38
-
39
- result = false
40
- sh "rbd snap ls --id #{rbd_id} --format json #{rbd_target(from)}" do |stdout|
41
- result = JSON.parse(stdout).any? { |x| x['name'] == RBD_SNAPSHOT }
42
- end
43
- result
44
- end
45
29
  end
46
30
  end
@@ -0,0 +1,27 @@
1
+ module Uricp::Strategy
2
+ class RbdCacheBaseSnap
3
+ include Uricp::Strategy::Common
4
+ include Methadone::SH
5
+
6
+ def appropriate?
7
+ if rbd_cache_name &&
8
+ rbd_image_spec(from) == rbd_cache_name
9
+ return proposal
10
+ end
11
+ debug "#{self.class.name}: not appropriate"
12
+ false
13
+ end
14
+
15
+ def command
16
+ "rbd snap create --id #{rbd_id} '#{rbd_clone_snapshot(rbd_cache_name)}';"\
17
+ "rbd snap protect --id #{rbd_id} '#{rbd_clone_snapshot(rbd_cache_name)}';"
18
+ end
19
+
20
+ def proposal
21
+ @proposed_options = options.dup
22
+ @proposed_options['from_uri'] = rbd_uri(rbd_clone_snapshot(rbd_cache_name))
23
+ @proposed_options.delete('rbd_cache_name')
24
+ self
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,42 @@
1
+ require 'uri'
2
+
3
+ module Uricp::Strategy
4
+ class RbdCacheCheck
5
+ include Uricp::Strategy::Common
6
+ include Uricp::Strategy::CacheCommon
7
+ include Methadone::SH
8
+
9
+ def appropriate?
10
+ unless (from.scheme == 'rbd') != (to.scheme == 'rbd')
11
+ debug "#{self.class.name}: not appropriate"
12
+ return false
13
+ end
14
+
15
+ with_active_cache do
16
+ if from.scheme == 'rbd'
17
+ cache_check = in_rbd_cache(rbd_cache_image_spec(from))
18
+ return proposal(cache_check) if cache_check
19
+ debug "#{self.class.name}: no rbd cache entry for #{options['from_uri']}"
20
+ elsif to.scheme == 'rbd'
21
+ cache_check = in_rbd_cache(rbd_cache_image_spec(to))
22
+ return proposal(cache_check) if cache_check
23
+ debug "#{self.class.name}: no rbd cache entry for #{options['to_uri']}"
24
+ end
25
+ false
26
+ end
27
+ end
28
+
29
+ def command
30
+ ':;'
31
+ end
32
+
33
+ def proposal(cache_check)
34
+ @proposed_options = options.dup
35
+ @proposed_options['from_uri'] = rbd_uri(cache_check)
36
+ @proposed_options.delete('cache')
37
+ @proposed_options.delete('cache_name')
38
+ @proposed_options.delete('dry-cache')
39
+ self
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,40 @@
1
+ require 'uri'
2
+
3
+ module Uricp::Strategy
4
+ class RbdCacheClone
5
+ include Uricp::Strategy::Common
6
+ include Uricp::Strategy::CacheCommon
7
+ include Methadone::SH
8
+
9
+ def appropriate?
10
+ unless from.scheme == 'rbd'
11
+ debug "#{self.class.name}: not appropriate"
12
+ return false
13
+ end
14
+
15
+ with_active_cache do
16
+ options['cache_name'] = File.basename(options['to_uri'].path)
17
+ cache_target = rbd_cache_image_spec(from)
18
+ cache_check = in_rbd_cache(rbd_cache_image_spec(from))
19
+ if cache_check
20
+ debug "#{self.class.name}: Unexpected existing cache entry for #{options['to_uri']}"
21
+ unsupported_transfer
22
+ end
23
+ proposal(cache_target)
24
+ end
25
+ end
26
+
27
+ def command
28
+ ':;'
29
+ end
30
+
31
+ def proposal(cache_check)
32
+ @proposed_options = options.dup
33
+ @proposed_options['rbd_cache_target'] = rbd_uri(cache_check)
34
+ @proposed_options.delete('cache')
35
+ @proposed_options.delete('cache_name')
36
+ @proposed_options.delete('dry-cache')
37
+ self
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ module Uricp::Strategy
2
+ class RbdCacheUpload
3
+ include Uricp::Strategy::Common
4
+ include Methadone::SH
5
+
6
+ def appropriate?
7
+ if compression_required? || conversion_required?
8
+ debug "#{self.class.name}: not ready to upload"
9
+ return false
10
+ end
11
+ if rbd_cache_name
12
+ case from.scheme
13
+ when 'pipe', 'file'
14
+ return proposal if to.scheme == 'rbd'
15
+ end
16
+ end
17
+ debug "#{self.class.name}: not appropriate"
18
+ false
19
+ end
20
+
21
+ def command
22
+ "rbd import --no-progress --id #{rbd_id} #{data_source} '#{rbd_cache_name}';"
23
+ end
24
+
25
+ def proposal
26
+ @proposed_options = options.dup
27
+ @proposed_options['from_uri'] = rbd_uri(rbd_cache_name)
28
+ self
29
+ end
30
+
31
+ def data_source
32
+ if from.scheme == 'pipe'
33
+ '-'
34
+ else
35
+ "'#{from.path}'"
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,36 @@
1
+ require 'uri'
2
+
3
+ module Uricp::Strategy
4
+ class RbdCachedGet
5
+ include Uricp::Strategy::Common
6
+ include Uricp::Strategy::CacheCommon
7
+ include Methadone::SH
8
+
9
+ def appropriate?
10
+ if from.scheme != 'rbd' || rbd_snapshot_spec?(from)
11
+ debug "#{self.class.name}: not appropriate"
12
+ return false
13
+ end
14
+
15
+ with_active_cache do
16
+ cache_check = in_rbd_cache(rbd_image_spec(from))
17
+ return proposal(cache_check) if cache_check
18
+ debug "#{self.class.name}: no rbd cache entry for #{options['from_uri']}"
19
+ false
20
+ end
21
+ end
22
+
23
+ def command
24
+ ':;'
25
+ end
26
+
27
+ def proposal(cache_check)
28
+ @proposed_options = options.dup
29
+ @proposed_options['from_uri'] = rbd_uri(cache_check)
30
+ @proposed_options.delete('cache')
31
+ @proposed_options.delete('cache_name')
32
+ @proposed_options.delete('dry-cache')
33
+ self
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,32 @@
1
+ module Uricp::Strategy
2
+ class RbdCachedPut
3
+ include Uricp::Strategy::Common
4
+ include Uricp::Strategy::CacheCommon
5
+
6
+ def appropriate?
7
+ return proposal if to.scheme == 'rbd' &&
8
+ rbd_snapshot_spec?(from) &&
9
+ rbd_cache_name.nil? &&
10
+ !rbd_sequence_complete?
11
+
12
+ debug "#{self.class.name}: not appropriate"
13
+ false
14
+ end
15
+
16
+ def command
17
+ "rbd clone --id #{rbd_id} '#{rbd_image_spec(from)}' '#{rbd_image_spec(to)}';"
18
+ end
19
+
20
+ def proposal
21
+ @proposed_options = options.dup
22
+ @proposed_options['from_uri'] = to
23
+ if options['rbd_cache_target']
24
+ @proposed_options['to_uri'] = options['rbd_cache_target']
25
+ @proposed_options['rbd_cache_name'] = rbd_image_spec(to)
26
+ @proposed_options.delete('rbd_cache_target')
27
+ end
28
+ self
29
+ end
30
+
31
+ end
32
+ end
@@ -1,5 +1,5 @@
1
1
  module Uricp::Strategy
2
- class RbdRemotePut
2
+ class RbdPut
3
3
  include Uricp::Strategy::Common
4
4
 
5
5
  def appropriate?
@@ -7,16 +7,18 @@ module Uricp::Strategy
7
7
  debug "#{self.class.name}: not ready to upload"
8
8
  return false
9
9
  end
10
- case from.scheme
11
- when 'pipe', 'file'
12
- return proposal if to.scheme == 'rbd'
10
+ unless rbd_cache_name
11
+ case from.scheme
12
+ when 'pipe', 'file'
13
+ return proposal if to.scheme == 'rbd'
14
+ end
13
15
  end
14
16
  debug "#{self.class.name}: not appropriate"
15
17
  false
16
18
  end
17
19
 
18
20
  def command
19
- "rbd import --no-progress --id #{rbd_id} #{data_source} '#{rbd_target(to)}';"
21
+ "rbd import --no-progress --id #{rbd_id} #{data_source} '#{rbd_image_spec(to)}';"
20
22
  end
21
23
 
22
24
  def proposal
@@ -0,0 +1,57 @@
1
+ require 'json'
2
+ module Uricp::Strategy
3
+ class RbdSnap
4
+ include Uricp::Strategy::Common
5
+ include Uricp::Strategy::CacheCommon
6
+ include Methadone::SH
7
+
8
+ def appropriate?
9
+ without_active_cache do
10
+ if from.scheme == 'rbd' &&
11
+ options['rbd_snapshot'].nil? &&
12
+ !rbd_snapshot_spec?(from) &&
13
+ (rbd_cache_name.nil? ||
14
+ !from.path.include?(rbd_cache_name))
15
+ if snap_in_progress?
16
+ debug "#{self.class.name}: detected snapshot in progress"
17
+ else
18
+ return proposal unless sequence_complete?
19
+ end
20
+ end
21
+ end
22
+ debug "#{self.class.name}: not appropriate"
23
+ false
24
+ end
25
+
26
+ def command
27
+ "rbd snap create --id #{rbd_id} '#{rbd_upload_snapshot(from)}';" \
28
+ "rbd snap protect --id #{rbd_id} '#{rbd_upload_snapshot(from)}';" \
29
+ end
30
+
31
+ def proposal
32
+ @proposed_options = options.dup
33
+ @proposed_options['from_uri'] = rbd_uri(rbd_upload_snapshot(from))
34
+ if options['rbd_cache_target']
35
+ @proposed_options['to_uri'] = options['rbd_cache_target']
36
+ @proposed_options['rbd_cache_target'] = to
37
+ else
38
+ @proposed_options['rbd_snapshot'] = rbd_upload_snapshot(from) unless to.scheme == 'rbd'
39
+ end
40
+ self
41
+ end
42
+
43
+ def rbd_upload_snapshot(uri)
44
+ "#{rbd_image_spec(uri)}@#{rbd_snapshot_name}"
45
+ end
46
+
47
+ def snap_in_progress?
48
+ return false if dry_run?
49
+
50
+ result = false
51
+ sh "rbd snap ls --id #{rbd_id} --format json #{rbd_image_spec(from)} 2>/dev/null" do |stdout|
52
+ result = JSON.parse(stdout).any? { |x| x['name'] == rbd_snapshot_name }
53
+ end
54
+ result
55
+ end
56
+ end
57
+ end
@@ -3,13 +3,14 @@ module Uricp::Strategy
3
3
  include Uricp::Strategy::Common
4
4
 
5
5
  def appropriate?
6
- return proposal if options['rbd_snapshot'] && sequence_complete?
6
+ return proposal if options['rbd_snapshot'] && rbd_sequence_complete?
7
7
 
8
8
  debug "#{self.class.name}: not appropriate"
9
9
  false
10
10
  end
11
11
 
12
12
  def command
13
+ "rbd snap unprotect --id #{rbd_id} '#{options['rbd_snapshot']}';" \
13
14
  "rbd snap rm --id #{rbd_id} '#{options['rbd_snapshot']}';"
14
15
  end
15
16
 
@@ -25,6 +25,10 @@ module Uricp
25
25
 
26
26
  # This is an ordered list from the most specific to the most general
27
27
  STRATEGIES = [
28
+ Strategy::RbdCachedGet,
29
+ Strategy::RbdCacheClone,
30
+ Strategy::RbdCacheCheck,
31
+ Strategy::RbdSnap,
28
32
  Strategy::CachedGet,
29
33
  Strategy::PipedRemoteGet,
30
34
  Strategy::PipedRbdGet,
@@ -35,7 +39,10 @@ module Uricp
35
39
  Strategy::LocalConvert,
36
40
  Strategy::LocalLink,
37
41
  Strategy::PipedCompress,
38
- Strategy::RbdRemotePut,
42
+ Strategy::RbdCacheUpload,
43
+ Strategy::RbdCachedPut,
44
+ Strategy::RbdPut,
45
+ Strategy::RbdCacheBaseSnap,
39
46
  Strategy::SegmentedRemotePut,
40
47
  Strategy::RemotePut,
41
48
  Strategy::PipedLocalCompress,
data/lib/uricp/version.rb CHANGED
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Uricp
2
- VERSION = '0.0.17'.freeze
3
- DEFAULT_SEGMENT_SIZE = '5 GiB'.freeze
4
+ VERSION = '0.0.22'
5
+ DEFAULT_SEGMENT_SIZE = '5 GiB'
4
6
  end