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