]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | # -*- coding: utf-8 -*- |
2 | # pylint: disable=dangerous-default-value,too-many-public-methods | |
9f95a23c TL |
3 | |
4 | import unittest | |
b3b6e05e TL |
5 | from datetime import datetime |
6 | from unittest.mock import MagicMock | |
f67539c2 | 7 | |
e306af50 TL |
8 | try: |
9 | import mock | |
10 | except ImportError: | |
11 | import unittest.mock as mock | |
9f95a23c | 12 | |
b3b6e05e | 13 | from .. import mgr |
2a845540 TL |
14 | from ..services.rbd import RbdConfiguration, RBDSchedulerInterval, RbdService, \ |
15 | get_image_spec, parse_image_spec | |
b3b6e05e TL |
16 | |
17 | ||
18 | class ImageNotFoundStub(Exception): | |
19 | def __init__(self, message, errno=None): | |
20 | super(ImageNotFoundStub, self).__init__( | |
21 | 'RBD image not found (%s)' % message, errno) | |
9f95a23c TL |
22 | |
23 | ||
24 | class RbdServiceTest(unittest.TestCase): | |
25 | ||
2a845540 TL |
26 | def setUp(self): |
27 | # pylint: disable=protected-access | |
28 | RbdService._rbd_inst = mock.Mock() | |
29 | self.rbd_inst_mock = RbdService._rbd_inst | |
30 | ||
9f95a23c TL |
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') | |
34 | ||
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')) | |
e306af50 TL |
38 | |
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', | |
45 | 'pool': 1, | |
46 | }, { | |
47 | 'pool_name': 'bad-pool', | |
48 | 'pool': 2, | |
49 | }] | |
50 | get.return_value = { | |
51 | 'by_pool': { | |
52 | '1': {'active+clean': 32}, | |
53 | '2': {'creating+incomplete': 32}, | |
54 | } | |
55 | } | |
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]) | |
b3b6e05e | 61 | |
2a845540 | 62 | def test_rbd_image_stat_removing(self): |
b3b6e05e | 63 | time = datetime.utcnow() |
2a845540 | 64 | self.rbd_inst_mock.trash_get.return_value = { |
b3b6e05e TL |
65 | 'id': '3c1a5ee60a88', |
66 | 'name': 'test_rbd', | |
67 | 'source': 'REMOVING', | |
68 | 'deletion_time': time, | |
69 | 'deferment_end_time': time | |
70 | } | |
71 | ||
72 | ioctx_mock = MagicMock() | |
73 | ||
74 | # pylint: disable=protected-access | |
75 | rbd = RbdService._rbd_image_stat_removing(ioctx_mock, 'test_pool', '', '3c1a5ee60a88') | |
76 | self.assertEqual(rbd, { | |
77 | 'id': '3c1a5ee60a88', | |
78 | 'unique_id': 'test_pool/3c1a5ee60a88', | |
79 | 'name': 'test_rbd', | |
80 | 'source': 'REMOVING', | |
81 | 'deletion_time': '{}Z'.format(time.isoformat()), | |
82 | 'deferment_end_time': '{}Z'.format(time.isoformat()), | |
83 | 'pool_name': 'test_pool', | |
84 | 'namespace': '' | |
85 | }) | |
86 | ||
87 | @mock.patch('dashboard.services.rbd.rbd.ImageNotFound', new_callable=lambda: ImageNotFoundStub) | |
2a845540 TL |
88 | def test_rbd_image_stat_filter_source_user(self, _): |
89 | self.rbd_inst_mock.trash_get.return_value = { | |
b3b6e05e TL |
90 | 'id': '3c1a5ee60a88', |
91 | 'name': 'test_rbd', | |
92 | 'source': 'USER' | |
93 | } | |
94 | ||
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.', | |
100 | str(ctx.exception)) | |
101 | ||
102 | @mock.patch('dashboard.services.rbd.rbd.ImageNotFound', new_callable=lambda: ImageNotFoundStub) | |
2a845540 | 103 | @mock.patch('dashboard.services.rbd.RbdService._pool_namespaces') |
b3b6e05e TL |
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') | |
2a845540 TL |
107 | def test_rbd_pool_list(self, rbd_image_ref_mock, rbd_image_stat_mock, |
108 | rbd_image_stat_removing_mock, pool_namespaces, _): | |
b3b6e05e TL |
109 | time = datetime.utcnow() |
110 | ||
111 | ioctx_mock = MagicMock() | |
112 | mgr.rados = MagicMock() | |
113 | mgr.rados.open_ioctx.return_value = ioctx_mock | |
114 | ||
2a845540 | 115 | self.rbd_inst_mock.namespace_list.return_value = [] |
b3b6e05e | 116 | rbd_image_ref_mock.return_value = [{'name': 'test_rbd', 'id': '3c1a5ee60a88'}] |
2a845540 | 117 | pool_namespaces.return_value = [''] |
b3b6e05e TL |
118 | |
119 | rbd_image_stat_mock.side_effect = mock.Mock(side_effect=ImageNotFoundStub( | |
120 | 'RBD image not found test_pool/3c1a5ee60a88')) | |
121 | ||
122 | rbd_image_stat_removing_mock.return_value = { | |
123 | 'id': '3c1a5ee60a88', | |
124 | 'unique_id': 'test_pool/3c1a5ee60a88', | |
125 | 'name': 'test_rbd', | |
126 | 'source': 'REMOVING', | |
127 | 'deletion_time': '{}Z'.format(time.isoformat()), | |
128 | 'deferment_end_time': '{}Z'.format(time.isoformat()), | |
129 | 'pool_name': 'test_pool', | |
130 | 'namespace': '' | |
131 | } | |
132 | ||
2a845540 TL |
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)) | |
136 | ||
137 | self.rbd_inst_mock.namespace_list.return_value = [] | |
138 | ||
139 | rbd_pool_list = RbdService.rbd_pool_list(['test_pool'], offset=0, limit=5) | |
140 | self.assertEqual(rbd_pool_list, ([{ | |
b3b6e05e TL |
141 | 'id': '3c1a5ee60a88', |
142 | 'unique_id': 'test_pool/3c1a5ee60a88', | |
143 | 'name': 'test_rbd', | |
144 | 'source': 'REMOVING', | |
145 | 'deletion_time': '{}Z'.format(time.isoformat()), | |
146 | 'deferment_end_time': '{}Z'.format(time.isoformat()), | |
147 | 'pool_name': 'test_pool', | |
148 | 'namespace': '' | |
2a845540 TL |
149 | }], 1)) |
150 | ||
151 | def test_valid_interval(self): | |
152 | test_cases = [ | |
153 | ('15m', False), | |
154 | ('1h', False), | |
155 | ('5d', False), | |
156 | ('m', True), | |
157 | ('d', True), | |
158 | ('1s', True), | |
159 | ('11', True), | |
160 | ('1m1', True), | |
161 | ] | |
162 | for interval, error in test_cases: | |
163 | if error: | |
164 | with self.assertRaises(ValueError): | |
165 | RBDSchedulerInterval(interval) | |
166 | else: | |
167 | self.assertEqual(str(RBDSchedulerInterval(interval)), interval) | |
168 | ||
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)] | |
174 | for i in range(5): | |
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]) |