1 # -*- coding: utf-8 -*-
2 # pylint: disable=dangerous-default-value,too-many-public-methods
5 from datetime
import datetime
6 from unittest
.mock
import MagicMock
11 import unittest
.mock
as mock
14 from ..services
.rbd
import RbdConfiguration
, RBDSchedulerInterval
, RbdService
, \
15 get_image_spec
, parse_image_spec
18 class ImageNotFoundStub(Exception):
19 def __init__(self
, message
, errno
=None):
20 super(ImageNotFoundStub
, self
).__init
__(
21 'RBD image not found (%s)' % message
, errno
)
24 class RbdServiceTest(unittest
.TestCase
):
27 # pylint: disable=protected-access
28 RbdService
._rbd
_inst
= mock
.Mock()
29 self
.rbd_inst_mock
= RbdService
._rbd
_inst
31 def test_compose_image_spec(self
):
32 self
.assertEqual(get_image_spec('mypool', 'myns', 'myimage'), 'mypool/myns/myimage')
33 self
.assertEqual(get_image_spec('mypool', None, 'myimage'), 'mypool/myimage')
35 def test_parse_image_spec(self
):
36 self
.assertEqual(parse_image_spec('mypool/myns/myimage'), ('mypool', 'myns', 'myimage'))
37 self
.assertEqual(parse_image_spec('mypool/myimage'), ('mypool', None, 'myimage'))
39 @mock.patch('dashboard.services.rbd.RbdConfiguration._rbd.config_list')
40 @mock.patch('dashboard.mgr.get')
41 @mock.patch('dashboard.services.ceph_service.CephService.get_pool_list')
42 def test_pool_rbd_configuration_with_different_pg_states(self
, get_pool_list
, get
, config_list
):
43 get_pool_list
.return_value
= [{
44 'pool_name': 'good-pool',
47 'pool_name': 'bad-pool',
52 '1': {'active+clean': 32},
53 '2': {'creating+incomplete': 32},
56 config_list
.return_value
= [1, 2, 3]
57 config
= RbdConfiguration('bad-pool')
58 self
.assertEqual(config
.list(), [])
59 config
= RbdConfiguration('good-pool')
60 self
.assertEqual(config
.list(), [1, 2, 3])
62 def test_rbd_image_stat_removing(self
):
63 time
= datetime
.utcnow()
64 self
.rbd_inst_mock
.trash_get
.return_value
= {
68 'deletion_time': time
,
69 'deferment_end_time': time
72 ioctx_mock
= MagicMock()
74 # pylint: disable=protected-access
75 rbd
= RbdService
._rbd
_image
_stat
_removing
(ioctx_mock
, 'test_pool', '', '3c1a5ee60a88')
76 self
.assertEqual(rbd
, {
78 'unique_id': 'test_pool/3c1a5ee60a88',
81 'deletion_time': '{}Z'.format(time
.isoformat()),
82 'deferment_end_time': '{}Z'.format(time
.isoformat()),
83 'pool_name': 'test_pool',
87 @mock.patch('dashboard.services.rbd.rbd.ImageNotFound', new_callable
=lambda: ImageNotFoundStub
)
88 def test_rbd_image_stat_filter_source_user(self
, _
):
89 self
.rbd_inst_mock
.trash_get
.return_value
= {
95 ioctx_mock
= MagicMock()
96 with self
.assertRaises(ImageNotFoundStub
) as ctx
:
97 # pylint: disable=protected-access
98 RbdService
._rbd
_image
_stat
_removing
(ioctx_mock
, 'test_pool', '', '3c1a5ee60a88')
99 self
.assertIn('No image test_pool/3c1a5ee60a88 in status `REMOVING` found.',
102 @mock.patch('dashboard.services.rbd.rbd.ImageNotFound', new_callable
=lambda: ImageNotFoundStub
)
103 @mock.patch('dashboard.services.rbd.RbdService._pool_namespaces')
104 @mock.patch('dashboard.services.rbd.RbdService._rbd_image_stat_removing')
105 @mock.patch('dashboard.services.rbd.RbdService._rbd_image_stat')
106 @mock.patch('dashboard.services.rbd.RbdService._rbd_image_refs')
107 def test_rbd_pool_list(self
, rbd_image_ref_mock
, rbd_image_stat_mock
,
108 rbd_image_stat_removing_mock
, pool_namespaces
, _
):
109 time
= datetime
.utcnow()
111 ioctx_mock
= MagicMock()
112 mgr
.rados
= MagicMock()
113 mgr
.rados
.open_ioctx
.return_value
= ioctx_mock
115 self
.rbd_inst_mock
.namespace_list
.return_value
= []
116 rbd_image_ref_mock
.return_value
= [{'name': 'test_rbd', 'id': '3c1a5ee60a88'}]
117 pool_namespaces
.return_value
= ['']
119 rbd_image_stat_mock
.side_effect
= mock
.Mock(side_effect
=ImageNotFoundStub(
120 'RBD image not found test_pool/3c1a5ee60a88'))
122 rbd_image_stat_removing_mock
.return_value
= {
123 'id': '3c1a5ee60a88',
124 'unique_id': 'test_pool/3c1a5ee60a88',
126 'source': 'REMOVING',
127 'deletion_time': '{}Z'.format(time
.isoformat()),
128 'deferment_end_time': '{}Z'.format(time
.isoformat()),
129 'pool_name': 'test_pool',
133 # test with limit 0, it should return a list of pools with an empty list, but
134 rbd_pool_list
= RbdService
.rbd_pool_list(['test_pool'], offset
=0, limit
=0)
135 self
.assertEqual(rbd_pool_list
, ([], 1))
137 self
.rbd_inst_mock
.namespace_list
.return_value
= []
139 rbd_pool_list
= RbdService
.rbd_pool_list(['test_pool'], offset
=0, limit
=5)
140 self
.assertEqual(rbd_pool_list
, ([{
141 'id': '3c1a5ee60a88',
142 'unique_id': 'test_pool/3c1a5ee60a88',
144 'source': 'REMOVING',
145 'deletion_time': '{}Z'.format(time
.isoformat()),
146 'deferment_end_time': '{}Z'.format(time
.isoformat()),
147 'pool_name': 'test_pool',
151 def test_valid_interval(self
):
162 for interval
, error
in test_cases
:
164 with self
.assertRaises(ValueError):
165 RBDSchedulerInterval(interval
)
167 self
.assertEqual(str(RBDSchedulerInterval(interval
)), interval
)
169 def test_rbd_image_refs_cache(self
):
170 ioctx_mock
= MagicMock()
171 mgr
.rados
= MagicMock()
172 mgr
.rados
.open_ioctx
.return_value
= ioctx_mock
173 images
= [{'image': str(i
), 'id': str(i
)} for i
in range(10)]
175 self
.rbd_inst_mock
.list2
.return_value
= images
[i
*2:(i
*2)+2]
176 ioctx_mock
= MagicMock()
177 # pylint: disable=protected-access
178 res
= RbdService
._rbd
_image
_refs
(ioctx_mock
, str(i
))
179 self
.assertEqual(res
, images
[i
*2:(i
*2)+2])