1 from __future__
import print_function
2 from nose
import SkipTest
3 from nose
.tools
import eq_
as eq
, ok_
as ok
, assert_raises
4 from rados
import (Rados
, Error
, RadosStateError
, Object
, ObjectExists
,
5 ObjectNotFound
, ObjectBusy
, requires
, opt
,
6 ANONYMOUS_AUID
, ADMIN_AUID
, LIBRADOS_ALL_NSPACES
, WriteOpCtx
, ReadOpCtx
,
7 LIBRADOS_SNAP_HEAD
, LIBRADOS_OPERATION_BALANCE_READS
, LIBRADOS_OPERATION_SKIPRWLOCKS
, MonitorLog
)
14 # Are we running Python 2.x
15 _python2
= sys
.version_info
[0] < 3
17 def test_rados_init_error():
18 assert_raises(Error
, Rados
, conffile
='', rados_id
='admin',
20 assert_raises(Error
, Rados
, conffile
='', name
='invalid')
21 assert_raises(Error
, Rados
, conffile
='', name
='bad.invalid')
23 def test_rados_init():
24 with
Rados(conffile
='', rados_id
='admin'):
26 with
Rados(conffile
='', name
='client.admin'):
28 with
Rados(conffile
='', name
='client.admin'):
30 with
Rados(conffile
='', name
='client.admin'):
33 def test_ioctx_context_manager():
34 with
Rados(conffile
='', rados_id
='admin') as conn
:
35 with conn
.open_ioctx('rbd') as ioctx
:
38 def test_parse_argv():
39 args
= ['osd', 'pool', 'delete', 'foobar', 'foobar', '--yes-i-really-really-mean-it']
41 eq(args
, r
.conf_parse_argv(args
))
43 def test_parse_argv_empty_str():
46 eq(args
, r
.conf_parse_argv(args
))
48 class TestRequires(object):
49 @requires(('foo', str), ('bar', int), ('baz', int))
50 def _method_plain(self
, foo
, bar
, baz
):
51 ok(isinstance(foo
, str))
52 ok(isinstance(bar
, int))
53 ok(isinstance(baz
, int))
54 return (foo
, bar
, baz
)
56 def test_method_plain(self
):
57 assert_raises(TypeError, self
._method
_plain
, 42, 42, 42)
58 assert_raises(TypeError, self
._method
_plain
, '42', '42', '42')
59 assert_raises(TypeError, self
._method
_plain
, foo
='42', bar
='42', baz
='42')
60 eq(self
._method
_plain
('42', 42, 42), ('42', 42, 42))
61 eq(self
._method
_plain
(foo
='42', bar
=42, baz
=42), ('42', 42, 42))
63 @requires(('opt_foo', opt(str)), ('opt_bar', opt(int)), ('baz', int))
64 def _method_with_opt_arg(self
, foo
, bar
, baz
):
65 ok(isinstance(foo
, str) or foo
is None)
66 ok(isinstance(bar
, int) or bar
is None)
67 ok(isinstance(baz
, int))
68 return (foo
, bar
, baz
)
70 def test_method_with_opt_args(self
):
71 assert_raises(TypeError, self
._method
_with
_opt
_arg
, 42, 42, 42)
72 assert_raises(TypeError, self
._method
_with
_opt
_arg
, '42', '42', 42)
73 assert_raises(TypeError, self
._method
_with
_opt
_arg
, None, None, None)
74 eq(self
._method
_with
_opt
_arg
(None, 42, 42), (None, 42, 42))
75 eq(self
._method
_with
_opt
_arg
('42', None, 42), ('42', None, 42))
76 eq(self
._method
_with
_opt
_arg
(None, None, 42), (None, None, 42))
79 class TestRadosStateError(object):
80 def _requires_configuring(self
, rados
):
81 assert_raises(RadosStateError
, rados
.connect
)
83 def _requires_configuring_or_connected(self
, rados
):
84 assert_raises(RadosStateError
, rados
.conf_read_file
)
85 assert_raises(RadosStateError
, rados
.conf_parse_argv
, None)
86 assert_raises(RadosStateError
, rados
.conf_parse_env
)
87 assert_raises(RadosStateError
, rados
.conf_get
, 'opt')
88 assert_raises(RadosStateError
, rados
.conf_set
, 'opt', 'val')
89 assert_raises(RadosStateError
, rados
.ping_monitor
, 0)
91 def _requires_connected(self
, rados
):
92 assert_raises(RadosStateError
, rados
.pool_exists
, 'foo')
93 assert_raises(RadosStateError
, rados
.pool_lookup
, 'foo')
94 assert_raises(RadosStateError
, rados
.pool_reverse_lookup
, 0)
95 assert_raises(RadosStateError
, rados
.create_pool
, 'foo')
96 assert_raises(RadosStateError
, rados
.get_pool_base_tier
, 0)
97 assert_raises(RadosStateError
, rados
.delete_pool
, 'foo')
98 assert_raises(RadosStateError
, rados
.list_pools
)
99 assert_raises(RadosStateError
, rados
.get_fsid
)
100 assert_raises(RadosStateError
, rados
.open_ioctx
, 'foo')
101 assert_raises(RadosStateError
, rados
.mon_command
, '', b
'')
102 assert_raises(RadosStateError
, rados
.osd_command
, 0, '', b
'')
103 assert_raises(RadosStateError
, rados
.pg_command
, '', '', b
'')
104 assert_raises(RadosStateError
, rados
.wait_for_latest_osdmap
)
105 assert_raises(RadosStateError
, rados
.blacklist_add
, '127.0.0.1/123', 0)
107 def test_configuring(self
):
108 rados
= Rados(conffile
='')
109 eq('configuring', rados
.state
)
110 self
._requires
_connected
(rados
)
112 def test_connected(self
):
113 rados
= Rados(conffile
='')
115 eq('connected', rados
.state
)
116 self
._requires
_configuring
(rados
)
118 def test_shutdown(self
):
119 rados
= Rados(conffile
='')
122 eq('shutdown', rados
.state
)
123 self
._requires
_configuring
(rados
)
124 self
._requires
_configuring
_or
_connected
(rados
)
125 self
._requires
_connected
(rados
)
128 class TestRados(object):
131 self
.rados
= Rados(conffile
='')
132 self
.rados
.conf_parse_env('FOO_DOES_NOT_EXIST_BLAHBLAH')
133 self
.rados
.conf_parse_env()
136 # Assume any pre-existing pools are the cluster's defaults
137 self
.default_pools
= self
.rados
.list_pools()
140 self
.rados
.shutdown()
142 def test_ping_monitor(self
):
143 assert_raises(ObjectNotFound
, self
.rados
.ping_monitor
, 'not_exists_monitor')
144 cmd
= {'prefix': 'mon dump', 'format':'json'}
145 ret
, buf
, out
= self
.rados
.mon_command(json
.dumps(cmd
), b
'')
146 for mon
in json
.loads(buf
.decode('utf8'))['mons']:
148 output
= self
.rados
.ping_monitor(mon
['name'])
151 buf
= json
.loads(output
)
152 if buf
.get('health'):
155 def test_create(self
):
156 self
.rados
.create_pool('foo')
157 self
.rados
.delete_pool('foo')
159 def test_create_utf8(self
):
161 # Use encoded bytestring
162 poolname
= b
"\351\273\204"
165 self
.rados
.create_pool(poolname
)
166 assert self
.rados
.pool_exists(u
"\u9ec4")
167 self
.rados
.delete_pool(poolname
)
169 def test_pool_lookup_utf8(self
):
174 self
.rados
.create_pool(poolname
)
176 poolid
= self
.rados
.pool_lookup(poolname
)
177 eq(poolname
, self
.rados
.pool_reverse_lookup(poolid
))
179 self
.rados
.delete_pool(poolname
)
181 def test_create_auid(self
):
182 self
.rados
.create_pool('foo', 100)
183 assert self
.rados
.pool_exists('foo')
184 self
.rados
.delete_pool('foo')
186 def test_eexist(self
):
187 self
.rados
.create_pool('foo')
188 assert_raises(ObjectExists
, self
.rados
.create_pool
, 'foo')
189 self
.rados
.delete_pool('foo')
191 def list_non_default_pools(self
):
192 pools
= self
.rados
.list_pools()
193 for p
in self
.default_pools
:
197 def test_list_pools(self
):
198 eq(set(), self
.list_non_default_pools())
199 self
.rados
.create_pool('foo')
200 eq(set(['foo']), self
.list_non_default_pools())
201 self
.rados
.create_pool('bar')
202 eq(set(['foo', 'bar']), self
.list_non_default_pools())
203 self
.rados
.create_pool('baz')
204 eq(set(['foo', 'bar', 'baz']), self
.list_non_default_pools())
205 self
.rados
.delete_pool('foo')
206 eq(set(['bar', 'baz']), self
.list_non_default_pools())
207 self
.rados
.delete_pool('baz')
208 eq(set(['bar']), self
.list_non_default_pools())
209 self
.rados
.delete_pool('bar')
210 eq(set(), self
.list_non_default_pools())
211 self
.rados
.create_pool('a' * 500)
212 eq(set(['a' * 500]), self
.list_non_default_pools())
213 self
.rados
.delete_pool('a' * 500)
215 def test_get_pool_base_tier(self
):
216 self
.rados
.create_pool('foo')
218 self
.rados
.create_pool('foo-cache')
220 pool_id
= self
.rados
.pool_lookup('foo')
221 tier_pool_id
= self
.rados
.pool_lookup('foo-cache')
223 cmd
= {"prefix":"osd tier add", "pool":"foo", "tierpool":"foo-cache", "force_nonempty":""}
224 ret
, buf
, errs
= self
.rados
.mon_command(json
.dumps(cmd
), b
'', timeout
=30)
228 cmd
= {"prefix":"osd tier cache-mode", "pool":"foo-cache", "tierpool":"foo-cache", "mode":"readonly", "sure":"--yes-i-really-mean-it"}
229 ret
, buf
, errs
= self
.rados
.mon_command(json
.dumps(cmd
), b
'', timeout
=30)
232 eq(self
.rados
.wait_for_latest_osdmap(), 0)
234 eq(pool_id
, self
.rados
.get_pool_base_tier(pool_id
))
235 eq(pool_id
, self
.rados
.get_pool_base_tier(tier_pool_id
))
237 cmd
= {"prefix":"osd tier remove", "pool":"foo", "tierpool":"foo-cache"}
238 ret
, buf
, errs
= self
.rados
.mon_command(json
.dumps(cmd
), b
'', timeout
=30)
241 self
.rados
.delete_pool('foo-cache')
243 self
.rados
.delete_pool('foo')
245 def test_get_fsid(self
):
246 fsid
= self
.rados
.get_fsid()
249 def test_blacklist_add(self
):
250 self
.rados
.blacklist_add("1.2.3.4/123", 1)
252 def test_get_cluster_stats(self
):
253 stats
= self
.rados
.get_cluster_stats()
254 assert stats
['kb'] > 0
255 assert stats
['kb_avail'] > 0
256 assert stats
['kb_used'] > 0
257 assert stats
['num_objects'] >= 0
259 def test_monitor_log(self
):
260 lock
= threading
.Condition()
261 def cb(arg
, line
, who
, sec
, nsec
, seq
, level
, msg
):
262 # NOTE(sileht): the old pyrados API was received the pointer as int
263 # instead of the value of arg
269 # NOTE(sileht): force don't save the monitor into local var
270 # to ensure all references are correctly tracked into the lib
271 MonitorLog(self
.rados
, "debug", cb
, "arg")
274 MonitorLog(self
.rados
, "debug", None, None)
275 eq(None, self
.rados
.monitor_callback
)
277 class TestIoctx(object):
280 self
.rados
= Rados(conffile
='')
282 self
.rados
.create_pool('test_pool')
283 assert self
.rados
.pool_exists('test_pool')
284 self
.ioctx
= self
.rados
.open_ioctx('test_pool')
287 cmd
= {"prefix":"osd unset", "key":"noup"}
288 self
.rados
.mon_command(json
.dumps(cmd
), b
'')
290 self
.rados
.delete_pool('test_pool')
291 self
.rados
.shutdown()
293 def test_get_last_version(self
):
294 version
= self
.ioctx
.get_last_version()
297 def test_get_stats(self
):
298 stats
= self
.ioctx
.get_stats()
299 eq(stats
, {'num_objects_unfound': 0,
300 'num_objects_missing_on_primary': 0,
301 'num_object_clones': 0,
303 'num_object_copies': 0,
309 'num_objects_degraded': 0,
312 def test_change_auid(self
):
313 self
.ioctx
.change_auid(ANONYMOUS_AUID
)
314 self
.ioctx
.change_auid(ADMIN_AUID
)
316 def test_write(self
):
317 self
.ioctx
.write('abc', b
'abc')
318 eq(self
.ioctx
.read('abc'), b
'abc')
320 def test_write_full(self
):
321 self
.ioctx
.write('abc', b
'abc')
322 eq(self
.ioctx
.read('abc'), b
'abc')
323 self
.ioctx
.write_full('abc', b
'd')
324 eq(self
.ioctx
.read('abc'), b
'd')
326 def test_append(self
):
327 self
.ioctx
.write('abc', b
'a')
328 self
.ioctx
.append('abc', b
'b')
329 self
.ioctx
.append('abc', b
'c')
330 eq(self
.ioctx
.read('abc'), b
'abc')
332 def test_write_zeros(self
):
333 self
.ioctx
.write('abc', b
'a\0b\0c')
334 eq(self
.ioctx
.read('abc'), b
'a\0b\0c')
336 def test_trunc(self
):
337 self
.ioctx
.write('abc', b
'abc')
338 self
.ioctx
.trunc('abc', 2)
339 eq(self
.ioctx
.read('abc'), b
'ab')
340 size
= self
.ioctx
.stat('abc')[0]
343 def test_list_objects_empty(self
):
344 eq(list(self
.ioctx
.list_objects()), [])
346 def test_list_objects(self
):
347 self
.ioctx
.write('a', b
'')
348 self
.ioctx
.write('b', b
'foo')
349 self
.ioctx
.write_full('c', b
'bar')
350 self
.ioctx
.append('d', b
'jazz')
351 object_names
= [obj
.key
for obj
in self
.ioctx
.list_objects()]
352 eq(sorted(object_names
), ['a', 'b', 'c', 'd'])
354 def test_list_ns_objects(self
):
355 self
.ioctx
.write('a', b
'')
356 self
.ioctx
.write('b', b
'foo')
357 self
.ioctx
.write_full('c', b
'bar')
358 self
.ioctx
.append('d', b
'jazz')
359 self
.ioctx
.set_namespace("ns1")
360 self
.ioctx
.write('ns1-a', b
'')
361 self
.ioctx
.write('ns1-b', b
'foo')
362 self
.ioctx
.write_full('ns1-c', b
'bar')
363 self
.ioctx
.append('ns1-d', b
'jazz')
364 self
.ioctx
.append('d', b
'jazz')
365 self
.ioctx
.set_namespace(LIBRADOS_ALL_NSPACES
)
366 object_names
= [(obj
.nspace
, obj
.key
) for obj
in self
.ioctx
.list_objects()]
367 eq(sorted(object_names
), [('', 'a'), ('','b'), ('','c'), ('','d'),\
368 ('ns1', 'd'), ('ns1', 'ns1-a'), ('ns1', 'ns1-b'),\
369 ('ns1', 'ns1-c'), ('ns1', 'ns1-d')])
371 def test_xattrs(self
):
372 xattrs
= dict(a
=b
'1', b
=b
'2', c
=b
'3', d
=b
'a\0b', e
=b
'\0', f
='')
373 self
.ioctx
.write('abc', b
'')
374 for key
, value
in xattrs
.items():
375 self
.ioctx
.set_xattr('abc', key
, value
)
376 eq(self
.ioctx
.get_xattr('abc', key
), value
)
378 for key
, value
in self
.ioctx
.get_xattrs('abc'):
379 stored_xattrs
[key
] = value
380 eq(stored_xattrs
, xattrs
)
382 def test_obj_xattrs(self
):
383 xattrs
= dict(a
=b
'1', b
=b
'2', c
=b
'3', d
=b
'a\0b', e
=b
'\0', f
='')
384 self
.ioctx
.write('abc', b
'')
385 obj
= list(self
.ioctx
.list_objects())[0]
386 for key
, value
in xattrs
.items():
387 obj
.set_xattr(key
, value
)
388 eq(obj
.get_xattr(key
), value
)
390 for key
, value
in obj
.get_xattrs():
391 stored_xattrs
[key
] = value
392 eq(stored_xattrs
, xattrs
)
394 def test_create_snap(self
):
395 assert_raises(ObjectNotFound
, self
.ioctx
.remove_snap
, 'foo')
396 self
.ioctx
.create_snap('foo')
397 self
.ioctx
.remove_snap('foo')
399 def test_list_snaps_empty(self
):
400 eq(list(self
.ioctx
.list_snaps()), [])
402 def test_list_snaps(self
):
403 snaps
= ['snap1', 'snap2', 'snap3']
405 self
.ioctx
.create_snap(snap
)
406 listed_snaps
= [snap
.name
for snap
in self
.ioctx
.list_snaps()]
407 eq(snaps
, listed_snaps
)
409 def test_lookup_snap(self
):
410 self
.ioctx
.create_snap('foo')
411 snap
= self
.ioctx
.lookup_snap('foo')
414 def test_snap_timestamp(self
):
415 self
.ioctx
.create_snap('foo')
416 snap
= self
.ioctx
.lookup_snap('foo')
419 def test_remove_snap(self
):
420 self
.ioctx
.create_snap('foo')
421 (snap
,) = self
.ioctx
.list_snaps()
423 self
.ioctx
.remove_snap('foo')
424 eq(list(self
.ioctx
.list_snaps()), [])
426 def test_snap_rollback(self
):
427 self
.ioctx
.write("insnap", b
"contents1")
428 self
.ioctx
.create_snap("snap1")
429 self
.ioctx
.remove_object("insnap")
430 self
.ioctx
.snap_rollback("insnap", "snap1")
431 eq(self
.ioctx
.read("insnap"), b
"contents1")
432 self
.ioctx
.remove_snap("snap1")
433 self
.ioctx
.remove_object("insnap")
435 def test_snap_read(self
):
436 self
.ioctx
.write("insnap", b
"contents1")
437 self
.ioctx
.create_snap("snap1")
438 self
.ioctx
.remove_object("insnap")
439 snap
= self
.ioctx
.lookup_snap("snap1")
440 self
.ioctx
.set_read(snap
.snap_id
)
441 eq(self
.ioctx
.read("insnap"), b
"contents1")
442 self
.ioctx
.set_read(LIBRADOS_SNAP_HEAD
)
443 self
.ioctx
.write("inhead", b
"contents2")
444 eq(self
.ioctx
.read("inhead"), b
"contents2")
445 self
.ioctx
.remove_snap("snap1")
446 self
.ioctx
.remove_object("inhead")
448 def test_set_omap(self
):
449 keys
= ("1", "2", "3", "4")
450 values
= (b
"aaa", b
"bbb", b
"ccc", b
"\x04\x04\x04\x04")
451 with
WriteOpCtx(self
.ioctx
) as write_op
:
452 self
.ioctx
.set_omap(write_op
, keys
, values
)
453 write_op
.set_flags(LIBRADOS_OPERATION_SKIPRWLOCKS
)
454 self
.ioctx
.operate_write_op(write_op
, "hw")
455 with
ReadOpCtx(self
.ioctx
) as read_op
:
456 iter, ret
= self
.ioctx
.get_omap_vals(read_op
, "", "", 4)
458 self
.ioctx
.operate_read_op(read_op
, "hw")
460 eq(list(iter), [("2", b
"bbb"), ("3", b
"ccc"), ("4", b
"\x04\x04\x04\x04")])
461 with
ReadOpCtx(self
.ioctx
) as read_op
:
462 iter, ret
= self
.ioctx
.get_omap_vals(read_op
, "2", "", 4)
464 self
.ioctx
.operate_read_op(read_op
, "hw")
465 eq(("3", b
"ccc"), next(iter))
466 eq(list(iter), [("4", b
"\x04\x04\x04\x04")])
467 with
ReadOpCtx(self
.ioctx
) as read_op
:
468 iter, ret
= self
.ioctx
.get_omap_vals(read_op
, "", "2", 4)
470 read_op
.set_flags(LIBRADOS_OPERATION_BALANCE_READS
)
471 self
.ioctx
.operate_read_op(read_op
, "hw")
472 eq(list(iter), [("2", b
"bbb")])
474 def test_set_omap_aio(self
):
475 lock
= threading
.Condition()
483 keys
= ("1", "2", "3", "4")
484 values
= (b
"aaa", b
"bbb", b
"ccc", b
"\x04\x04\x04\x04")
485 with
WriteOpCtx(self
.ioctx
) as write_op
:
486 self
.ioctx
.set_omap(write_op
, keys
, values
)
487 comp
= self
.ioctx
.operate_aio_write_op(write_op
, "hw", cb
, cb
)
488 comp
.wait_for_complete()
493 eq(comp
.get_return_value(), 0)
495 with
ReadOpCtx(self
.ioctx
) as read_op
:
496 iter, ret
= self
.ioctx
.get_omap_vals(read_op
, "", "", 4)
498 comp
= self
.ioctx
.operate_aio_read_op(read_op
, "hw", cb
, cb
)
499 comp
.wait_for_complete()
504 eq(comp
.get_return_value(), 0)
506 eq(list(iter), [("2", b
"bbb"), ("3", b
"ccc"), ("4", b
"\x04\x04\x04\x04")])
508 def test_write_ops(self
):
509 with
WriteOpCtx(self
.ioctx
) as write_op
:
511 self
.ioctx
.operate_write_op(write_op
, "write_ops")
512 eq(self
.ioctx
.read('write_ops'), b
'')
513 write_op
.write_full(b
'1')
514 write_op
.append(b
'2')
515 self
.ioctx
.operate_write_op(write_op
, "write_ops")
516 eq(self
.ioctx
.read('write_ops'), b
'12')
517 write_op
.write_full(b
'12345')
518 write_op
.write(b
'x', 2)
519 self
.ioctx
.operate_write_op(write_op
, "write_ops")
520 eq(self
.ioctx
.read('write_ops'), b
'12x45')
521 write_op
.write_full(b
'12345')
523 self
.ioctx
.operate_write_op(write_op
, "write_ops")
524 eq(self
.ioctx
.read('write_ops'), b
'12\x00\x005')
525 write_op
.write_full(b
'12345')
527 self
.ioctx
.operate_write_op(write_op
, "write_ops")
528 eq(self
.ioctx
.read('write_ops'), b
'12')
530 self
.ioctx
.operate_write_op(write_op
, "write_ops")
531 with
assert_raises(ObjectNotFound
):
532 self
.ioctx
.read('write_ops')
534 def test_get_omap_vals_by_keys(self
):
535 keys
= ("1", "2", "3", "4")
536 values
= (b
"aaa", b
"bbb", b
"ccc", b
"\x04\x04\x04\x04")
537 with
WriteOpCtx(self
.ioctx
) as write_op
:
538 self
.ioctx
.set_omap(write_op
, keys
, values
)
539 self
.ioctx
.operate_write_op(write_op
, "hw")
540 with
ReadOpCtx(self
.ioctx
) as read_op
:
541 iter, ret
= self
.ioctx
.get_omap_vals_by_keys(read_op
,("3","4",))
543 self
.ioctx
.operate_read_op(read_op
, "hw")
544 eq(list(iter), [("3", b
"ccc"), ("4", b
"\x04\x04\x04\x04")])
545 with
ReadOpCtx(self
.ioctx
) as read_op
:
546 iter, ret
= self
.ioctx
.get_omap_vals_by_keys(read_op
,("3","4",))
548 with
assert_raises(ObjectNotFound
):
549 self
.ioctx
.operate_read_op(read_op
, "no_such")
551 def test_get_omap_keys(self
):
552 keys
= ("1", "2", "3")
553 values
= (b
"aaa", b
"bbb", b
"ccc")
554 with
WriteOpCtx(self
.ioctx
) as write_op
:
555 self
.ioctx
.set_omap(write_op
, keys
, values
)
556 self
.ioctx
.operate_write_op(write_op
, "hw")
557 with
ReadOpCtx(self
.ioctx
) as read_op
:
558 iter, ret
= self
.ioctx
.get_omap_keys(read_op
,"",2)
560 self
.ioctx
.operate_read_op(read_op
, "hw")
561 eq(list(iter), [("1", None), ("2", None)])
562 with
ReadOpCtx(self
.ioctx
) as read_op
:
563 iter, ret
= self
.ioctx
.get_omap_keys(read_op
,"",2)
565 with
assert_raises(ObjectNotFound
):
566 self
.ioctx
.operate_read_op(read_op
, "no_such")
568 def test_clear_omap(self
):
569 keys
= ("1", "2", "3")
570 values
= (b
"aaa", b
"bbb", b
"ccc")
571 with
WriteOpCtx(self
.ioctx
) as write_op
:
572 self
.ioctx
.set_omap(write_op
, keys
, values
)
573 self
.ioctx
.operate_write_op(write_op
, "hw")
574 with
WriteOpCtx(self
.ioctx
) as write_op_1
:
575 self
.ioctx
.clear_omap(write_op_1
)
576 self
.ioctx
.operate_write_op(write_op_1
, "hw")
577 with
ReadOpCtx(self
.ioctx
) as read_op
:
578 iter, ret
= self
.ioctx
.get_omap_vals_by_keys(read_op
,("1",))
580 self
.ioctx
.operate_read_op(read_op
, "hw")
583 def test_locator(self
):
584 self
.ioctx
.set_locator_key("bar")
585 self
.ioctx
.write('foo', b
'contents1')
586 objects
= [i
for i
in self
.ioctx
.list_objects()]
588 eq(self
.ioctx
.get_locator_key(), "bar")
589 self
.ioctx
.set_locator_key("")
591 objects
[0].write(b
"contents2")
592 eq(self
.ioctx
.get_locator_key(), "")
593 self
.ioctx
.set_locator_key("bar")
594 contents
= self
.ioctx
.read("foo")
595 eq(contents
, b
"contents2")
596 eq(self
.ioctx
.get_locator_key(), "bar")
598 objects
= [i
for i
in self
.ioctx
.list_objects()]
600 self
.ioctx
.set_locator_key("")
602 def test_aio_write(self
):
603 lock
= threading
.Condition()
610 comp
= self
.ioctx
.aio_write("foo", b
"bar", 0, cb
, cb
)
611 comp
.wait_for_complete()
616 eq(comp
.get_return_value(), 0)
617 contents
= self
.ioctx
.read("foo")
619 [i
.remove() for i
in self
.ioctx
.list_objects()]
621 def test_aio_write_no_comp_ref(self
):
622 lock
= threading
.Condition()
629 # NOTE(sileht): force don't save the comp into local var
630 # to ensure all references are correctly tracked into the lib
631 self
.ioctx
.aio_write("foo", b
"bar", 0, cb
, cb
)
635 contents
= self
.ioctx
.read("foo")
637 [i
.remove() for i
in self
.ioctx
.list_objects()]
639 def test_aio_append(self
):
640 lock
= threading
.Condition()
647 comp
= self
.ioctx
.aio_write("foo", b
"bar", 0, cb
, cb
)
648 comp2
= self
.ioctx
.aio_append("foo", b
"baz", cb
, cb
)
649 comp
.wait_for_complete()
650 contents
= self
.ioctx
.read("foo")
651 eq(contents
, b
"barbaz")
655 eq(comp
.get_return_value(), 0)
656 eq(comp2
.get_return_value(), 0)
657 [i
.remove() for i
in self
.ioctx
.list_objects()]
659 def test_aio_write_full(self
):
660 lock
= threading
.Condition()
667 self
.ioctx
.aio_write("foo", b
"barbaz", 0, cb
, cb
)
668 comp
= self
.ioctx
.aio_write_full("foo", b
"bar", cb
, cb
)
669 comp
.wait_for_complete()
674 eq(comp
.get_return_value(), 0)
675 contents
= self
.ioctx
.read("foo")
677 [i
.remove() for i
in self
.ioctx
.list_objects()]
679 def test_aio_stat(self
):
680 lock
= threading
.Condition()
682 def cb(_
, size
, mtime
):
687 comp
= self
.ioctx
.aio_stat("foo", cb
)
688 comp
.wait_for_complete()
692 eq(comp
.get_return_value(), -2)
694 self
.ioctx
.write("foo", b
"bar")
696 comp
= self
.ioctx
.aio_stat("foo", cb
)
697 comp
.wait_for_complete()
701 eq(comp
.get_return_value(), 0)
703 [i
.remove() for i
in self
.ioctx
.list_objects()]
705 def _take_down_acting_set(self
, pool
, objectname
):
706 # find acting_set for pool:objectname and take it down; used to
707 # verify that async reads don't complete while acting set is missing
714 r
, jsonout
, _
= self
.rados
.mon_command(json
.dumps(cmd
), b
'')
715 objmap
= json
.loads(jsonout
.decode("utf-8"))
716 acting_set
= objmap
['acting']
717 cmd
= {"prefix":"osd set", "key":"noup"}
718 r
, _
, _
= self
.rados
.mon_command(json
.dumps(cmd
), b
'')
720 cmd
= {"prefix":"osd down", "ids":[str(i
) for i
in acting_set
]}
721 r
, _
, _
= self
.rados
.mon_command(json
.dumps(cmd
), b
'')
724 # wait for OSDs to acknowledge the down
725 eq(self
.rados
.wait_for_latest_osdmap(), 0)
727 def _let_osds_back_up(self
):
728 cmd
= {"prefix":"osd unset", "key":"noup"}
729 r
, _
, _
= self
.rados
.mon_command(json
.dumps(cmd
), b
'')
732 def test_aio_read(self
):
733 # this is a list so that the local cb() can modify it
735 lock
= threading
.Condition()
740 payload
= b
"bar\000frob"
741 self
.ioctx
.write("foo", payload
)
743 # test1: use wait_for_complete() and wait for cb by
745 self
._take
_down
_acting
_set
('test_pool', 'foo')
746 comp
= self
.ioctx
.aio_read("foo", len(payload
), 0, cb
)
747 eq(False, comp
.is_complete())
749 eq(False, comp
.is_complete())
752 self
._let
_osds
_back
_up
()
753 comp
.wait_for_complete()
756 while retval
[0] is None and loops
<= 10:
761 eq(retval
[0], payload
)
762 eq(sys
.getrefcount(comp
), 2)
764 # test2: use wait_for_complete_and_cb(), verify retval[0] is
765 # set by the time we regain control
768 self
._take
_down
_acting
_set
('test_pool', 'foo')
769 comp
= self
.ioctx
.aio_read("foo", len(payload
), 0, cb
)
770 eq(False, comp
.is_complete())
772 eq(False, comp
.is_complete())
775 self
._let
_osds
_back
_up
()
777 comp
.wait_for_complete_and_cb()
778 assert(retval
[0] is not None)
779 eq(retval
[0], payload
)
780 eq(sys
.getrefcount(comp
), 2)
782 # test3: error case, use wait_for_complete_and_cb(), verify retval[0] is
783 # set by the time we regain control
786 self
._take
_down
_acting
_set
('test_pool', 'bar')
787 comp
= self
.ioctx
.aio_read("bar", len(payload
), 0, cb
)
788 eq(False, comp
.is_complete())
790 eq(False, comp
.is_complete())
793 self
._let
_osds
_back
_up
()
795 comp
.wait_for_complete_and_cb()
797 assert(comp
.get_return_value() < 0)
798 eq(sys
.getrefcount(comp
), 2)
800 [i
.remove() for i
in self
.ioctx
.list_objects()]
803 self
.ioctx
.lock_exclusive("foo", "lock", "locker", "desc_lock",
805 assert_raises(ObjectExists
,
806 self
.ioctx
.lock_exclusive
,
807 "foo", "lock", "locker", "desc_lock", 10000, 0)
808 self
.ioctx
.unlock("foo", "lock", "locker")
809 assert_raises(ObjectNotFound
, self
.ioctx
.unlock
, "foo", "lock", "locker")
811 self
.ioctx
.lock_shared("foo", "lock", "locker1", "tag", "desc_lock",
813 self
.ioctx
.lock_shared("foo", "lock", "locker2", "tag", "desc_lock",
815 assert_raises(ObjectBusy
,
816 self
.ioctx
.lock_exclusive
,
817 "foo", "lock", "locker3", "desc_lock", 10000, 0)
818 self
.ioctx
.unlock("foo", "lock", "locker1")
819 self
.ioctx
.unlock("foo", "lock", "locker2")
820 assert_raises(ObjectNotFound
, self
.ioctx
.unlock
, "foo", "lock", "locker1")
821 assert_raises(ObjectNotFound
, self
.ioctx
.unlock
, "foo", "lock", "locker2")
823 def test_execute(self
):
824 self
.ioctx
.write("foo", b
"") # ensure object exists
826 ret
, buf
= self
.ioctx
.execute("foo", "hello", "say_hello", b
"")
827 eq(buf
, b
"Hello, world!")
829 ret
, buf
= self
.ioctx
.execute("foo", "hello", "say_hello", b
"nose")
830 eq(buf
, b
"Hello, nose!")
832 def test_aio_execute(self
):
835 lock
= threading
.Condition()
838 if retval
[0] is None:
842 self
.ioctx
.write("foo", b
"") # ensure object exists
844 comp
= self
.ioctx
.aio_execute("foo", "hello", "say_hello", b
"", 32, cb
, cb
)
845 comp
.wait_for_complete()
849 eq(comp
.get_return_value(), 13)
850 eq(retval
[0], b
"Hello, world!")
853 comp
= self
.ioctx
.aio_execute("foo", "hello", "say_hello", b
"nose", 32, cb
, cb
)
854 comp
.wait_for_complete()
858 eq(comp
.get_return_value(), 12)
859 eq(retval
[0], b
"Hello, nose!")
861 [i
.remove() for i
in self
.ioctx
.list_objects()]
863 def test_applications(self
):
864 cmd
= {"prefix":"osd dump", "format":"json"}
865 ret
, buf
, errs
= self
.rados
.mon_command(json
.dumps(cmd
), b
'')
868 release
= json
.loads(buf
.decode("utf-8")).get("require_osd_release",
870 if not release
or release
[0] < 'l':
873 eq([], self
.ioctx
.application_list())
875 self
.ioctx
.application_enable("app1")
876 assert_raises(Error
, self
.ioctx
.application_enable
, "app2")
877 self
.ioctx
.application_enable("app2", True)
879 assert_raises(Error
, self
.ioctx
.application_metadata_list
, "dne")
880 eq([], self
.ioctx
.application_metadata_list("app1"))
882 assert_raises(Error
, self
.ioctx
.application_metadata_set
, "dne", "key",
884 self
.ioctx
.application_metadata_set("app1", "key1", "val1")
885 self
.ioctx
.application_metadata_set("app1", "key2", "val2")
886 self
.ioctx
.application_metadata_set("app2", "key1", "val1")
888 eq([("key1", "val1"), ("key2", "val2")],
889 self
.ioctx
.application_metadata_list("app1"))
891 self
.ioctx
.application_metadata_remove("app1", "key1")
892 eq([("key2", "val2")], self
.ioctx
.application_metadata_list("app1"))
894 class TestObject(object):
897 self
.rados
= Rados(conffile
='')
899 self
.rados
.create_pool('test_pool')
900 assert self
.rados
.pool_exists('test_pool')
901 self
.ioctx
= self
.rados
.open_ioctx('test_pool')
902 self
.ioctx
.write('foo', b
'bar')
903 self
.object = Object(self
.ioctx
, 'foo')
908 self
.rados
.delete_pool('test_pool')
909 self
.rados
.shutdown()
913 eq(self
.object.read(3), b
'bar')
914 eq(self
.object.read(100), b
'')
917 self
.object.write(b
'blah')
919 eq(self
.object.read(4), b
'blah')
921 eq(self
.object.read(3), b
'lah')
923 def test_write(self
):
924 self
.object.write(b
'barbaz')
926 eq(self
.object.read(3), b
'bar')
927 eq(self
.object.read(3), b
'baz')
929 class TestIoCtxSelfManagedSnaps(object):
931 self
.rados
= Rados(conffile
='')
933 self
.rados
.create_pool('test_pool')
934 assert self
.rados
.pool_exists('test_pool')
935 self
.ioctx
= self
.rados
.open_ioctx('test_pool')
938 cmd
= {"prefix":"osd unset", "key":"noup"}
939 self
.rados
.mon_command(json
.dumps(cmd
), b
'')
941 self
.rados
.delete_pool('test_pool')
942 self
.rados
.shutdown()
945 # cannot mix-and-match pool and self-managed snapshot mode
946 self
.ioctx
.set_self_managed_snap_write([])
947 self
.ioctx
.write('abc', b
'abc')
948 snap_id_1
= self
.ioctx
.create_self_managed_snap()
949 self
.ioctx
.set_self_managed_snap_write([snap_id_1
])
951 self
.ioctx
.write('abc', b
'def')
952 snap_id_2
= self
.ioctx
.create_self_managed_snap()
953 self
.ioctx
.set_self_managed_snap_write([snap_id_1
, snap_id_2
])
955 self
.ioctx
.write('abc', b
'ghi')
957 self
.ioctx
.rollback_self_managed_snap('abc', snap_id_1
)
958 eq(self
.ioctx
.read('abc'), b
'abc')
960 self
.ioctx
.rollback_self_managed_snap('abc', snap_id_2
)
961 eq(self
.ioctx
.read('abc'), b
'def')
963 self
.ioctx
.remove_self_managed_snap(snap_id_1
)
964 self
.ioctx
.remove_self_managed_snap(snap_id_2
)
966 class TestCommand(object):
969 self
.rados
= Rados(conffile
='')
973 self
.rados
.shutdown()
975 def test_monmap_dump(self
):
977 # check for success and some plain output with epoch in it
978 cmd
= {"prefix":"mon dump"}
979 ret
, buf
, errs
= self
.rados
.mon_command(json
.dumps(cmd
), b
'', timeout
=30)
982 assert(b
'epoch' in buf
)
984 # JSON, and grab current epoch
985 cmd
['format'] = 'json'
986 ret
, buf
, errs
= self
.rados
.mon_command(json
.dumps(cmd
), b
'', timeout
=30)
989 d
= json
.loads(buf
.decode("utf-8"))
993 # assume epoch + 1000 does not exist; test for ENOENT
994 cmd
['epoch'] = epoch
+ 1000
995 ret
, buf
, errs
= self
.rados
.mon_command(json
.dumps(cmd
), b
'', timeout
=30)
996 eq(ret
, -errno
.ENOENT
)
1000 # send to specific target by name
1001 target
= d
['mons'][0]['name']
1003 ret
, buf
, errs
= self
.rados
.mon_command(json
.dumps(cmd
), b
'', timeout
=30,
1007 d
= json
.loads(buf
.decode("utf-8"))
1008 assert('epoch' in d
)
1011 target
= d
['mons'][0]['rank']
1013 ret
, buf
, errs
= self
.rados
.mon_command(json
.dumps(cmd
), b
'', timeout
=30,
1017 d
= json
.loads(buf
.decode("utf-8"))
1018 assert('epoch' in d
)
1020 def test_osd_bench(self
):
1021 cmd
= dict(prefix
='bench', size
=4096, count
=8192)
1022 ret
, buf
, err
= self
.rados
.osd_command(0, json
.dumps(cmd
), b
'',
1026 out
= json
.loads(err
)
1027 eq(out
['blocksize'], cmd
['size'])
1028 eq(out
['bytes_written'], cmd
['count'])
1030 def test_ceph_osd_pool_create_utf8(self
):
1032 # Use encoded bytestring
1033 poolname
= b
"\351\273\205"
1037 cmd
= {"prefix": "osd pool create", "pg_num": 16, "pool": poolname
}
1038 ret
, buf
, out
= self
.rados
.mon_command(json
.dumps(cmd
), b
'')
1041 eq(u
"pool '\u9ec5' created", out
)