Coverage for manila/tests/share/drivers/hpe/test_hpe_3par_driver.py: 99%
329 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 2015 Hewlett Packard Enterprise Development LP
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.
15from copy import deepcopy
16import sys
17from unittest import mock
19import ddt
20if 'hpe3parclient' not in sys.modules: 20 ↛ 23line 20 didn't jump to line 23 because the condition on line 20 was always true
21 sys.modules['hpe3parclient'] = mock.Mock()
23from manila import exception
24from manila.share.drivers.hpe import hpe_3par_driver as hpe3pardriver
25from manila.share.drivers.hpe import hpe_3par_mediator as hpe3parmediator
26from manila import test
27from manila.tests.share.drivers.hpe import test_hpe_3par_constants as constants
30@ddt.ddt
31class HPE3ParDriverFPGTestCase(test.TestCase):
33 @ddt.data((-1, 4),
34 (0, 5),
35 (0, -1))
36 @ddt.unpack
37 def test_FPG_init_args_failure(self, min_ip, max_ip):
38 self.assertRaises(exception.HPE3ParInvalid,
39 hpe3pardriver.FPG, min_ip, max_ip)
41 @ddt.data(('invalid_ip_fpg, 10.256.0.1', 0, 4),
42 (None, 0, 4),
43 (' ', 0, 4),
44 ('', 0, 4),
45 ('max_ip_fpg, 10.0.0.1, 10.0.0.2, 10.0.0.3, 10.0.0.4, 10.0.0.5',
46 0, 4),
47 ('min_1_ip_fpg', 1, 4))
48 @ddt.unpack
49 def test_FPG_type_failures(self, value, min_ip, max_ip):
50 fpg_type_obj = hpe3pardriver.FPG(min_ip=min_ip, max_ip=max_ip)
51 self.assertRaises(exception.HPE3ParInvalid, fpg_type_obj, value)
53 @ddt.data(('samplefpg, 10.0.0.1', {'samplefpg': ['10.0.0.1']}),
54 ('samplefpg', {'samplefpg': []}),
55 ('samplefpg, 10.0.0.1, 10.0.0.2',
56 {'samplefpg': ['10.0.0.1', '10.0.0.2']}))
57 @ddt.unpack
58 def test_FPG_type_success(self, value, expected_fpg):
59 fpg_type_obj = hpe3pardriver.FPG()
60 fpg = fpg_type_obj(value)
61 self.assertEqual(expected_fpg, fpg)
64@ddt.ddt
65class HPE3ParDriverTestCase(test.TestCase):
67 def setUp(self):
68 super(HPE3ParDriverTestCase, self).setUp()
70 # Create a mock configuration with attributes and a safe_get()
71 self.conf = mock.Mock()
72 self.conf.driver_handles_share_servers = True
73 self.conf.hpe3par_debug = constants.EXPECTED_HPE_DEBUG
74 self.conf.hpe3par_username = constants.USERNAME
75 self.conf.hpe3par_password = constants.PASSWORD
76 self.conf.hpe3par_api_url = constants.API_URL
77 self.conf.hpe3par_san_login = constants.SAN_LOGIN
78 self.conf.hpe3par_san_password = constants.SAN_PASSWORD
79 self.conf.hpe3par_san_ip = constants.EXPECTED_IP_1234
80 self.conf.hpe3par_fpg = constants.EXPECTED_FPG_CONF
81 self.conf.hpe3par_san_ssh_port = constants.PORT
82 self.conf.ssh_conn_timeout = constants.TIMEOUT
83 self.conf.hpe3par_fstore_per_share = False
84 self.conf.hpe3par_require_cifs_ip = False
85 self.conf.hpe3par_cifs_admin_access_username = constants.USERNAME,
86 self.conf.hpe3par_cifs_admin_access_password = constants.PASSWORD,
87 self.conf.hpe3par_cifs_admin_access_domain = (
88 constants.EXPECTED_CIFS_DOMAIN),
89 self.conf.hpe3par_share_mount_path = constants.EXPECTED_MOUNT_PATH,
90 self.conf.my_ip = constants.EXPECTED_IP_1234
91 self.conf.network_config_group = 'test_network_config_group'
92 self.conf.admin_network_config_group = (
93 'test_admin_network_config_group')
94 self.conf.filter_function = None
95 self.conf.goodness_function = None
97 def safe_get(attr):
98 try:
99 return self.conf.__getattribute__(attr)
100 except AttributeError:
101 return None
102 self.conf.safe_get = safe_get
104 self.real_hpe_3par_mediator = hpe3parmediator.HPE3ParMediator
105 self.mock_object(hpe3parmediator, 'HPE3ParMediator')
106 self.mock_mediator_constructor = hpe3parmediator.HPE3ParMediator
107 self.mock_mediator = self.mock_mediator_constructor()
108 # restore needed static methods
109 self.mock_mediator.ensure_supported_protocol = (
110 self.real_hpe_3par_mediator.ensure_supported_protocol)
111 self.mock_mediator.build_export_locations = (
112 self.real_hpe_3par_mediator.build_export_locations)
114 self.driver = hpe3pardriver.HPE3ParShareDriver(
115 configuration=self.conf)
117 def test_driver_setup_success(self,
118 get_vfs_ret_val=constants.EXPECTED_GET_VFS):
119 """Driver do_setup without any errors."""
121 self.mock_mediator.get_vfs.return_value = get_vfs_ret_val
123 self.driver.do_setup(None)
124 conf = self.conf
125 self.mock_mediator_constructor.assert_has_calls([
126 mock.call(hpe3par_san_ssh_port=conf.hpe3par_san_ssh_port,
127 hpe3par_san_password=conf.hpe3par_san_password,
128 hpe3par_username=conf.hpe3par_username,
129 hpe3par_san_login=conf.hpe3par_san_login,
130 hpe3par_debug=conf.hpe3par_debug,
131 hpe3par_api_url=conf.hpe3par_api_url,
132 hpe3par_password=conf.hpe3par_password,
133 hpe3par_san_ip=conf.hpe3par_san_ip,
134 hpe3par_fstore_per_share=conf.hpe3par_fstore_per_share,
135 hpe3par_require_cifs_ip=conf.hpe3par_require_cifs_ip,
136 hpe3par_cifs_admin_access_username=(
137 conf.hpe3par_cifs_admin_access_username),
138 hpe3par_cifs_admin_access_password=(
139 conf.hpe3par_cifs_admin_access_password),
140 hpe3par_cifs_admin_access_domain=(
141 conf.hpe3par_cifs_admin_access_domain),
142 hpe3par_share_mount_path=conf.hpe3par_share_mount_path,
143 my_ip=self.conf.my_ip,
144 ssh_conn_timeout=conf.ssh_conn_timeout)])
146 self.mock_mediator.assert_has_calls([
147 mock.call.do_setup(),
148 mock.call.get_vfs(constants.EXPECTED_FPG)])
150 def test_driver_setup_dhss_success(self):
151 """Driver do_setup without any errors with dhss=True."""
153 self.test_driver_setup_success()
154 self.assertEqual(constants.EXPECTED_FPG_MAP, self.driver.fpgs)
156 def test_driver_setup_no_dhss_success(self):
157 """Driver do_setup without any errors with dhss=False."""
159 self.conf.driver_handles_share_servers = False
160 self.test_driver_setup_success()
161 self.assertEqual(constants.EXPECTED_FPG_MAP, self.driver.fpgs)
163 def test_driver_setup_no_dhss_multi_getvfs_success(self):
164 """Driver do_setup when dhss=False, getvfs returns multiple IPs."""
166 self.conf.driver_handles_share_servers = False
167 self.test_driver_setup_success(
168 get_vfs_ret_val=constants.EXPECTED_GET_VFS_MULTIPLES)
169 self.assertEqual(constants.EXPECTED_FPG_MAP,
170 self.driver.fpgs)
172 def test_driver_setup_success_no_dhss_no_conf_ss_ip(self):
173 """test driver's do_setup()
175 Driver do_setup with dhss=False, share server ip not set in config file
176 but discoverable at 3par array
177 """
179 self.conf.driver_handles_share_servers = False
180 # ss ip not provided in conf
181 original_fpg = deepcopy(self.conf.hpe3par_fpg)
182 self.conf.hpe3par_fpg[0][constants.EXPECTED_FPG] = []
184 self.test_driver_setup_success()
186 self.assertEqual(constants.EXPECTED_FPG_MAP, self.driver.fpgs)
187 constants.EXPECTED_FPG_CONF = original_fpg
189 def test_driver_setup_failure_no_dhss_no_conf_ss_ip(self):
190 """Configured IP address is required for dhss=False."""
192 self.conf.driver_handles_share_servers = False
193 # ss ip not provided in conf
194 fpg_without_ss_ip = deepcopy(self.conf.hpe3par_fpg)
195 self.conf.hpe3par_fpg[0][constants.EXPECTED_FPG] = []
196 # ss ip not configured on array
197 vfs_without_ss_ip = deepcopy(constants.EXPECTED_GET_VFS)
198 vfs_without_ss_ip['vfsip']['address'] = []
199 self.mock_mediator.get_vfs.return_value = vfs_without_ss_ip
201 self.assertRaises(exception.HPE3ParInvalid,
202 self.driver.do_setup, None)
203 constants.EXPECTED_FPG_CONF = fpg_without_ss_ip
205 def test_driver_setup_mediator_error(self):
206 """Driver do_setup when the mediator setup fails."""
208 self.mock_mediator.do_setup.side_effect = (
209 exception.ShareBackendException('fail'))
211 self.assertRaises(exception.ShareBackendException,
212 self.driver.do_setup, None)
214 conf = self.conf
215 self.mock_mediator_constructor.assert_has_calls([
216 mock.call(hpe3par_san_ssh_port=conf.hpe3par_san_ssh_port,
217 hpe3par_san_password=conf.hpe3par_san_password,
218 hpe3par_username=conf.hpe3par_username,
219 hpe3par_san_login=conf.hpe3par_san_login,
220 hpe3par_debug=conf.hpe3par_debug,
221 hpe3par_api_url=conf.hpe3par_api_url,
222 hpe3par_password=conf.hpe3par_password,
223 hpe3par_san_ip=conf.hpe3par_san_ip,
224 hpe3par_fstore_per_share=conf.hpe3par_fstore_per_share,
225 hpe3par_require_cifs_ip=conf.hpe3par_require_cifs_ip,
226 hpe3par_cifs_admin_access_username=(
227 conf.hpe3par_cifs_admin_access_username),
228 hpe3par_cifs_admin_access_password=(
229 conf.hpe3par_cifs_admin_access_password),
230 hpe3par_cifs_admin_access_domain=(
231 conf.hpe3par_cifs_admin_access_domain),
232 hpe3par_share_mount_path=conf.hpe3par_share_mount_path,
233 my_ip=self.conf.my_ip,
234 ssh_conn_timeout=conf.ssh_conn_timeout)])
236 self.mock_mediator.assert_has_calls([mock.call.do_setup()])
238 def test_driver_setup_with_vfs_error(self):
239 """Driver do_setup when the get_vfs fails."""
241 self.mock_mediator.get_vfs.side_effect = (
242 exception.ShareBackendException('fail'))
244 self.assertRaises(exception.ShareBackendException,
245 self.driver.do_setup, None)
247 conf = self.conf
248 self.mock_mediator_constructor.assert_has_calls([
249 mock.call(hpe3par_san_ssh_port=conf.hpe3par_san_ssh_port,
250 hpe3par_san_password=conf.hpe3par_san_password,
251 hpe3par_username=conf.hpe3par_username,
252 hpe3par_san_login=conf.hpe3par_san_login,
253 hpe3par_debug=conf.hpe3par_debug,
254 hpe3par_api_url=conf.hpe3par_api_url,
255 hpe3par_password=conf.hpe3par_password,
256 hpe3par_san_ip=conf.hpe3par_san_ip,
257 hpe3par_fstore_per_share=conf.hpe3par_fstore_per_share,
258 hpe3par_require_cifs_ip=conf.hpe3par_require_cifs_ip,
259 hpe3par_cifs_admin_access_username=(
260 conf.hpe3par_cifs_admin_access_username),
261 hpe3par_cifs_admin_access_password=(
262 conf.hpe3par_cifs_admin_access_password),
263 hpe3par_cifs_admin_access_domain=(
264 conf.hpe3par_cifs_admin_access_domain),
265 hpe3par_share_mount_path=conf.hpe3par_share_mount_path,
266 my_ip=self.conf.my_ip,
267 ssh_conn_timeout=conf.ssh_conn_timeout)])
269 self.mock_mediator.assert_has_calls([
270 mock.call.do_setup(),
271 mock.call.get_vfs(constants.EXPECTED_FPG)])
273 def test_driver_setup_conf_ips_validation_fails(self):
274 """Driver do_setup when the _validate_pool_ips fails."""
276 self.conf.driver_handles_share_servers = False
277 vfs_with_ss_ip = deepcopy(constants.EXPECTED_GET_VFS)
278 vfs_with_ss_ip['vfsip']['address'] = ['10.100.100.100']
279 self.mock_mediator.get_vfs.return_value = vfs_with_ss_ip
280 self.assertRaises(exception.HPE3ParInvalid,
281 self.driver.do_setup, None)
283 conf = self.conf
284 self.mock_mediator_constructor.assert_has_calls([
285 mock.call(hpe3par_san_ssh_port=conf.hpe3par_san_ssh_port,
286 hpe3par_san_password=conf.hpe3par_san_password,
287 hpe3par_username=conf.hpe3par_username,
288 hpe3par_san_login=conf.hpe3par_san_login,
289 hpe3par_debug=conf.hpe3par_debug,
290 hpe3par_api_url=conf.hpe3par_api_url,
291 hpe3par_password=conf.hpe3par_password,
292 hpe3par_san_ip=conf.hpe3par_san_ip,
293 hpe3par_fstore_per_share=conf.hpe3par_fstore_per_share,
294 hpe3par_require_cifs_ip=conf.hpe3par_require_cifs_ip,
295 hpe3par_cifs_admin_access_username=(
296 conf.hpe3par_cifs_admin_access_username),
297 hpe3par_cifs_admin_access_password=(
298 conf.hpe3par_cifs_admin_access_password),
299 hpe3par_cifs_admin_access_domain=(
300 conf.hpe3par_cifs_admin_access_domain),
301 hpe3par_share_mount_path=conf.hpe3par_share_mount_path,
302 my_ip=self.conf.my_ip,
303 ssh_conn_timeout=conf.ssh_conn_timeout)])
305 self.mock_mediator.assert_has_calls([
306 mock.call.do_setup(),
307 mock.call.get_vfs(constants.EXPECTED_FPG)])
309 def init_driver(self):
310 """Simple driver setup for re-use with tests that need one."""
312 self.driver._hpe3par = self.mock_mediator
313 self.driver.fpgs = constants.EXPECTED_FPG_MAP
314 self.mock_object(hpe3pardriver, 'share_types')
315 get_extra_specs = hpe3pardriver.share_types.get_extra_specs_from_share
316 get_extra_specs.return_value = constants.EXPECTED_EXTRA_SPECS
318 def test_driver_check_for_setup_error_success(self):
319 """check_for_setup_error when things go well."""
321 # Generally this is always mocked, but here we reference the class.
322 hpe3parmediator.HPE3ParMediator = self.real_hpe_3par_mediator
324 self.mock_object(hpe3pardriver, 'LOG')
325 self.init_driver()
326 self.driver.check_for_setup_error()
327 expected_calls = [
328 mock.call.debug('HPE3ParShareDriver SHA1: %s', mock.ANY),
329 mock.call.debug('HPE3ParMediator SHA1: %s', mock.ANY)
330 ]
331 hpe3pardriver.LOG.assert_has_calls(expected_calls)
333 def test_driver_check_for_setup_error_exception(self):
334 """check_for_setup_error catch and log any exceptions."""
336 # Since HPE3ParMediator is mocked, we'll hit the except/log.
337 self.mock_object(hpe3pardriver, 'LOG')
338 self.init_driver()
339 self.driver.check_for_setup_error()
340 expected_calls = [
341 mock.call.debug('HPE3ParShareDriver SHA1: %s', mock.ANY),
342 mock.call.debug('Source code SHA1 not logged due to: %s', mock.ANY)
343 ]
344 hpe3pardriver.LOG.assert_has_calls(expected_calls)
346 @ddt.data(([constants.SHARE_SERVER], constants.SHARE_SERVER),
347 ([], None),)
348 @ddt.unpack
349 def test_choose_share_server_compatible_with_share(self, share_servers,
350 expected_share_sever):
351 context = None
352 share_server = self.driver.choose_share_server_compatible_with_share(
353 context,
354 share_servers,
355 constants.NFS_SHARE_INFO,
356 None,
357 None)
359 self.assertEqual(expected_share_sever, share_server)
361 def test_choose_share_server_compatible_with_share_with_cg(self):
362 context = None
363 cg_ref = {'id': 'dummy'}
364 self.assertRaises(
365 exception.InvalidRequest,
366 self.driver.choose_share_server_compatible_with_share,
367 context,
368 [constants.SHARE_SERVER],
369 constants.NFS_SHARE_INFO,
370 None,
371 cg_ref)
373 def do_create_share(self, protocol, share_type_id, expected_project_id,
374 expected_share_id, expected_size):
375 """Re-usable code for create share."""
376 context = None
378 share = {
379 'display_name': constants.EXPECTED_SHARE_NAME,
380 'host': constants.EXPECTED_HOST,
381 'project_id': expected_project_id,
382 'id': expected_share_id,
383 'share_proto': protocol,
384 'share_type_id': share_type_id,
385 'size': expected_size,
386 }
387 location = self.driver.create_share(context, share,
388 constants.SHARE_SERVER)
389 return location
391 def do_create_share_from_snapshot(self,
392 protocol,
393 share_type_id,
394 snapshot_instance,
395 expected_share_id,
396 expected_size):
397 """Re-usable code for create share from snapshot."""
398 context = None
399 share = {
400 'project_id': constants.EXPECTED_PROJECT_ID,
401 'display_name': constants.EXPECTED_SHARE_NAME,
402 'host': constants.EXPECTED_HOST,
403 'id': expected_share_id,
404 'share_proto': protocol,
405 'share_type_id': share_type_id,
406 'size': expected_size,
407 }
408 location = self.driver.create_share_from_snapshot(
409 context,
410 share,
411 snapshot_instance,
412 constants.SHARE_SERVER)
413 return location
415 @ddt.data((constants.UNEXPECTED_HOST, exception.InvalidHost),
416 (constants.HOST_WITHOUT_POOL_1, exception.InvalidHost),
417 (constants.HOST_WITHOUT_POOL_2, exception.InvalidHost))
418 @ddt.unpack
419 def test_driver_create_share_fails_get_pool_location(self, host,
420 expected_exception):
421 """get_pool_location fails to extract pool name from host"""
422 self.init_driver()
423 context = None
424 share_server = None
425 share = {
426 'display_name': constants.EXPECTED_SHARE_NAME,
427 'host': host,
428 'project_id': constants.EXPECTED_PROJECT_ID,
429 'id': constants.EXPECTED_SHARE_ID,
430 'share_proto': constants.CIFS,
431 'share_type_id': constants.SHARE_TYPE_ID,
432 'size': constants.EXPECTED_SIZE_2,
433 }
434 self.assertRaises(expected_exception,
435 self.driver.create_share,
436 context, share, share_server)
438 def test_driver_create_cifs_share(self):
439 self.init_driver()
441 expected_location = '\\\\%s\\%s' % (constants.EXPECTED_IP_10203040,
442 constants.EXPECTED_SHARE_NAME)
444 self.mock_mediator.create_share.return_value = (
445 constants.EXPECTED_SHARE_NAME)
447 hpe3parmediator.HPE3ParMediator = self.real_hpe_3par_mediator
449 location = self.do_create_share(constants.CIFS,
450 constants.SHARE_TYPE_ID,
451 constants.EXPECTED_PROJECT_ID,
452 constants.EXPECTED_SHARE_ID,
453 constants.EXPECTED_SIZE_2)
455 self.assertIn(expected_location, location)
456 expected_calls = [mock.call.create_share(
457 constants.EXPECTED_PROJECT_ID,
458 constants.EXPECTED_SHARE_ID,
459 constants.CIFS,
460 constants.EXPECTED_EXTRA_SPECS,
461 constants.EXPECTED_FPG,
462 constants.EXPECTED_VFS,
463 comment=mock.ANY,
464 size=constants.EXPECTED_SIZE_2)]
465 self.mock_mediator.assert_has_calls(expected_calls)
467 def test_driver_create_nfs_share(self):
468 self.init_driver()
470 expected_location = ':'.join((constants.EXPECTED_IP_10203040,
471 constants.EXPECTED_SHARE_PATH))
473 self.mock_mediator.create_share.return_value = (
474 constants.EXPECTED_SHARE_PATH)
475 hpe3parmediator.HPE3ParMediator = self.real_hpe_3par_mediator
477 location = self.do_create_share(constants.NFS,
478 constants.SHARE_TYPE_ID,
479 constants.EXPECTED_PROJECT_ID,
480 constants.EXPECTED_SHARE_ID,
481 constants.EXPECTED_SIZE_1)
483 self.assertIn(expected_location, location)
484 expected_calls = [
485 mock.call.create_share(constants.EXPECTED_PROJECT_ID,
486 constants.EXPECTED_SHARE_ID,
487 constants.NFS,
488 constants.EXPECTED_EXTRA_SPECS,
489 constants.EXPECTED_FPG,
490 constants.EXPECTED_VFS,
491 comment=mock.ANY,
492 size=constants.EXPECTED_SIZE_1)]
494 self.mock_mediator.assert_has_calls(expected_calls)
496 def test_driver_create_cifs_share_from_snapshot(self):
497 self.init_driver()
499 expected_location = '\\\\%s\\%s' % (constants.EXPECTED_IP_10203040,
500 constants.EXPECTED_SHARE_NAME)
502 self.mock_mediator.create_share_from_snapshot.return_value = (
503 constants.EXPECTED_SHARE_NAME)
504 hpe3parmediator.HPE3ParMediator = self.real_hpe_3par_mediator
506 snapshot_instance = constants.SNAPSHOT_INSTANCE.copy()
507 snapshot_instance['protocol'] = constants.CIFS
509 location = self.do_create_share_from_snapshot(
510 constants.CIFS,
511 constants.SHARE_TYPE_ID,
512 snapshot_instance,
513 constants.EXPECTED_SHARE_ID,
514 constants.EXPECTED_SIZE_2)
516 self.assertIn(expected_location, location)
517 expected_calls = [
518 mock.call.create_share_from_snapshot(
519 constants.EXPECTED_SHARE_ID,
520 constants.CIFS,
521 constants.EXPECTED_EXTRA_SPECS,
522 constants.EXPECTED_FSTORE,
523 constants.EXPECTED_SHARE_ID,
524 constants.EXPECTED_SNAP_ID,
525 constants.EXPECTED_FPG,
526 constants.EXPECTED_VFS,
527 [constants.EXPECTED_IP_10203040],
528 comment=mock.ANY,
529 size=constants.EXPECTED_SIZE_2),
530 ]
531 self.mock_mediator.assert_has_calls(expected_calls)
533 def test_driver_create_nfs_share_from_snapshot(self):
534 self.init_driver()
536 expected_location = ':'.join((constants.EXPECTED_IP_10203040,
537 constants.EXPECTED_SHARE_PATH))
539 self.mock_mediator.create_share_from_snapshot.return_value = (
540 constants.EXPECTED_SHARE_PATH)
541 hpe3parmediator.HPE3ParMediator = self.real_hpe_3par_mediator
543 location = self.do_create_share_from_snapshot(
544 constants.NFS,
545 constants.SHARE_TYPE_ID,
546 constants.SNAPSHOT_INSTANCE,
547 constants.EXPECTED_SHARE_ID,
548 constants.EXPECTED_SIZE_1)
550 self.assertIn(expected_location, location)
551 expected_calls = [
552 mock.call.create_share_from_snapshot(
553 constants.EXPECTED_SHARE_ID,
554 constants.NFS,
555 constants.EXPECTED_EXTRA_SPECS,
556 constants.EXPECTED_PROJECT_ID,
557 constants.EXPECTED_SHARE_ID,
558 constants.EXPECTED_SNAP_ID,
559 constants.EXPECTED_FPG,
560 constants.EXPECTED_VFS,
561 [constants.EXPECTED_IP_10203040],
562 comment=mock.ANY,
563 size=constants.EXPECTED_SIZE_1),
564 ]
566 self.mock_mediator.assert_has_calls(expected_calls)
568 def test_driver_delete_share(self):
569 self.init_driver()
571 context = None
572 share_server = None
573 share = {
574 'project_id': constants.EXPECTED_PROJECT_ID,
575 'id': constants.EXPECTED_SHARE_ID,
576 'share_proto': constants.CIFS,
577 'size': constants.EXPECTED_SIZE_1,
578 'host': constants.EXPECTED_HOST
579 }
581 self.driver.delete_share(context, share, share_server)
583 expected_calls = [
584 mock.call.delete_share(constants.EXPECTED_PROJECT_ID,
585 constants.EXPECTED_SHARE_ID,
586 constants.EXPECTED_SIZE_1,
587 constants.CIFS,
588 constants.EXPECTED_FPG,
589 constants.EXPECTED_VFS,
590 constants.EXPECTED_IP_10203040)]
592 self.mock_mediator.assert_has_calls(expected_calls)
594 def test_driver_create_snapshot(self):
595 self.init_driver()
597 context = None
598 share_server = None
599 self.driver.create_snapshot(context,
600 constants.SNAPSHOT_INFO,
601 share_server)
603 expected_calls = [
604 mock.call.create_snapshot(constants.EXPECTED_PROJECT_ID,
605 constants.EXPECTED_SHARE_ID,
606 constants.NFS,
607 constants.EXPECTED_SNAP_ID,
608 constants.EXPECTED_FPG,
609 constants.EXPECTED_VFS)]
610 self.mock_mediator.assert_has_calls(expected_calls)
612 def test_driver_delete_snapshot(self):
613 self.init_driver()
615 context = None
616 share_server = None
617 self.driver.delete_snapshot(context,
618 constants.SNAPSHOT_INFO,
619 share_server)
621 expected_calls = [
622 mock.call.delete_snapshot(constants.EXPECTED_PROJECT_ID,
623 constants.EXPECTED_SHARE_ID,
624 constants.NFS,
625 constants.EXPECTED_SNAP_ID,
626 constants.EXPECTED_FPG,
627 constants.EXPECTED_VFS)
628 ]
629 self.mock_mediator.assert_has_calls(expected_calls)
631 def test_driver_update_access_add_rule(self):
632 self.init_driver()
634 context = None
636 self.driver.update_access(context,
637 constants.NFS_SHARE_INFO,
638 [constants.ACCESS_RULE_NFS],
639 [constants.ADD_RULE_IP],
640 [],
641 constants.SHARE_SERVER)
643 expected_calls = [
644 mock.call.update_access(constants.EXPECTED_PROJECT_ID,
645 constants.EXPECTED_SHARE_ID,
646 constants.NFS,
647 constants.EXPECTED_EXTRA_SPECS,
648 [constants.ACCESS_RULE_NFS],
649 [constants.ADD_RULE_IP],
650 [],
651 constants.EXPECTED_FPG,
652 constants.EXPECTED_VFS)
653 ]
654 self.mock_mediator.assert_has_calls(expected_calls)
656 def test_driver_update_access_delete_rule(self):
657 self.init_driver()
659 context = None
661 self.driver.update_access(context,
662 constants.NFS_SHARE_INFO,
663 [constants.ACCESS_RULE_NFS],
664 [],
665 [constants.DELETE_RULE_IP],
666 constants.SHARE_SERVER)
668 expected_calls = [
669 mock.call.update_access(constants.EXPECTED_PROJECT_ID,
670 constants.EXPECTED_SHARE_ID,
671 constants.NFS,
672 constants.EXPECTED_EXTRA_SPECS,
673 [constants.ACCESS_RULE_NFS],
674 [],
675 [constants.DELETE_RULE_IP],
676 constants.EXPECTED_FPG,
677 constants.EXPECTED_VFS)
678 ]
679 self.mock_mediator.assert_has_calls(expected_calls)
681 def test_driver_extend_share(self):
682 self.init_driver()
684 old_size = constants.NFS_SHARE_INFO['size']
685 new_size = old_size * 2
687 share_server = None
688 self.driver.extend_share(constants.NFS_SHARE_INFO,
689 new_size, share_server)
691 self.mock_mediator.resize_share.assert_called_once_with(
692 constants.EXPECTED_PROJECT_ID,
693 constants.EXPECTED_SHARE_ID,
694 constants.NFS,
695 new_size,
696 old_size,
697 constants.EXPECTED_FPG,
698 constants.EXPECTED_VFS)
700 def test_driver_shrink_share(self):
701 self.init_driver()
703 old_size = constants.NFS_SHARE_INFO['size']
704 new_size = old_size / 2
705 share_server = None
706 self.driver.shrink_share(constants.NFS_SHARE_INFO,
707 new_size, share_server)
709 self.mock_mediator.resize_share.assert_called_once_with(
710 constants.EXPECTED_PROJECT_ID,
711 constants.EXPECTED_SHARE_ID,
712 constants.NFS,
713 new_size,
714 old_size,
715 constants.EXPECTED_FPG,
716 constants.EXPECTED_VFS)
718 def test_driver_get_share_stats_not_ready(self):
719 """Protect against stats update before driver is ready."""
721 self.mock_object(hpe3pardriver, 'LOG')
723 expected_result = {
724 'driver_handles_share_servers': True,
725 'qos': False,
726 'driver_version': self.driver.VERSION,
727 'free_capacity_gb': 0,
728 'max_over_subscription_ratio': None,
729 'reserved_percentage': 0,
730 'reserved_snapshot_percentage': 0,
731 'reserved_share_extend_percentage': 0,
732 'provisioned_capacity_gb': 0,
733 'share_backend_name': 'HPE_3PAR',
734 'snapshot_support': True,
735 'create_share_from_snapshot_support': True,
736 'revert_to_snapshot_support': False,
737 'mount_snapshot_support': False,
738 'share_group_stats': {
739 'consistent_snapshot_support': None,
740 },
741 'storage_protocol': 'NFS_CIFS',
742 'thin_provisioning': True,
743 'total_capacity_gb': 0,
744 'vendor_name': 'HPE',
745 'pools': None,
746 'replication_domain': None,
747 'filter_function': None,
748 'goodness_function': None,
749 'mount_point_name_support': False,
750 'ipv4_support': True,
751 'ipv6_support': False,
752 'max_share_server_size': -1,
753 'max_shares_per_share_server': -1,
754 'security_service_update_support': False,
755 'share_server_multiple_subnet_support': False,
756 'network_allocation_update_support': False,
757 'share_replicas_migration_support': False,
758 'encryption_support': None,
759 }
761 result = self.driver.get_share_stats(refresh=True)
762 self.assertEqual(expected_result, result)
764 expected_calls = [
765 mock.call.info('Skipping capacity and capabilities update. '
766 'Setup has not completed.')
767 ]
768 hpe3pardriver.LOG.assert_has_calls(expected_calls)
770 def test_driver_get_share_stats_no_refresh(self):
771 """Driver does not call mediator when refresh=False."""
773 self.init_driver()
774 self.driver._stats = constants.EXPECTED_STATS
776 result = self.driver.get_share_stats(refresh=False)
778 self.assertEqual(constants.EXPECTED_STATS, result)
779 self.assertEqual([], self.mock_mediator.mock_calls)
781 def test_driver_get_share_stats_with_refresh(self):
782 """Driver adds stats from mediator to expected structure."""
784 self.init_driver()
785 expected_free = constants.EXPECTED_SIZE_1
786 expected_capacity = constants.EXPECTED_SIZE_2
787 expected_version = self.driver.VERSION
789 self.mock_mediator.get_fpg_status.return_value = {
790 'pool_name': constants.EXPECTED_FPG,
791 'total_capacity_gb': expected_capacity,
792 'free_capacity_gb': expected_free,
793 'thin_provisioning': True,
794 'dedupe': False,
795 'hpe3par_flash_cache': False,
796 'hp3par_flash_cache': False,
797 'reserved_percentage': 0,
798 'reserved_snapshot_percentage': 0,
799 'reserved_share_extend_percentage': 0,
800 'provisioned_capacity_gb': expected_capacity
801 }
803 expected_result = {
804 'share_backend_name': 'HPE_3PAR',
805 'vendor_name': 'HPE',
806 'driver_version': expected_version,
807 'storage_protocol': 'NFS_CIFS',
808 'driver_handles_share_servers': True,
809 'total_capacity_gb': 0,
810 'free_capacity_gb': 0,
811 'provisioned_capacity_gb': 0,
812 'reserved_percentage': 0,
813 'reserved_snapshot_percentage': 0,
814 'reserved_share_extend_percentage': 0,
815 'max_over_subscription_ratio': None,
816 'max_share_server_size': -1,
817 'max_shares_per_share_server': -1,
818 'qos': False,
819 'thin_provisioning': True,
820 'pools': [{
821 'pool_name': constants.EXPECTED_FPG,
822 'total_capacity_gb': expected_capacity,
823 'free_capacity_gb': expected_free,
824 'thin_provisioning': True,
825 'dedupe': False,
826 'hpe3par_flash_cache': False,
827 'hp3par_flash_cache': False,
828 'reserved_percentage': 0,
829 'reserved_snapshot_percentage': 0,
830 'reserved_share_extend_percentage': 0,
831 'provisioned_capacity_gb': expected_capacity}],
832 'snapshot_support': True,
833 'create_share_from_snapshot_support': True,
834 'revert_to_snapshot_support': False,
835 'security_service_update_support': False,
836 'share_server_multiple_subnet_support': False,
837 'network_allocation_update_support': False,
838 'mount_snapshot_support': False,
839 'share_replicas_migration_support': False,
840 'encryption_support': None,
841 'share_group_stats': {
842 'consistent_snapshot_support': None,
843 },
844 'replication_domain': None,
845 'filter_function': None,
846 'goodness_function': None,
847 'mount_point_name_support': False,
848 'ipv4_support': True,
849 'ipv6_support': False,
850 }
852 result = self.driver.get_share_stats(refresh=True)
853 self.assertEqual(expected_result, result)
855 expected_calls = [
856 mock.call.get_fpg_status(constants.EXPECTED_FPG)
857 ]
858 self.mock_mediator.assert_has_calls(expected_calls)
859 self.assertTrue(self.mock_mediator.get_fpg_status.called)
861 def test_driver_get_share_stats_premature(self):
862 """Driver init stats before init_driver completed."""
864 expected_version = self.driver.VERSION
866 self.mock_mediator.get_fpg_status.return_value = {'not_called': 1}
868 expected_result = {
869 'qos': False,
870 'driver_handles_share_servers': True,
871 'driver_version': expected_version,
872 'free_capacity_gb': 0,
873 'max_over_subscription_ratio': None,
874 'max_share_server_size': -1,
875 'max_shares_per_share_server': -1,
876 'pools': None,
877 'provisioned_capacity_gb': 0,
878 'reserved_percentage': 0,
879 'reserved_snapshot_percentage': 0,
880 'reserved_share_extend_percentage': 0,
881 'share_backend_name': 'HPE_3PAR',
882 'storage_protocol': 'NFS_CIFS',
883 'thin_provisioning': True,
884 'total_capacity_gb': 0,
885 'vendor_name': 'HPE',
886 'snapshot_support': True,
887 'create_share_from_snapshot_support': True,
888 'revert_to_snapshot_support': False,
889 'security_service_update_support': False,
890 'share_server_multiple_subnet_support': False,
891 'network_allocation_update_support': False,
892 'share_replicas_migration_support': False,
893 'encryption_support': None,
894 'mount_snapshot_support': False,
895 'share_group_stats': {
896 'consistent_snapshot_support': None,
897 },
898 'replication_domain': None,
899 'filter_function': None,
900 'goodness_function': None,
901 'mount_point_name_support': False,
902 'ipv4_support': True,
903 'ipv6_support': False,
904 }
906 result = self.driver.get_share_stats(refresh=True)
907 self.assertEqual(expected_result, result)
908 self.assertFalse(self.mock_mediator.get_fpg_status.called)
910 @ddt.data(('test"dquote', 'test_dquote'),
911 ("test'squote", "test_squote"),
912 ('test-:;,.punc', 'test-:_punc'),
913 ('test with spaces ', 'test with spaces '),
914 ('x' * 300, 'x' * 300))
915 @ddt.unpack
916 def test_build_comment(self, display_name, clean_name):
918 host = 'test-stack1@backend#pool'
919 share = {
920 'host': host,
921 'display_name': display_name
922 }
923 comment = self.driver.build_share_comment(share)
925 cleaned = {
926 'host': host,
927 'clean_name': clean_name
928 }
930 expected = ("OpenStack Manila - host=%(host)s "
931 "orig_name=%(clean_name)s created=" % cleaned)[:254]
933 self.assertLess(len(comment), 255)
934 self.assertTrue(comment.startswith(expected))
936 # Test for some chars that are not allowed.
937 # Don't test with same regex as the code uses.
938 for c in "'\".,;":
939 self.assertNotIn(c, comment)
941 def test_get_network_allocations_number(self):
942 self.assertEqual(1, self.driver.get_network_allocations_number())
944 def test_setup_server(self):
945 """Setup server by creating a new FSIP."""
947 self.init_driver()
949 network_info = [{
950 'network_allocations': [
951 {'ip_address': constants.EXPECTED_IP_1234}],
952 'cidr': '/'.join((constants.EXPECTED_IP_1234,
953 constants.CIDR_PREFIX)),
954 'network_type': constants.EXPECTED_VLAN_TYPE,
955 'segmentation_id': constants.EXPECTED_VLAN_TAG,
956 'server_id': constants.EXPECTED_SERVER_ID,
957 }]
959 expected_result = {
960 'share_server_name': constants.EXPECTED_SERVER_ID,
961 'share_server_id': constants.EXPECTED_SERVER_ID,
962 'ip': constants.EXPECTED_IP_1234,
963 'subnet': constants.EXPECTED_SUBNET,
964 'vlantag': constants.EXPECTED_VLAN_TAG,
965 'fpg': constants.EXPECTED_FPG,
966 'vfs': constants.EXPECTED_VFS,
967 }
968 metadata = {'request_host': constants.EXPECTED_HOST}
969 result = self.driver._setup_server(network_info, metadata)
971 expected_calls = [
972 mock.call.create_fsip(constants.EXPECTED_IP_1234,
973 constants.EXPECTED_SUBNET,
974 constants.EXPECTED_VLAN_TAG,
975 constants.EXPECTED_FPG,
976 constants.EXPECTED_VFS)
977 ]
978 self.mock_mediator.assert_has_calls(expected_calls)
980 self.assertEqual(expected_result, result)
982 def test_setup_server_fails_for_unsupported_network_type(self):
983 """Setup server fails for unsupported network type"""
985 self.init_driver()
987 network_info = [{
988 'network_allocations': [
989 {'ip_address': constants.EXPECTED_IP_1234}],
990 'cidr': '/'.join((constants.EXPECTED_IP_1234,
991 constants.CIDR_PREFIX)),
992 'network_type': constants.EXPECTED_VXLAN_TYPE,
993 'segmentation_id': constants.EXPECTED_VLAN_TAG,
994 'server_id': constants.EXPECTED_SERVER_ID,
995 }]
996 metadata = {'request_host': constants.EXPECTED_HOST}
998 self.assertRaises(exception.NetworkBadConfigurationException,
999 self.driver._setup_server,
1000 network_info, metadata)
1002 def test_setup_server_fails_for_exceed_pool_max_supported_ips(self):
1003 """Setup server fails when the VFS has reached max supported IPs"""
1005 self.init_driver()
1007 network_info = [{
1008 'network_allocations': [
1009 {'ip_address': constants.EXPECTED_IP_1234}],
1010 'cidr': '/'.join((constants.EXPECTED_IP_1234,
1011 constants.CIDR_PREFIX)),
1012 'network_type': constants.EXPECTED_VLAN_TYPE,
1013 'segmentation_id': constants.EXPECTED_VLAN_TAG,
1014 'server_id': constants.EXPECTED_SERVER_ID,
1015 }]
1016 metadata = {'request_host': constants.EXPECTED_HOST}
1018 expected_vfs = self.driver.fpgs[
1019 constants.EXPECTED_FPG][constants.EXPECTED_VFS]
1020 self.driver.fpgs[constants.EXPECTED_FPG][constants.EXPECTED_VFS] = [
1021 '10.0.0.1', '10.0.0.2', '10.0.0.3', '10.0.0.4']
1023 self.assertRaises(exception.Invalid,
1024 self.driver._setup_server,
1025 network_info, metadata)
1026 self.driver.fpgs[constants.EXPECTED_FPG][constants.EXPECTED_VFS
1027 ] = expected_vfs
1029 def test_teardown_server(self):
1030 """Test tear down server"""
1032 self.init_driver()
1034 server_details = {
1035 'ip': constants.EXPECTED_IP_10203040,
1036 'fpg': constants.EXPECTED_FPG,
1037 'vfs': constants.EXPECTED_VFS,
1038 }
1040 self.driver._teardown_server(server_details)
1042 expected_calls = [
1043 mock.call.remove_fsip(constants.EXPECTED_IP_10203040,
1044 constants.EXPECTED_FPG,
1045 constants.EXPECTED_VFS)
1046 ]
1047 self.mock_mediator.assert_has_calls(expected_calls)