]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | # -*- coding: utf-8 -*- |
2 | # pylint: disable=too-many-public-methods | |
3 | ||
4 | from __future__ import absolute_import | |
5 | ||
6 | import time | |
7 | ||
8 | from .helper import DashboardTestCase, JObj, JLeaf, JList | |
9 | ||
10 | ||
11 | class RbdTest(DashboardTestCase): | |
12 | AUTH_ROLES = ['pool-manager', 'block-manager'] | |
13 | ||
14 | @classmethod | |
15 | def create_pool(cls, name, pg_num, pool_type, application='rbd'): | |
16 | data = { | |
17 | 'pool': name, | |
18 | 'pg_num': pg_num, | |
19 | 'pool_type': pool_type, | |
20 | 'application_metadata': [application] | |
21 | } | |
22 | if pool_type == 'erasure': | |
23 | data['flags'] = ['ec_overwrites'] | |
24 | cls._task_post("/api/pool", data) | |
25 | ||
26 | @DashboardTestCase.RunAs('test', 'test', [{'rbd-image': ['create', 'update', 'delete']}]) | |
27 | def test_read_access_permissions(self): | |
28 | self._get('/api/block/image') | |
29 | self.assertStatus(403) | |
30 | self._get('/api/block/image/pool/image') | |
31 | self.assertStatus(403) | |
32 | ||
33 | @DashboardTestCase.RunAs('test', 'test', [{'rbd-image': ['read', 'update', 'delete']}]) | |
34 | def test_create_access_permissions(self): | |
35 | self.create_image('pool', 'name', 0) | |
36 | self.assertStatus(403) | |
37 | self.create_snapshot('pool', 'image', 'snapshot') | |
38 | self.assertStatus(403) | |
39 | self.copy_image('src_pool', 'src_image', 'dest_pool', 'dest_image') | |
40 | self.assertStatus(403) | |
41 | self.clone_image('parent_pool', 'parent_image', 'parent_snap', 'pool', 'name') | |
42 | self.assertStatus(403) | |
43 | ||
44 | @DashboardTestCase.RunAs('test', 'test', [{'rbd-image': ['read', 'create', 'delete']}]) | |
45 | def test_update_access_permissions(self): | |
46 | self.edit_image('pool', 'image') | |
47 | self.assertStatus(403) | |
48 | self.update_snapshot('pool', 'image', 'snapshot', None, None) | |
49 | self.assertStatus(403) | |
50 | self._task_post('/api/block/image/rbd/rollback_img/snap/snap1/rollback') | |
51 | self.assertStatus(403) | |
52 | self.flatten_image('pool', 'image') | |
53 | self.assertStatus(403) | |
54 | ||
55 | @DashboardTestCase.RunAs('test', 'test', [{'rbd-image': ['read', 'create', 'update']}]) | |
56 | def test_delete_access_permissions(self): | |
57 | self.remove_image('pool', 'image') | |
58 | self.assertStatus(403) | |
59 | self.remove_snapshot('pool', 'image', 'snapshot') | |
60 | self.assertStatus(403) | |
61 | ||
62 | @classmethod | |
63 | def create_image(cls, pool, name, size, **kwargs): | |
64 | data = {'name': name, 'pool_name': pool, 'size': size} | |
65 | data.update(kwargs) | |
66 | return cls._task_post('/api/block/image', data) | |
67 | ||
68 | @classmethod | |
69 | def clone_image(cls, parent_pool, parent_image, parent_snap, pool, name, | |
70 | **kwargs): | |
71 | # pylint: disable=too-many-arguments | |
72 | data = {'child_image_name': name, 'child_pool_name': pool} | |
73 | data.update(kwargs) | |
74 | return cls._task_post('/api/block/image/{}/{}/snap/{}/clone' | |
75 | .format(parent_pool, parent_image, parent_snap), | |
76 | data) | |
77 | ||
78 | @classmethod | |
79 | def copy_image(cls, src_pool, src_image, dest_pool, dest_image, **kwargs): | |
80 | # pylint: disable=too-many-arguments | |
81 | data = {'dest_image_name': dest_image, 'dest_pool_name': dest_pool} | |
82 | data.update(kwargs) | |
83 | return cls._task_post('/api/block/image/{}/{}/copy' | |
84 | .format(src_pool, src_image), data) | |
85 | ||
86 | @classmethod | |
87 | def remove_image(cls, pool, image): | |
88 | return cls._task_delete('/api/block/image/{}/{}'.format(pool, image)) | |
89 | ||
90 | # pylint: disable=too-many-arguments | |
91 | @classmethod | |
92 | def edit_image(cls, pool, image, name=None, size=None, features=None, **kwargs): | |
93 | kwargs.update({'name': name, 'size': size, 'features': features}) | |
94 | return cls._task_put('/api/block/image/{}/{}'.format(pool, image), kwargs) | |
95 | ||
96 | @classmethod | |
97 | def flatten_image(cls, pool, image): | |
98 | return cls._task_post('/api/block/image/{}/{}/flatten'.format(pool, image)) | |
99 | ||
100 | @classmethod | |
101 | def create_snapshot(cls, pool, image, snapshot): | |
102 | return cls._task_post('/api/block/image/{}/{}/snap'.format(pool, image), | |
103 | {'snapshot_name': snapshot}) | |
104 | ||
105 | @classmethod | |
106 | def remove_snapshot(cls, pool, image, snapshot): | |
107 | return cls._task_delete('/api/block/image/{}/{}/snap/{}'.format(pool, image, snapshot)) | |
108 | ||
109 | @classmethod | |
110 | def update_snapshot(cls, pool, image, snapshot, new_name, is_protected): | |
111 | return cls._task_put('/api/block/image/{}/{}/snap/{}'.format(pool, image, snapshot), | |
112 | {'new_snap_name': new_name, 'is_protected': is_protected}) | |
113 | ||
114 | @classmethod | |
115 | def setUpClass(cls): | |
116 | super(RbdTest, cls).setUpClass() | |
eafe8130 TL |
117 | cls.create_pool('rbd', 2**3, 'replicated') |
118 | cls.create_pool('rbd_iscsi', 2**3, 'replicated') | |
11fdf7f2 TL |
119 | |
120 | cls.create_image('rbd', 'img1', 2**30) | |
121 | cls.create_image('rbd', 'img2', 2*2**30) | |
122 | cls.create_image('rbd_iscsi', 'img1', 2**30) | |
123 | cls.create_image('rbd_iscsi', 'img2', 2*2**30) | |
124 | ||
125 | osd_metadata = cls.ceph_cluster.mon_manager.get_osd_metadata() | |
126 | cls.bluestore_support = True | |
127 | for osd in osd_metadata: | |
128 | if osd['osd_objectstore'] != 'bluestore': | |
129 | cls.bluestore_support = False | |
130 | break | |
131 | ||
132 | @classmethod | |
133 | def tearDownClass(cls): | |
134 | super(RbdTest, cls).tearDownClass() | |
135 | cls._ceph_cmd(['osd', 'pool', 'delete', 'rbd', 'rbd', '--yes-i-really-really-mean-it']) | |
136 | cls._ceph_cmd(['osd', 'pool', 'delete', 'rbd_iscsi', 'rbd_iscsi', | |
137 | '--yes-i-really-really-mean-it']) | |
138 | cls._ceph_cmd(['osd', 'pool', 'delete', 'rbd_data', 'rbd_data', | |
139 | '--yes-i-really-really-mean-it']) | |
140 | ||
141 | @classmethod | |
142 | def create_image_in_trash(cls, pool, name, delay=0): | |
143 | cls.create_image(pool, name, 10240) | |
144 | img = cls._get('/api/block/image/{}/{}'.format(pool, name)) | |
145 | ||
146 | cls._task_post("/api/block/image/{}/{}/move_trash".format(pool, name), | |
147 | {'delay': delay}) | |
148 | ||
149 | return img['id'] | |
150 | ||
151 | @classmethod | |
152 | def remove_trash(cls, pool, image_id, image_name, force=False): | |
153 | return cls._task_delete('/api/block/image/trash/{}/{}/?image_name={}&force={}'.format('rbd', image_id, image_name, force)) | |
154 | ||
155 | @classmethod | |
156 | def get_trash(cls, pool, image_id): | |
157 | trash = cls._get('/api/block/image/trash/?pool_name={}'.format(pool)) | |
158 | if isinstance(trash, list): | |
159 | for pool in trash: | |
160 | for image in pool['value']: | |
161 | if image['id'] == image_id: | |
162 | return image | |
163 | ||
164 | return None | |
165 | ||
166 | def _validate_image(self, img, **kwargs): | |
167 | """ | |
168 | Example of an RBD image json: | |
169 | ||
170 | { | |
171 | "size": 1073741824, | |
172 | "obj_size": 4194304, | |
173 | "num_objs": 256, | |
174 | "order": 22, | |
175 | "block_name_prefix": "rbd_data.10ae2ae8944a", | |
176 | "name": "img1", | |
177 | "pool_name": "rbd", | |
178 | "features": 61, | |
179 | "features_name": ["deep-flatten", "exclusive-lock", "fast-diff", "layering", | |
180 | "object-map"] | |
181 | } | |
182 | """ | |
183 | schema = JObj(sub_elems={ | |
184 | 'size': JLeaf(int), | |
185 | 'obj_size': JLeaf(int), | |
186 | 'num_objs': JLeaf(int), | |
187 | 'order': JLeaf(int), | |
188 | 'block_name_prefix': JLeaf(str), | |
189 | 'name': JLeaf(str), | |
190 | 'id': JLeaf(str), | |
191 | 'pool_name': JLeaf(str), | |
192 | 'features': JLeaf(int), | |
193 | 'features_name': JList(JLeaf(str)), | |
194 | 'stripe_count': JLeaf(int, none=True), | |
195 | 'stripe_unit': JLeaf(int, none=True), | |
196 | 'parent': JObj(sub_elems={'pool_name': JLeaf(str), | |
197 | 'image_name': JLeaf(str), | |
198 | 'snap_name': JLeaf(str)}, none=True), | |
199 | 'data_pool': JLeaf(str, none=True), | |
200 | 'snapshots': JList(JLeaf(dict)), | |
201 | 'timestamp': JLeaf(str, none=True), | |
202 | 'disk_usage': JLeaf(int, none=True), | |
203 | 'total_disk_usage': JLeaf(int, none=True), | |
204 | 'configuration': JList(JObj(sub_elems={ | |
205 | 'name': JLeaf(str), | |
206 | 'source': JLeaf(int), | |
207 | 'value': JLeaf(str), | |
208 | })), | |
209 | }) | |
210 | self.assertSchema(img, schema) | |
211 | ||
212 | for k, v in kwargs.items(): | |
213 | if isinstance(v, list): | |
214 | self.assertSetEqual(set(img[k]), set(v)) | |
215 | else: | |
216 | self.assertEqual(img[k], v) | |
217 | ||
218 | def _validate_snapshot(self, snap, **kwargs): | |
219 | self.assertIn('id', snap) | |
220 | self.assertIn('name', snap) | |
221 | self.assertIn('is_protected', snap) | |
222 | self.assertIn('timestamp', snap) | |
223 | self.assertIn('size', snap) | |
224 | self.assertIn('children', snap) | |
225 | ||
226 | for k, v in kwargs.items(): | |
227 | if isinstance(v, list): | |
228 | self.assertSetEqual(set(snap[k]), set(v)) | |
229 | else: | |
230 | self.assertEqual(snap[k], v) | |
231 | ||
232 | def _validate_snapshot_list(self, snap_list, snap_name=None, **kwargs): | |
233 | found = False | |
234 | for snap in snap_list: | |
235 | self.assertIn('name', snap) | |
236 | if snap_name and snap['name'] == snap_name: | |
237 | found = True | |
238 | self._validate_snapshot(snap, **kwargs) | |
239 | break | |
240 | if snap_name and not found: | |
241 | self.fail("Snapshot {} not found".format(snap_name)) | |
242 | ||
243 | def test_list(self): | |
244 | data = self._view_cache_get('/api/block/image') | |
245 | self.assertStatus(200) | |
246 | self.assertEqual(len(data), 2) | |
247 | ||
248 | for pool_view in data: | |
249 | self.assertEqual(pool_view['status'], 0) | |
250 | self.assertIsNotNone(pool_view['value']) | |
251 | self.assertIn('pool_name', pool_view) | |
252 | self.assertIn(pool_view['pool_name'], ['rbd', 'rbd_iscsi']) | |
253 | image_list = pool_view['value'] | |
254 | self.assertEqual(len(image_list), 2) | |
255 | ||
256 | for img in image_list: | |
257 | self.assertIn('name', img) | |
258 | self.assertIn('pool_name', img) | |
259 | self.assertIn(img['pool_name'], ['rbd', 'rbd_iscsi']) | |
260 | if img['name'] == 'img1': | |
261 | self._validate_image(img, size=1073741824, | |
262 | num_objs=256, obj_size=4194304, | |
263 | features_name=['deep-flatten', | |
264 | 'exclusive-lock', | |
265 | 'fast-diff', | |
266 | 'layering', | |
267 | 'object-map']) | |
268 | elif img['name'] == 'img2': | |
269 | self._validate_image(img, size=2147483648, | |
270 | num_objs=512, obj_size=4194304, | |
271 | features_name=['deep-flatten', | |
272 | 'exclusive-lock', | |
273 | 'fast-diff', | |
274 | 'layering', | |
275 | 'object-map']) | |
276 | else: | |
277 | assert False, "Unexcepted image '{}' in result list".format(img['name']) | |
278 | ||
279 | def test_create(self): | |
280 | rbd_name = 'test_rbd' | |
281 | self.create_image('rbd', rbd_name, 10240) | |
282 | self.assertStatus(201) | |
283 | ||
284 | img = self._get('/api/block/image/rbd/test_rbd') | |
285 | self.assertStatus(200) | |
286 | ||
287 | self._validate_image(img, name=rbd_name, size=10240, | |
288 | num_objs=1, obj_size=4194304, | |
289 | features_name=['deep-flatten', | |
290 | 'exclusive-lock', | |
291 | 'fast-diff', 'layering', | |
292 | 'object-map']) | |
293 | ||
294 | self.remove_image('rbd', rbd_name) | |
295 | ||
296 | def test_create_with_configuration(self): | |
297 | pool = 'rbd' | |
298 | image_name = 'image_with_config' | |
299 | size = 10240 | |
300 | configuration = { | |
301 | 'rbd_qos_bps_limit': 10240, | |
302 | 'rbd_qos_bps_burst': 10240 * 2, | |
303 | } | |
304 | expected = [{ | |
305 | 'name': 'rbd_qos_bps_limit', | |
306 | 'source': 2, | |
307 | 'value': str(10240), | |
308 | }, { | |
309 | 'name': 'rbd_qos_bps_burst', | |
310 | 'source': 2, | |
311 | 'value': str(10240 * 2), | |
312 | }] | |
313 | ||
314 | self.create_image(pool, image_name, size, configuration=configuration) | |
315 | self.assertStatus(201) | |
316 | img = self._get('/api/block/image/rbd/{}'.format(image_name)) | |
317 | self.assertStatus(200) | |
318 | for conf in expected: | |
319 | self.assertIn(conf, img['configuration']) | |
320 | ||
321 | self.remove_image(pool, image_name) | |
322 | ||
323 | def test_create_rbd_in_data_pool(self): | |
324 | if not self.bluestore_support: | |
325 | self.skipTest('requires bluestore cluster') | |
326 | ||
eafe8130 | 327 | self.create_pool('data_pool', 2**4, 'erasure') |
11fdf7f2 TL |
328 | |
329 | rbd_name = 'test_rbd_in_data_pool' | |
330 | self.create_image('rbd', rbd_name, 10240, data_pool='data_pool') | |
331 | self.assertStatus(201) | |
332 | ||
333 | img = self._get('/api/block/image/rbd/test_rbd_in_data_pool') | |
334 | self.assertStatus(200) | |
335 | ||
336 | self._validate_image(img, name=rbd_name, size=10240, | |
337 | num_objs=1, obj_size=4194304, | |
338 | data_pool='data_pool', | |
339 | features_name=['data-pool', 'deep-flatten', | |
340 | 'exclusive-lock', | |
341 | 'fast-diff', 'layering', | |
342 | 'object-map']) | |
343 | ||
344 | self.remove_image('rbd', rbd_name) | |
345 | self.assertStatus(204) | |
346 | self._ceph_cmd(['osd', 'pool', 'delete', 'data_pool', 'data_pool', | |
347 | '--yes-i-really-really-mean-it']) | |
348 | ||
349 | def test_create_rbd_twice(self): | |
350 | res = self.create_image('rbd', 'test_rbd_twice', 10240) | |
351 | ||
352 | res = self.create_image('rbd', 'test_rbd_twice', 10240) | |
353 | self.assertStatus(400) | |
354 | self.assertEqual(res, {"code": '17', 'status': 400, "component": "rbd", | |
355 | "detail": "[errno 17] error creating image", | |
356 | 'task': {'name': 'rbd/create', | |
357 | 'metadata': {'pool_name': 'rbd', | |
358 | 'image_name': 'test_rbd_twice'}}}) | |
359 | self.remove_image('rbd', 'test_rbd_twice') | |
360 | self.assertStatus(204) | |
361 | ||
362 | def test_snapshots_and_clone_info(self): | |
363 | self.create_snapshot('rbd', 'img1', 'snap1') | |
364 | self.create_snapshot('rbd', 'img1', 'snap2') | |
365 | self._rbd_cmd(['snap', 'protect', 'rbd/img1@snap1']) | |
366 | self._rbd_cmd(['clone', 'rbd/img1@snap1', 'rbd_iscsi/img1_clone']) | |
367 | ||
368 | img = self._get('/api/block/image/rbd/img1') | |
369 | self.assertStatus(200) | |
370 | self._validate_image(img, name='img1', size=1073741824, | |
371 | num_objs=256, obj_size=4194304, parent=None, | |
372 | features_name=['deep-flatten', 'exclusive-lock', | |
373 | 'fast-diff', 'layering', | |
374 | 'object-map']) | |
375 | for snap in img['snapshots']: | |
376 | if snap['name'] == 'snap1': | |
377 | self._validate_snapshot(snap, is_protected=True) | |
378 | self.assertEqual(len(snap['children']), 1) | |
379 | self.assertDictEqual(snap['children'][0], | |
380 | {'pool_name': 'rbd_iscsi', | |
381 | 'image_name': 'img1_clone'}) | |
382 | elif snap['name'] == 'snap2': | |
383 | self._validate_snapshot(snap, is_protected=False) | |
384 | ||
385 | img = self._get('/api/block/image/rbd_iscsi/img1_clone') | |
386 | self.assertStatus(200) | |
387 | self._validate_image(img, name='img1_clone', size=1073741824, | |
388 | num_objs=256, obj_size=4194304, | |
389 | parent={'pool_name': 'rbd', 'image_name': 'img1', | |
390 | 'snap_name': 'snap1'}, | |
391 | features_name=['deep-flatten', 'exclusive-lock', | |
392 | 'fast-diff', 'layering', | |
393 | 'object-map']) | |
394 | self.remove_image('rbd_iscsi', 'img1_clone') | |
395 | self.assertStatus(204) | |
396 | ||
397 | def test_disk_usage(self): | |
398 | self._rbd_cmd(['bench', '--io-type', 'write', '--io-total', '50M', 'rbd/img2']) | |
399 | self.create_snapshot('rbd', 'img2', 'snap1') | |
400 | self._rbd_cmd(['bench', '--io-type', 'write', '--io-total', '20M', 'rbd/img2']) | |
401 | self.create_snapshot('rbd', 'img2', 'snap2') | |
402 | self._rbd_cmd(['bench', '--io-type', 'write', '--io-total', '10M', 'rbd/img2']) | |
403 | self.create_snapshot('rbd', 'img2', 'snap3') | |
404 | self._rbd_cmd(['bench', '--io-type', 'write', '--io-total', '5M', 'rbd/img2']) | |
405 | img = self._get('/api/block/image/rbd/img2') | |
406 | self.assertStatus(200) | |
407 | self._validate_image(img, name='img2', size=2147483648, | |
408 | total_disk_usage=268435456, disk_usage=67108864) | |
409 | ||
410 | def test_delete_non_existent_image(self): | |
411 | res = self.remove_image('rbd', 'i_dont_exist') | |
412 | self.assertStatus(400) | |
413 | self.assertEqual(res, {u'code': u'2', "status": 400, "component": "rbd", | |
414 | "detail": "[errno 2] error removing image", | |
415 | 'task': {'name': 'rbd/delete', | |
416 | 'metadata': {'pool_name': 'rbd', | |
417 | 'image_name': 'i_dont_exist'}}}) | |
418 | ||
419 | def test_image_delete(self): | |
420 | self.create_image('rbd', 'delete_me', 2**30) | |
421 | self.assertStatus(201) | |
422 | self.create_snapshot('rbd', 'delete_me', 'snap1') | |
423 | self.assertStatus(201) | |
424 | self.create_snapshot('rbd', 'delete_me', 'snap2') | |
425 | self.assertStatus(201) | |
426 | ||
427 | img = self._get('/api/block/image/rbd/delete_me') | |
428 | self.assertStatus(200) | |
429 | self._validate_image(img, name='delete_me', size=2**30) | |
430 | self.assertEqual(len(img['snapshots']), 2) | |
431 | ||
432 | self.remove_snapshot('rbd', 'delete_me', 'snap1') | |
433 | self.assertStatus(204) | |
434 | self.remove_snapshot('rbd', 'delete_me', 'snap2') | |
435 | self.assertStatus(204) | |
436 | ||
437 | img = self._get('/api/block/image/rbd/delete_me') | |
438 | self.assertStatus(200) | |
439 | self._validate_image(img, name='delete_me', size=2**30) | |
440 | self.assertEqual(len(img['snapshots']), 0) | |
441 | ||
442 | self.remove_image('rbd', 'delete_me') | |
443 | self.assertStatus(204) | |
444 | ||
445 | def test_image_rename(self): | |
446 | self.create_image('rbd', 'edit_img', 2**30) | |
447 | self.assertStatus(201) | |
448 | self._get('/api/block/image/rbd/edit_img') | |
449 | self.assertStatus(200) | |
450 | self.edit_image('rbd', 'edit_img', 'new_edit_img') | |
451 | self.assertStatus(200) | |
452 | self._get('/api/block/image/rbd/edit_img') | |
453 | self.assertStatus(404) | |
454 | self._get('/api/block/image/rbd/new_edit_img') | |
455 | self.assertStatus(200) | |
456 | self.remove_image('rbd', 'new_edit_img') | |
457 | self.assertStatus(204) | |
458 | ||
459 | def test_image_resize(self): | |
460 | self.create_image('rbd', 'edit_img', 2**30) | |
461 | self.assertStatus(201) | |
462 | img = self._get('/api/block/image/rbd/edit_img') | |
463 | self.assertStatus(200) | |
464 | self._validate_image(img, size=2**30) | |
465 | self.edit_image('rbd', 'edit_img', size=2*2**30) | |
466 | self.assertStatus(200) | |
467 | img = self._get('/api/block/image/rbd/edit_img') | |
468 | self.assertStatus(200) | |
469 | self._validate_image(img, size=2*2**30) | |
470 | self.remove_image('rbd', 'edit_img') | |
471 | self.assertStatus(204) | |
472 | ||
473 | def test_image_change_features(self): | |
474 | self.create_image('rbd', 'edit_img', 2**30, features=["layering"]) | |
475 | self.assertStatus(201) | |
476 | img = self._get('/api/block/image/rbd/edit_img') | |
477 | self.assertStatus(200) | |
478 | self._validate_image(img, features_name=["layering"]) | |
479 | self.edit_image('rbd', 'edit_img', | |
480 | features=["fast-diff", "object-map", "exclusive-lock"]) | |
81eedcae | 481 | self.assertStatus(200) |
11fdf7f2 TL |
482 | img = self._get('/api/block/image/rbd/edit_img') |
483 | self.assertStatus(200) | |
484 | self._validate_image(img, features_name=['exclusive-lock', | |
485 | 'fast-diff', 'layering', | |
486 | 'object-map']) | |
487 | self.edit_image('rbd', 'edit_img', | |
488 | features=["journaling", "exclusive-lock"]) | |
81eedcae | 489 | self.assertStatus(200) |
11fdf7f2 TL |
490 | img = self._get('/api/block/image/rbd/edit_img') |
491 | self.assertStatus(200) | |
492 | self._validate_image(img, features_name=['exclusive-lock', | |
493 | 'journaling', 'layering']) | |
494 | self.remove_image('rbd', 'edit_img') | |
495 | self.assertStatus(204) | |
496 | ||
497 | def test_image_change_config(self): | |
498 | pool = 'rbd' | |
499 | image = 'image_with_config' | |
500 | initial_conf = { | |
501 | 'rbd_qos_bps_limit': 10240, | |
502 | 'rbd_qos_write_iops_limit': None | |
503 | } | |
504 | initial_expect = [{ | |
505 | 'name': 'rbd_qos_bps_limit', | |
506 | 'source': 2, | |
507 | 'value': '10240', | |
508 | }, { | |
509 | 'name': 'rbd_qos_write_iops_limit', | |
510 | 'source': 0, | |
511 | 'value': '0', | |
512 | }] | |
513 | new_conf = { | |
514 | 'rbd_qos_bps_limit': 0, | |
515 | 'rbd_qos_bps_burst': 20480, | |
516 | 'rbd_qos_write_iops_limit': None | |
517 | } | |
518 | new_expect = [{ | |
519 | 'name': 'rbd_qos_bps_limit', | |
520 | 'source': 2, | |
521 | 'value': '0', | |
522 | }, { | |
523 | 'name': 'rbd_qos_bps_burst', | |
524 | 'source': 2, | |
525 | 'value': '20480', | |
526 | }, { | |
527 | 'name': 'rbd_qos_write_iops_limit', | |
528 | 'source': 0, | |
529 | 'value': '0', | |
530 | }] | |
531 | ||
532 | self.create_image(pool, image, 2**30, configuration=initial_conf) | |
533 | self.assertStatus(201) | |
534 | img = self._get('/api/block/image/{}/{}'.format(pool, image)) | |
535 | self.assertStatus(200) | |
536 | for conf in initial_expect: | |
537 | self.assertIn(conf, img['configuration']) | |
538 | ||
539 | self.edit_image(pool, image, configuration=new_conf) | |
540 | img = self._get('/api/block/image/{}/{}'.format(pool, image)) | |
541 | self.assertStatus(200) | |
542 | for conf in new_expect: | |
543 | self.assertIn(conf, img['configuration']) | |
544 | ||
545 | self.remove_image(pool, image) | |
546 | self.assertStatus(204) | |
547 | ||
548 | def test_update_snapshot(self): | |
549 | self.create_snapshot('rbd', 'img1', 'snap5') | |
550 | self.assertStatus(201) | |
551 | img = self._get('/api/block/image/rbd/img1') | |
552 | self._validate_snapshot_list(img['snapshots'], 'snap5', is_protected=False) | |
553 | ||
554 | self.update_snapshot('rbd', 'img1', 'snap5', 'snap6', None) | |
555 | self.assertStatus(200) | |
556 | img = self._get('/api/block/image/rbd/img1') | |
557 | self._validate_snapshot_list(img['snapshots'], 'snap6', is_protected=False) | |
558 | ||
559 | self.update_snapshot('rbd', 'img1', 'snap6', None, True) | |
560 | self.assertStatus(200) | |
561 | img = self._get('/api/block/image/rbd/img1') | |
562 | self._validate_snapshot_list(img['snapshots'], 'snap6', is_protected=True) | |
563 | ||
564 | self.update_snapshot('rbd', 'img1', 'snap6', 'snap5', False) | |
565 | self.assertStatus(200) | |
566 | img = self._get('/api/block/image/rbd/img1') | |
567 | self._validate_snapshot_list(img['snapshots'], 'snap5', is_protected=False) | |
568 | ||
569 | self.remove_snapshot('rbd', 'img1', 'snap5') | |
570 | self.assertStatus(204) | |
571 | ||
572 | def test_snapshot_rollback(self): | |
573 | self.create_image('rbd', 'rollback_img', 2**30, | |
574 | features=["layering", "exclusive-lock", "fast-diff", | |
575 | "object-map"]) | |
576 | self.assertStatus(201) | |
577 | self.create_snapshot('rbd', 'rollback_img', 'snap1') | |
578 | self.assertStatus(201) | |
579 | ||
580 | img = self._get('/api/block/image/rbd/rollback_img') | |
581 | self.assertStatus(200) | |
582 | self.assertEqual(img['disk_usage'], 0) | |
583 | ||
584 | self._rbd_cmd(['bench', '--io-type', 'write', '--io-total', '5M', | |
585 | 'rbd/rollback_img']) | |
586 | ||
587 | img = self._get('/api/block/image/rbd/rollback_img') | |
588 | self.assertStatus(200) | |
589 | self.assertGreater(img['disk_usage'], 0) | |
590 | ||
591 | self._task_post('/api/block/image/rbd/rollback_img/snap/snap1/rollback') | |
592 | self.assertStatus([201, 200]) | |
593 | ||
594 | img = self._get('/api/block/image/rbd/rollback_img') | |
595 | self.assertStatus(200) | |
596 | self.assertEqual(img['disk_usage'], 0) | |
597 | ||
598 | self.remove_snapshot('rbd', 'rollback_img', 'snap1') | |
599 | self.assertStatus(204) | |
600 | self.remove_image('rbd', 'rollback_img') | |
601 | self.assertStatus(204) | |
602 | ||
603 | def test_clone(self): | |
604 | self.create_image('rbd', 'cimg', 2**30, features=["layering"]) | |
605 | self.assertStatus(201) | |
606 | self.create_snapshot('rbd', 'cimg', 'snap1') | |
607 | self.assertStatus(201) | |
608 | self.update_snapshot('rbd', 'cimg', 'snap1', None, True) | |
609 | self.assertStatus(200) | |
610 | self.clone_image('rbd', 'cimg', 'snap1', 'rbd', 'cimg-clone', | |
611 | features=["layering", "exclusive-lock", "fast-diff", | |
612 | "object-map"]) | |
613 | self.assertStatus([200, 201]) | |
614 | ||
615 | img = self._get('/api/block/image/rbd/cimg-clone') | |
616 | self.assertStatus(200) | |
617 | self._validate_image(img, features_name=['exclusive-lock', | |
618 | 'fast-diff', 'layering', | |
619 | 'object-map'], | |
620 | parent={'pool_name': 'rbd', 'image_name': 'cimg', | |
621 | 'snap_name': 'snap1'}) | |
622 | ||
623 | res = self.remove_image('rbd', 'cimg') | |
624 | self.assertStatus(400) | |
625 | self.assertIn('code', res) | |
626 | self.assertEqual(res['code'], '39') | |
627 | ||
628 | self.remove_image('rbd', 'cimg-clone') | |
629 | self.assertStatus(204) | |
630 | self.update_snapshot('rbd', 'cimg', 'snap1', None, False) | |
631 | self.assertStatus(200) | |
632 | self.remove_snapshot('rbd', 'cimg', 'snap1') | |
633 | self.assertStatus(204) | |
634 | self.remove_image('rbd', 'cimg') | |
635 | self.assertStatus(204) | |
636 | ||
637 | def test_copy(self): | |
638 | self.create_image('rbd', 'coimg', 2**30, | |
639 | features=["layering", "exclusive-lock", "fast-diff", | |
640 | "object-map"]) | |
641 | self.assertStatus(201) | |
642 | ||
643 | self._rbd_cmd(['bench', '--io-type', 'write', '--io-total', '5M', | |
644 | 'rbd/coimg']) | |
645 | ||
646 | self.copy_image('rbd', 'coimg', 'rbd_iscsi', 'coimg-copy', | |
647 | features=["layering", "fast-diff", "exclusive-lock", | |
648 | "object-map"]) | |
649 | self.assertStatus([200, 201]) | |
650 | ||
651 | img = self._get('/api/block/image/rbd/coimg') | |
652 | self.assertStatus(200) | |
653 | self._validate_image(img, features_name=['layering', 'exclusive-lock', | |
654 | 'fast-diff', 'object-map']) | |
655 | ||
656 | img_copy = self._get('/api/block/image/rbd_iscsi/coimg-copy') | |
657 | self._validate_image(img_copy, features_name=['exclusive-lock', | |
658 | 'fast-diff', 'layering', | |
659 | 'object-map'], | |
660 | disk_usage=img['disk_usage']) | |
661 | ||
662 | self.remove_image('rbd', 'coimg') | |
663 | self.assertStatus(204) | |
664 | self.remove_image('rbd_iscsi', 'coimg-copy') | |
665 | self.assertStatus(204) | |
666 | ||
667 | def test_flatten(self): | |
668 | self.create_snapshot('rbd', 'img1', 'snapf') | |
669 | self.update_snapshot('rbd', 'img1', 'snapf', None, True) | |
670 | self.clone_image('rbd', 'img1', 'snapf', 'rbd_iscsi', 'img1_snapf_clone') | |
671 | ||
672 | img = self._get('/api/block/image/rbd_iscsi/img1_snapf_clone') | |
673 | self.assertStatus(200) | |
674 | self.assertIsNotNone(img['parent']) | |
675 | ||
676 | self.flatten_image('rbd_iscsi', 'img1_snapf_clone') | |
677 | self.assertStatus([200, 201]) | |
678 | ||
679 | img = self._get('/api/block/image/rbd_iscsi/img1_snapf_clone') | |
680 | self.assertStatus(200) | |
681 | self.assertIsNone(img['parent']) | |
682 | ||
683 | self.update_snapshot('rbd', 'img1', 'snapf', None, False) | |
684 | self.remove_snapshot('rbd', 'img1', 'snapf') | |
685 | self.assertStatus(204) | |
686 | ||
687 | self.remove_image('rbd_iscsi', 'img1_snapf_clone') | |
688 | self.assertStatus(204) | |
689 | ||
690 | def test_default_features(self): | |
691 | default_features = self._get('/api/block/image/default_features') | |
692 | self.assertEqual(default_features, ['deep-flatten', 'exclusive-lock', | |
693 | 'fast-diff', 'layering', | |
694 | 'object-map']) | |
695 | ||
696 | def test_image_with_special_name(self): | |
697 | rbd_name = 'test/rbd' | |
698 | rbd_name_encoded = 'test%2Frbd' | |
699 | ||
700 | self.create_image('rbd', rbd_name, 10240) | |
701 | self.assertStatus(201) | |
702 | ||
703 | img = self._get("/api/block/image/rbd/" + rbd_name_encoded) | |
704 | self.assertStatus(200) | |
705 | ||
706 | self._validate_image(img, name=rbd_name, size=10240, | |
707 | num_objs=1, obj_size=4194304, | |
708 | features_name=['deep-flatten', | |
709 | 'exclusive-lock', | |
710 | 'fast-diff', 'layering', | |
711 | 'object-map']) | |
712 | ||
713 | self.remove_image('rbd', rbd_name_encoded) | |
714 | ||
715 | def test_move_image_to_trash(self): | |
716 | id = self.create_image_in_trash('rbd', 'test_rbd') | |
717 | self.assertStatus(200) | |
718 | ||
719 | self._get('/api/block/image/rbd/test_rbd') | |
720 | self.assertStatus(404) | |
721 | ||
722 | time.sleep(1) | |
723 | ||
724 | image = self.get_trash('rbd', id) | |
725 | self.assertIsNotNone(image) | |
726 | ||
727 | self.remove_trash('rbd', id, 'test_rbd') | |
728 | ||
729 | def test_list_trash(self): | |
730 | id = self.create_image_in_trash('rbd', 'test_rbd', 0) | |
731 | data = self._get('/api/block/image/trash/?pool_name={}'.format('rbd')) | |
732 | self.assertStatus(200) | |
733 | self.assertIsInstance(data, list) | |
734 | self.assertIsNotNone(data) | |
735 | ||
736 | self.remove_trash('rbd', id, 'test_rbd') | |
737 | self.assertStatus(204) | |
738 | ||
739 | def test_restore_trash(self): | |
740 | id = self.create_image_in_trash('rbd', 'test_rbd') | |
741 | ||
742 | self._task_post('/api/block/image/trash/{}/{}/restore'.format('rbd', id), {'new_image_name': 'test_rbd'}) | |
743 | ||
744 | self._get('/api/block/image/rbd/test_rbd') | |
745 | self.assertStatus(200) | |
746 | ||
747 | image = self.get_trash('rbd', id) | |
748 | self.assertIsNone(image) | |
749 | ||
750 | self.remove_image('rbd', 'test_rbd') | |
751 | ||
752 | def test_remove_expired_trash(self): | |
753 | id = self.create_image_in_trash('rbd', 'test_rbd', 0) | |
754 | self.remove_trash('rbd', id, 'test_rbd', False) | |
755 | self.assertStatus(204) | |
756 | ||
757 | image = self.get_trash('rbd', id) | |
758 | self.assertIsNone(image) | |
759 | ||
760 | def test_remove_not_expired_trash(self): | |
761 | id = self.create_image_in_trash('rbd', 'test_rbd', 9999) | |
762 | self.remove_trash('rbd', id, 'test_rbd', False) | |
763 | self.assertStatus(400) | |
764 | ||
765 | time.sleep(1) | |
766 | ||
767 | image = self.get_trash('rbd', id) | |
768 | self.assertIsNotNone(image) | |
769 | ||
770 | self.remove_trash('rbd', id, 'test_rbd', True) | |
771 | ||
772 | def test_remove_not_expired_trash_with_force(self): | |
773 | id = self.create_image_in_trash('rbd', 'test_rbd', 9999) | |
774 | self.remove_trash('rbd', id, 'test_rbd', True) | |
775 | self.assertStatus(204) | |
776 | ||
777 | image = self.get_trash('rbd', id) | |
778 | self.assertIsNone(image) | |
779 | ||
780 | def test_purge_trash(self): | |
781 | id_expired = self.create_image_in_trash('rbd', 'test_rbd_expired', 0) | |
782 | id_not_expired = self.create_image_in_trash('rbd', 'test_rbd', 9999) | |
783 | ||
784 | time.sleep(1) | |
785 | ||
786 | self._task_post('/api/block/image/trash/purge?pool_name={}'.format('rbd')) | |
787 | self.assertStatus(200) | |
788 | ||
789 | time.sleep(1) | |
790 | ||
791 | trash_not_expired = self.get_trash('rbd', id_not_expired) | |
792 | self.assertIsNotNone(trash_not_expired) | |
793 | ||
794 | trash_expired = self.get_trash('rbd', id_expired) | |
795 | self.assertIsNone(trash_expired) |