ceph-ruby-livelink 1.5.1
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 +7 -0
- data/.cluster.example.yml +19 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.rubocop.yml +2 -0
- data/Gemfile +4 -0
- data/Guardfile +20 -0
- data/LICENSE.txt +22 -0
- data/README.md +1 -0
- data/Rakefile +7 -0
- data/ceph-ruby.gemspec +31 -0
- data/lib/ceph-ruby.rb +32 -0
- data/lib/ceph-ruby/cluster.rb +129 -0
- data/lib/ceph-ruby/cluster_helper.rb +39 -0
- data/lib/ceph-ruby/lib/rados.rb +158 -0
- data/lib/ceph-ruby/lib/rbd.rb +65 -0
- data/lib/ceph-ruby/pool.rb +117 -0
- data/lib/ceph-ruby/pool_enumerator.rb +32 -0
- data/lib/ceph-ruby/pool_helper.rb +46 -0
- data/lib/ceph-ruby/rados_aio_completion.rb +42 -0
- data/lib/ceph-ruby/rados_aio_object.rb +37 -0
- data/lib/ceph-ruby/rados_block_device.rb +118 -0
- data/lib/ceph-ruby/rados_block_device_helper.rb +39 -0
- data/lib/ceph-ruby/rados_object.rb +124 -0
- data/lib/ceph-ruby/rados_object_enumerator.rb +89 -0
- data/lib/ceph-ruby/version.rb +3 -0
- data/lib/ceph-ruby/xattr.rb +67 -0
- data/lib/ceph-ruby/xattr_enumerator.rb +62 -0
- data/spec/ceph_ruby_cluster_spec.rb +101 -0
- data/spec/ceph_ruby_pool_enumerator_spec.rb +55 -0
- data/spec/ceph_ruby_pool_spec.rb +177 -0
- data/spec/ceph_ruby_rados_object_enumerator_spec.rb +88 -0
- data/spec/ceph_ruby_rados_object_spec.rb +303 -0
- data/spec/ceph_ruby_xattr_enumerator_spec.rb +88 -0
- data/spec/ceph_ruby_xattr_spec.rb +122 -0
- data/spec/spec_helper.rb +23 -0
- data/tasks/rspec.rake +4 -0
- data/tasks/rubocop.rake +13 -0
- metadata +236 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
# see https://github.com/ceph/ceph/blob/v0.48.2argonaut/src/pybind/rbd.py
|
4
|
+
|
5
|
+
module CephRuby
|
6
|
+
module Lib
|
7
|
+
# Ruby librbd bindings
|
8
|
+
module Rbd
|
9
|
+
extend FFI::Library
|
10
|
+
|
11
|
+
ffi_lib ['rbd', 'librbd.so.1']
|
12
|
+
|
13
|
+
attach_function 'rbd_version', [:pointer, :pointer, :pointer], :void
|
14
|
+
|
15
|
+
attach_function 'rbd_create2', [:pointer, :string, :size_t, :uint64,
|
16
|
+
:pointer], :int
|
17
|
+
|
18
|
+
attach_function 'rbd_remove', [:pointer, :string], :int
|
19
|
+
|
20
|
+
attach_function 'rbd_open', [:pointer, :string, :pointer, :string], :int
|
21
|
+
|
22
|
+
attach_function 'rbd_close', [:pointer], :void
|
23
|
+
|
24
|
+
attach_function 'rbd_write', [:pointer, :off_t, :size_t, :buffer_in], :int
|
25
|
+
|
26
|
+
attach_function 'rbd_read', [:pointer, :off_t, :size_t, :buffer_out], :int
|
27
|
+
|
28
|
+
attach_function 'rbd_stat', [:pointer, :pointer, :size_t], :int
|
29
|
+
|
30
|
+
attach_function 'rbd_resize', [:pointer, :size_t], :int
|
31
|
+
|
32
|
+
attach_function 'rbd_copy', [:pointer, :pointer, :string], :int
|
33
|
+
|
34
|
+
attach_function 'rbd_copy_with_progress', [:pointer, :pointer, :string,
|
35
|
+
:pointer, :pointer], :int
|
36
|
+
|
37
|
+
# Datatype to store rbd status information
|
38
|
+
class StatStruct < FFI::Struct #:nodoc:
|
39
|
+
layout :size, :uint64,
|
40
|
+
:obj_size, :uint64,
|
41
|
+
:num_objs, :uint64,
|
42
|
+
:order, :int,
|
43
|
+
:block_name_prefix, [:char, 24],
|
44
|
+
:parent_pool, :int, # deprecated
|
45
|
+
:parent_name, [:char, 96] # deprecated
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.version
|
49
|
+
major = FFI::MemoryPointer.new(:int)
|
50
|
+
minor = FFI::MemoryPointer.new(:int)
|
51
|
+
extra = FFI::MemoryPointer.new(:int)
|
52
|
+
rbd_version(major, minor, extra)
|
53
|
+
{
|
54
|
+
major: major.get_int(0),
|
55
|
+
minor: minor.get_int(0),
|
56
|
+
extra: extra.get_int(0)
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.version_string
|
61
|
+
"#{version[:major]}.#{version[:minor]}.#{version[:extra]}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module CephRuby
|
2
|
+
# Represents a Ceph pool
|
3
|
+
# = usage
|
4
|
+
# pool = cluster.pool('name')
|
5
|
+
class Pool
|
6
|
+
extend CephRuby::PoolHelper
|
7
|
+
include CephRuby::PoolHelper
|
8
|
+
include ::Comparable
|
9
|
+
attr_accessor :cluster, :name, :handle
|
10
|
+
|
11
|
+
def initialize(cluster, name)
|
12
|
+
self.cluster = cluster
|
13
|
+
self.name = name
|
14
|
+
begin
|
15
|
+
yield(self)
|
16
|
+
ensure
|
17
|
+
close
|
18
|
+
end if block_given?
|
19
|
+
end
|
20
|
+
|
21
|
+
def exists?
|
22
|
+
log('exists?')
|
23
|
+
ret = Lib::Rados.rados_pool_lookup(cluster.handle, name)
|
24
|
+
return true if ret >= 0
|
25
|
+
return false if ret == -Errno::ENOENT::Errno
|
26
|
+
raise SystemCallError.new("lookup of '#{name}' failed", -ret) if ret < 0
|
27
|
+
end
|
28
|
+
|
29
|
+
alias exist? exists?
|
30
|
+
|
31
|
+
def id
|
32
|
+
ensure_open
|
33
|
+
Lib::Rados.rados_ioctx_get_id(handle)
|
34
|
+
end
|
35
|
+
|
36
|
+
def auid=(dst_auid)
|
37
|
+
log("auid=#{dst_auid}")
|
38
|
+
ensure_open
|
39
|
+
ret = Lib::Rados.rados_ioctx_pool_set_auid(handle, dst_auid)
|
40
|
+
raise SystemCallError.new('set of auid for'\
|
41
|
+
" '#{name}' failed", -ret) if ret < 0
|
42
|
+
end
|
43
|
+
|
44
|
+
def auid
|
45
|
+
log('auid')
|
46
|
+
ensure_open
|
47
|
+
auid_p = FFI::MemoryPointer.new(:uint64)
|
48
|
+
ret = Lib::Rados.rados_ioctx_pool_get_auid(handle, auid_p)
|
49
|
+
raise SystemCallError.new('get of auid for'\
|
50
|
+
" '#{name}' failed", -ret) if ret < 0
|
51
|
+
auid_p.get_uint64(0)
|
52
|
+
end
|
53
|
+
|
54
|
+
def open
|
55
|
+
return if open?
|
56
|
+
log('open')
|
57
|
+
handle_p = FFI::MemoryPointer.new(:pointer)
|
58
|
+
ret = Lib::Rados.rados_ioctx_create(cluster.handle, name, handle_p)
|
59
|
+
raise SystemCallError.new('creation of io context for'\
|
60
|
+
" '#{name}' failed", -ret) if ret < 0
|
61
|
+
self.handle = handle_p.get_pointer(0)
|
62
|
+
end
|
63
|
+
|
64
|
+
def close
|
65
|
+
return unless open?
|
66
|
+
log('close')
|
67
|
+
Lib::Rados.rados_ioctx_destroy(handle)
|
68
|
+
self.handle = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def rados_object(name, &block)
|
72
|
+
ensure_open
|
73
|
+
RadosObject.new(self, name, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
def rados_object_enumerator(&block)
|
77
|
+
ensure_open
|
78
|
+
RadosObjectEnumerator.new(self, &block)
|
79
|
+
end
|
80
|
+
|
81
|
+
def rados_block_device(name, &block)
|
82
|
+
ensure_open
|
83
|
+
RadosBlockDevice.new(self, name, &block)
|
84
|
+
end
|
85
|
+
|
86
|
+
def create(auid: nil, rule_id: nil)
|
87
|
+
log("create auid: #{auid}, rule: #{rule_id}")
|
88
|
+
rule_id ||= 0
|
89
|
+
return create_with_all(auid, rule_id) if auid
|
90
|
+
create_with_rule(rule_id)
|
91
|
+
close
|
92
|
+
end
|
93
|
+
|
94
|
+
def destroy
|
95
|
+
ret = Lib::Rados.rados_pool_delete(cluster.handle, name)
|
96
|
+
raise SystemCallError.new('delete pool failed',
|
97
|
+
-ret) if ret < 0
|
98
|
+
end
|
99
|
+
|
100
|
+
def stat
|
101
|
+
log('stat')
|
102
|
+
stat_s = Lib::Rados::PoolStatStruct.new
|
103
|
+
ensure_open
|
104
|
+
ret = Lib::Rados.rados_ioctx_pool_stat(handle, stat_s)
|
105
|
+
raise SystemCallError.new('stat failed',
|
106
|
+
-ret) if ret < 0
|
107
|
+
stat_s.to_hash
|
108
|
+
end
|
109
|
+
|
110
|
+
def flush_aio
|
111
|
+
ensure_open
|
112
|
+
ret = Lib::Rados.rados_aio_flush(handle)
|
113
|
+
raise SystemCallError.new('aio flush faield',
|
114
|
+
-ret) if ret < 0
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module CephRuby
|
2
|
+
# Enumerator of Ceph pools
|
3
|
+
class PoolEnumerator
|
4
|
+
include Enumerable
|
5
|
+
attr_accessor :cluster, :members
|
6
|
+
def initialize(cluster)
|
7
|
+
self.cluster = cluster
|
8
|
+
yield self if block_given?
|
9
|
+
end
|
10
|
+
|
11
|
+
def each
|
12
|
+
return enum_for(:each) unless block_given?
|
13
|
+
|
14
|
+
pools.each do |pool|
|
15
|
+
yield Pool.new(cluster, pool)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def size
|
20
|
+
pools.size
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def pools(size = 512)
|
26
|
+
data_p = FFI::MemoryPointer.new(:char, size)
|
27
|
+
ret = Lib::Rados.rados_pool_list(cluster.handle, data_p, size)
|
28
|
+
return pools(ret) if ret > size
|
29
|
+
data_p.get_bytes(0, ret).split("\0")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module CephRuby
|
2
|
+
# Pool Helper MEthods
|
3
|
+
module PoolHelper
|
4
|
+
def by_id(cluster, id, &block)
|
5
|
+
Pool.new(cluster, cluster.pool_name_by_id(id), &block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_with_all(auid, rule_id)
|
9
|
+
ret = Lib::Rados.rados_pool_create_with_all(cluster.handle, name,
|
10
|
+
auid, rule_id)
|
11
|
+
raise SystemCallError.new("create pool with auid: #{auid}, "\
|
12
|
+
"rule_id: #{rule_id} failed", -ret) if ret < 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_with_rule(rule_id)
|
16
|
+
ret = Lib::Rados.rados_pool_create_with_crush_rule(cluster.handle, name,
|
17
|
+
rule_id)
|
18
|
+
raise SystemCallError.new("create pool with rule_id: #{rule_id}"\
|
19
|
+
' failed', -ret) if ret < 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def open?
|
23
|
+
!handle.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def ensure_open
|
27
|
+
return if open?
|
28
|
+
open
|
29
|
+
end
|
30
|
+
|
31
|
+
def log(message)
|
32
|
+
CephRuby.log("pool #{name} #{message}")
|
33
|
+
end
|
34
|
+
|
35
|
+
def <=>(other)
|
36
|
+
cluster_check = other.cluster <=> cluster
|
37
|
+
return cluster_check unless cluster_check == 0
|
38
|
+
other.name <=> name
|
39
|
+
end
|
40
|
+
|
41
|
+
def eql?(other)
|
42
|
+
return false unless other.class == self.class
|
43
|
+
self == other
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module CephRuby
|
2
|
+
# A Completion for callbacks for asynchronous IO
|
3
|
+
class RadosCompletion
|
4
|
+
attr_accessor :completion_t
|
5
|
+
|
6
|
+
def initialize(args: nil, complete_callback: nil, safe_callback: nil)
|
7
|
+
self.completion_t = Lib::Rados.rados_completion(args,
|
8
|
+
complete_callback,
|
9
|
+
safe_callback)
|
10
|
+
end
|
11
|
+
|
12
|
+
def wait_for_complete
|
13
|
+
end
|
14
|
+
|
15
|
+
def wait_for_safe
|
16
|
+
end
|
17
|
+
|
18
|
+
def complete?
|
19
|
+
end
|
20
|
+
|
21
|
+
def safe?
|
22
|
+
end
|
23
|
+
|
24
|
+
def wait_for_complete_and_cb
|
25
|
+
end
|
26
|
+
|
27
|
+
def wait_for_safe_and_cb
|
28
|
+
end
|
29
|
+
|
30
|
+
def complete_and_cb?
|
31
|
+
end
|
32
|
+
|
33
|
+
def safe_and_cb?
|
34
|
+
end
|
35
|
+
|
36
|
+
def return_value
|
37
|
+
end
|
38
|
+
|
39
|
+
def destroy
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module CephRuby
|
2
|
+
# Asynchronous object operations on Rados Object
|
3
|
+
class RadosAIOObject < RadosObject
|
4
|
+
attr_accessor :completions
|
5
|
+
|
6
|
+
def initialize(pool, name)
|
7
|
+
super(pool, name)
|
8
|
+
self.completions = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def read(completion, _offset, _size)
|
12
|
+
completions << completion
|
13
|
+
end
|
14
|
+
|
15
|
+
def write(completion, _offset, data)
|
16
|
+
completions << completion
|
17
|
+
size = data.size
|
18
|
+
p size
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy(completion)
|
22
|
+
completions << completion
|
23
|
+
end
|
24
|
+
|
25
|
+
def append(completion, _data)
|
26
|
+
completions << completion
|
27
|
+
end
|
28
|
+
|
29
|
+
def stat(completion)
|
30
|
+
completions << completion
|
31
|
+
end
|
32
|
+
|
33
|
+
def cancel(completion)
|
34
|
+
completions << completion
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module CephRuby
|
2
|
+
# Rados Block Device
|
3
|
+
class RadosBlockDevice
|
4
|
+
extend CephRuby::RadosBlockDeviceHelper
|
5
|
+
attr_accessor :pool, :name, :handle
|
6
|
+
|
7
|
+
delegate :cluster, to: :pool
|
8
|
+
|
9
|
+
def initialize(pool, name)
|
10
|
+
self.pool = pool
|
11
|
+
self.name = name
|
12
|
+
if block_given?
|
13
|
+
begin
|
14
|
+
yield(self)
|
15
|
+
ensure
|
16
|
+
close
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def exists?
|
22
|
+
log('exists?')
|
23
|
+
RadosBlockDevice.close_handle(open_handle)
|
24
|
+
rescue SystemCallError => e
|
25
|
+
return false if e.errno == -Errno::ENOENT::Errno
|
26
|
+
raise
|
27
|
+
end
|
28
|
+
|
29
|
+
def create(size, features = 0, order = 26)
|
30
|
+
log("create size #{size}, features #{features}, order #{order}")
|
31
|
+
order_p = FFI::MemoryPointer.new(:int)
|
32
|
+
order_p.put_int(0, order)
|
33
|
+
ret = Lib::Rbd.rbd_create2(pool.handle, name, size, features, order_p)
|
34
|
+
raise SystemCallError.new("creation of '#{name}' failed", -ret) if ret < 0
|
35
|
+
end
|
36
|
+
|
37
|
+
def open
|
38
|
+
return if open?
|
39
|
+
log('open')
|
40
|
+
self.handle = open_handle
|
41
|
+
end
|
42
|
+
|
43
|
+
def open_handle
|
44
|
+
handle_p = FFI::MemoryPointer.new(:pointer)
|
45
|
+
ret = Lib::Rbd.rbd_open(pool.handle, name, handle_p, nil)
|
46
|
+
raise SystemCallError.new("open of '#{name}' failed", -ret) if ret < 0
|
47
|
+
handle_p.get_pointer(0)
|
48
|
+
end
|
49
|
+
|
50
|
+
def close
|
51
|
+
return unless open?
|
52
|
+
log('close')
|
53
|
+
RadosBlockDevice.close_handle(handle)
|
54
|
+
self.handle = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def destroy
|
58
|
+
close if open?
|
59
|
+
log('destroy')
|
60
|
+
ret = Lib::Rbd.rbd_remove(pool.handle, name)
|
61
|
+
raise SystemCallError.new("destroy of '#{name}' failed", -ret) if ret < 0
|
62
|
+
end
|
63
|
+
|
64
|
+
def write(offset, data)
|
65
|
+
ensure_open
|
66
|
+
size = data.bytesize
|
67
|
+
log("write offset #{offset}, size #{size}")
|
68
|
+
ret = Lib::Rbd.rbd_write(handle, offset, size, data)
|
69
|
+
raise SystemCallError.new("write of #{size} bytes to '#{name}' "\
|
70
|
+
"at #{offset} failed", -ret) if ret < 0
|
71
|
+
raise Errno::EIO, "wrote only #{ret} of #{size} bytes to "\
|
72
|
+
"'#{name}' at #{offset}" if ret < size
|
73
|
+
end
|
74
|
+
|
75
|
+
def read(offset, size)
|
76
|
+
ensure_open
|
77
|
+
log("read offset #{offset}, size #{size}")
|
78
|
+
data_p = FFI::MemoryPointer.new(:char, size)
|
79
|
+
ret = Lib::Rbd.rbd_read(handle, offset, size, data_p)
|
80
|
+
raise SystemCallError.new("read of #{size} bytes from "\
|
81
|
+
"'#{name}' at #{offset} failed",
|
82
|
+
-ret) if ret < 0
|
83
|
+
data_p.get_bytes(0, ret)
|
84
|
+
end
|
85
|
+
|
86
|
+
def stat
|
87
|
+
ensure_open
|
88
|
+
log('stat')
|
89
|
+
stat = Lib::Rbd::StatStruct.new
|
90
|
+
ret = Lib::Rbd.rbd_stat(handle, stat, stat.size)
|
91
|
+
raise SystemCallError.new("stat of '#{name}' failed", -ret) if ret < 0
|
92
|
+
RadosBlockDevice.parse_stat(stat)
|
93
|
+
end
|
94
|
+
|
95
|
+
def resize(size)
|
96
|
+
ensure_open
|
97
|
+
log("resize size #{size}")
|
98
|
+
ret = Lib::Rbd.rbd_resize(handle, size)
|
99
|
+
raise SystemCallError.new("resize of '#{name}' to #{size} failed",
|
100
|
+
-ret) if ret < 0
|
101
|
+
end
|
102
|
+
|
103
|
+
def size
|
104
|
+
stat[:size]
|
105
|
+
end
|
106
|
+
|
107
|
+
def copy_to(dst_name, dst_pool = nil)
|
108
|
+
ensure_open
|
109
|
+
dst_pool = parse_dst(dst, pool)
|
110
|
+
dst_pool_name = dst_pool.name
|
111
|
+
log("copy_to #{dst_pool_name}/#{dst_name}")
|
112
|
+
ret = Lib::Rbd.rbd_copy(handle, dst_pool.handle, dst_name)
|
113
|
+
raise SystemCallError.new("copy of '#{name}' to "\
|
114
|
+
"'#{dst_pool_name}/#{dst_name}' failed",
|
115
|
+
-ret) if ret < 0
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|