Coverage for manila/tests/share/drivers/zfssa/test_zfssarest.py: 100%
197 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14"""
15Unit tests for Oracle's ZFSSA REST API.
16"""
17from unittest import mock
19from manila import exception
20from manila.share.drivers.zfssa import restclient
21from manila.share.drivers.zfssa import zfssarest
22from manila import test
23from manila.tests import fake_zfssa
26class ZFSSAApiTestCase(test.TestCase):
27 """Tests ZFSSAApi."""
29 @mock.patch.object(zfssarest, 'factory_restclient')
30 def setUp(self, _restclient):
31 super(ZFSSAApiTestCase, self).setUp()
32 self.host = 'fakehost'
33 self.user = 'fakeuser'
34 self.url = None
35 self.pool = 'fakepool'
36 self.project = 'fakeproject'
37 self.share = 'fakeshare'
38 self.snap = 'fakesnapshot'
39 _restclient.return_value = fake_zfssa.FakeRestClient()
40 self._zfssa = zfssarest.ZFSSAApi()
41 self._zfssa.set_host('fakehost')
43 self.schema = {
44 'property': 'manila_managed',
45 'description': 'Managed by Manila',
46 'type': 'Boolean',
47 }
49 def _create_response(self, status):
50 response = fake_zfssa.FakeResponse(status)
51 return response
53 def test_enable_service(self):
54 self.mock_object(self._zfssa.rclient, 'put')
55 self._zfssa.rclient.put.return_value = self._create_response(
56 restclient.Status.ACCEPTED)
58 self._zfssa.enable_service('nfs')
59 self.assertEqual(1, self._zfssa.rclient.put.call_count)
61 self._zfssa.rclient.put.return_value = self._create_response(
62 restclient.Status.OK)
63 self.assertRaises(exception.ShareBackendException,
64 self._zfssa.enable_service,
65 'nfs')
67 def test_verify_avail_space(self):
68 self.mock_object(self._zfssa, 'verify_project')
69 self.mock_object(self._zfssa, 'get_project_stats')
70 self._zfssa.get_project_stats.return_value = 2000
72 self._zfssa.verify_avail_space(self.pool,
73 self.project,
74 self.share,
75 1000)
76 self.assertEqual(1, self._zfssa.verify_project.call_count)
77 self.assertEqual(1, self._zfssa.get_project_stats.call_count)
78 self._zfssa.verify_project.assert_called_with(self.pool, self.project)
79 self._zfssa.get_project_stats.assert_called_with(self.pool,
80 self.project)
82 self._zfssa.get_project_stats.return_value = 900
83 self.assertRaises(exception.ShareBackendException,
84 self._zfssa.verify_avail_space,
85 self.pool,
86 self.project,
87 self.share,
88 1000)
90 def test_create_project(self):
91 self.mock_object(self._zfssa, 'verify_pool')
92 self.mock_object(self._zfssa.rclient, 'get')
93 self.mock_object(self._zfssa.rclient, 'post')
94 arg = {
95 'name': self.project,
96 'sharesmb': 'off',
97 'sharenfs': 'off',
98 'mountpoint': 'fakemnpt',
99 }
100 self._zfssa.rclient.get.return_value = self._create_response(
101 restclient.Status.NOT_FOUND)
102 self._zfssa.rclient.post.return_value = self._create_response(
103 restclient.Status.CREATED)
105 self._zfssa.create_project(self.pool, self.project, arg)
106 self.assertEqual(1, self._zfssa.rclient.get.call_count)
107 self.assertEqual(1, self._zfssa.rclient.post.call_count)
108 self.assertEqual(1, self._zfssa.verify_pool.call_count)
109 self._zfssa.verify_pool.assert_called_with(self.pool)
111 self._zfssa.rclient.post.return_value = self._create_response(
112 restclient.Status.NOT_FOUND)
113 self.assertRaises(exception.ShareBackendException,
114 self._zfssa.create_project,
115 self.pool,
116 self.project,
117 arg)
119 def test_create_share(self):
120 self.mock_object(self._zfssa, 'verify_avail_space')
121 self.mock_object(self._zfssa.rclient, 'get')
122 self.mock_object(self._zfssa.rclient, 'post')
123 self._zfssa.rclient.get.return_value = self._create_response(
124 restclient.Status.NOT_FOUND)
125 self._zfssa.rclient.post.return_value = self._create_response(
126 restclient.Status.CREATED)
127 arg = {
128 "name": self.share,
129 "quota": 1,
130 }
132 self._zfssa.create_share(self.pool, self.project, arg)
133 self.assertEqual(1, self._zfssa.rclient.get.call_count)
134 self.assertEqual(1, self._zfssa.rclient.post.call_count)
135 self.assertEqual(1, self._zfssa.verify_avail_space.call_count)
136 self._zfssa.verify_avail_space.assert_called_with(self.pool,
137 self.project,
138 arg,
139 arg['quota'])
141 self._zfssa.rclient.post.return_value = self._create_response(
142 restclient.Status.NOT_FOUND)
143 self.assertRaises(exception.ShareBackendException,
144 self._zfssa.create_share,
145 self.pool,
146 self.project,
147 arg)
149 self._zfssa.rclient.get.return_value = self._create_response(
150 restclient.Status.OK)
151 self.assertRaises(exception.ShareBackendException,
152 self._zfssa.create_share,
153 self.pool,
154 self.project,
155 arg)
157 def test_modify_share(self):
158 self.mock_object(self._zfssa.rclient, 'put')
159 self._zfssa.rclient.put.return_value = self._create_response(
160 restclient.Status.ACCEPTED)
161 arg = {"name": "dummyname"}
162 svc = self._zfssa.share_path % (self.pool, self.project, self.share)
164 self._zfssa.modify_share(self.pool, self.project, self.share, arg)
165 self.assertEqual(1, self._zfssa.rclient.put.call_count)
166 self._zfssa.rclient.put.assert_called_with(svc, arg)
168 self._zfssa.rclient.put.return_value = self._create_response(
169 restclient.Status.BAD_REQUEST)
170 self.assertRaises(exception.ShareBackendException,
171 self._zfssa.modify_share,
172 self.pool,
173 self.project,
174 self.share,
175 arg)
177 def test_delete_share(self):
178 self.mock_object(self._zfssa.rclient, 'delete')
179 self._zfssa.rclient.delete.return_value = self._create_response(
180 restclient.Status.NO_CONTENT)
181 svc = self._zfssa.share_path % (self.pool, self.project, self.share)
183 self._zfssa.delete_share(self.pool, self.project, self.share)
184 self.assertEqual(1, self._zfssa.rclient.delete.call_count)
185 self._zfssa.rclient.delete.assert_called_with(svc)
187 def test_create_snapshot(self):
188 self.mock_object(self._zfssa.rclient, 'post')
189 self._zfssa.rclient.post.return_value = self._create_response(
190 restclient.Status.CREATED)
191 arg = {"name": self.snap}
192 svc = self._zfssa.snapshots_path % (self.pool,
193 self.project,
194 self.share)
196 self._zfssa.create_snapshot(self.pool,
197 self.project,
198 self.share,
199 self.snap)
200 self.assertEqual(1, self._zfssa.rclient.post.call_count)
201 self._zfssa.rclient.post.assert_called_with(svc, arg)
203 self._zfssa.rclient.post.return_value = self._create_response(
204 restclient.Status.BAD_REQUEST)
205 self.assertRaises(exception.ShareBackendException,
206 self._zfssa.create_snapshot,
207 self.pool,
208 self.project,
209 self.share,
210 self.snap)
212 def test_delete_snapshot(self):
213 self.mock_object(self._zfssa.rclient, 'delete')
214 self._zfssa.rclient.delete.return_value = self._create_response(
215 restclient.Status.NO_CONTENT)
216 svc = self._zfssa.snapshot_path % (self.pool,
217 self.project,
218 self.share,
219 self.snap)
221 self._zfssa.delete_snapshot(self.pool,
222 self.project,
223 self.share,
224 self.snap)
225 self.assertEqual(1, self._zfssa.rclient.delete.call_count)
226 self._zfssa.rclient.delete.assert_called_with(svc)
228 self._zfssa.rclient.delete.return_value = self._create_response(
229 restclient.Status.BAD_REQUEST)
230 self.assertRaises(exception.ShareBackendException,
231 self._zfssa.delete_snapshot,
232 self.pool,
233 self.project,
234 self.share,
235 self.snap)
237 def test_clone_snapshot(self):
238 self.mock_object(self._zfssa, 'verify_avail_space')
239 self.mock_object(self._zfssa.rclient, 'put')
240 self._zfssa.rclient.put.return_value = self._create_response(
241 restclient.Status.CREATED)
242 snapshot = {
243 "id": self.snap,
244 "share_id": self.share,
245 }
246 clone = {
247 "id": "cloneid",
248 "size": 1,
249 }
250 arg = {
251 "name": "dummyname",
252 "quota": 1,
253 }
255 self._zfssa.clone_snapshot(self.pool,
256 self.project,
257 snapshot,
258 clone,
259 arg)
260 self.assertEqual(1, self._zfssa.rclient.put.call_count)
261 self.assertEqual(1, self._zfssa.verify_avail_space.call_count)
262 self._zfssa.verify_avail_space.assert_called_with(self.pool,
263 self.project,
264 clone['id'],
265 clone['size'])
267 self._zfssa.rclient.put.return_value = self._create_response(
268 restclient.Status.NOT_FOUND)
269 self.assertRaises(exception.ShareBackendException,
270 self._zfssa.clone_snapshot,
271 self.pool,
272 self.project,
273 snapshot,
274 clone,
275 arg)
277 def _create_entry(self, sharenfs, ip):
278 if sharenfs == 'off':
279 sharenfs = 'sec=sys'
280 entry = (',rw=@%s' % ip)
281 if '/' not in ip:
282 entry = entry + '/32'
283 arg = {'sharenfs': sharenfs + entry}
284 return arg
286 def test_allow_access_nfs(self):
287 self.mock_object(self._zfssa, 'get_share')
288 self.mock_object(self._zfssa, 'modify_share')
289 details = {"sharenfs": "off"}
290 access = {
291 "access_type": "nonip",
292 "access_to": "foo",
293 }
295 # invalid access type
296 self.assertRaises(exception.InvalidShareAccess,
297 self._zfssa.allow_access_nfs,
298 self.pool,
299 self.project,
300 self.share,
301 access)
303 # valid entry
304 access.update({"access_type": "ip"})
305 arg = self._create_entry("off", access['access_to'])
306 self._zfssa.get_share.return_value = details
307 self._zfssa.allow_access_nfs(self.pool,
308 self.project,
309 self.share,
310 access)
311 self.assertEqual(1, self._zfssa.get_share.call_count)
312 self.assertEqual(1, self._zfssa.modify_share.call_count)
313 self._zfssa.get_share.assert_called_with(self.pool,
314 self.project,
315 self.share)
316 self._zfssa.modify_share.assert_called_with(self.pool,
317 self.project,
318 self.share,
319 arg)
321 # add another entry
322 access.update({"access_to": "10.0.0.1/24"})
323 arg = self._create_entry("off", access['access_to'])
324 self._zfssa.allow_access_nfs(self.pool,
325 self.project,
326 self.share,
327 access)
328 self.assertEqual(2, self._zfssa.modify_share.call_count)
329 self._zfssa.modify_share.assert_called_with(self.pool,
330 self.project,
331 self.share,
332 arg)
334 # verify modify_share is not called if sharenfs='on'
335 details = {"sharenfs": "on"}
336 self._zfssa.get_share.return_value = details
337 self._zfssa.allow_access_nfs(self.pool,
338 self.project,
339 self.share,
340 access)
341 self.assertEqual(2, self._zfssa.modify_share.call_count)
343 # verify modify_share is not called if ip is already in the list
344 access.update({"access_to": "10.0.0.1/24"})
345 details = self._create_entry("off", access['access_to'])
346 self._zfssa.get_share.return_value = details
347 self._zfssa.allow_access_nfs(self.pool,
348 self.project,
349 self.share,
350 access)
351 self.assertEqual(2, self._zfssa.modify_share.call_count)
353 def test_deny_access_nfs(self):
354 self.mock_object(self._zfssa, 'get_share')
355 self.mock_object(self._zfssa, 'modify_share')
356 data1 = self._create_entry("off", "10.0.0.1")
357 access = {
358 "access_type": "nonip",
359 "access_to": "foo",
360 }
362 # invalid access_type
363 self.assertRaises(exception.InvalidShareAccess,
364 self._zfssa.deny_access_nfs,
365 self.pool,
366 self.project,
367 self.share,
368 access)
370 # valid entry
371 access.update({"access_type": "ip"})
372 self._zfssa.get_share.return_value = data1
373 self._zfssa.deny_access_nfs(self.pool,
374 self.project,
375 self.share,
376 access)
377 self.assertEqual(1, self._zfssa.get_share.call_count)
378 self.assertEqual(0, self._zfssa.modify_share.call_count)
379 self._zfssa.get_share.assert_called_with(self.pool,
380 self.project,
381 self.share)
382 # another valid entry
383 data1 = self._create_entry(data1['sharenfs'], '10.0.0.2/24')
384 data2 = self._create_entry(data1['sharenfs'], access['access_to'])
385 self._zfssa.get_share.return_value = data2
386 self._zfssa.deny_access_nfs(self.pool,
387 self.project,
388 self.share,
389 access)
390 self.assertEqual(2, self._zfssa.get_share.call_count)
391 self.assertEqual(1, self._zfssa.modify_share.call_count)
392 self._zfssa.get_share.assert_called_with(self.pool,
393 self.project,
394 self.share)
395 self._zfssa.modify_share.assert_called_with(self.pool,
396 self.project,
397 self.share,
398 data1)
400 def test_create_schema_negative(self):
401 self.mock_object(self._zfssa.rclient, 'get')
402 self.mock_object(self._zfssa.rclient, 'post')
403 self._zfssa.rclient.post.return_value = self._create_response(
404 restclient.Status.NOT_FOUND)
406 self.assertRaises(exception.ShareBackendException,
407 self._zfssa.create_schema,
408 self.schema)
410 def test_create_schema_property_exists(self):
411 self.mock_object(self._zfssa.rclient, 'get')
412 self.mock_object(self._zfssa.rclient, 'post')
413 self._zfssa.rclient.get.return_value = self._create_response(
414 restclient.Status.OK)
416 self._zfssa.create_schema(self.schema)
418 self.assertEqual(1, self._zfssa.rclient.get.call_count)
419 self.assertEqual(0, self._zfssa.rclient.post.call_count)
421 def test_create_schema(self):
422 self.mock_object(self._zfssa.rclient, 'get')
423 self.mock_object(self._zfssa.rclient, 'post')
424 self._zfssa.rclient.get.return_value = self._create_response(
425 restclient.Status.NOT_FOUND)
426 self._zfssa.rclient.post.return_value = self._create_response(
427 restclient.Status.CREATED)
429 self._zfssa.create_schema(self.schema)
431 self.assertEqual(1, self._zfssa.rclient.get.call_count)
432 self.assertEqual(1, self._zfssa.rclient.post.call_count)