cinder/cinder/tests/unit/volume/drivers/open_e/test_iscsi.py

1462 lines
50 KiB
Python

# Copyright (c) 2020 Open-E, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import re
from unittest import mock
from oslo_utils import units as o_units
from cinder import context
from cinder import exception
from cinder.tests.unit import fake_snapshot
from cinder.tests.unit import fake_volume
from cinder.tests.unit import test
from cinder.volume.drivers.open_e import iscsi
from cinder.volume.drivers.open_e.jovian_common import exception as jexc
from cinder.volume.drivers.open_e.jovian_common import jdss_common as jcom
UUID_1 = '12345678-1234-1234-1234-000000000001'
UUID_2 = '12345678-1234-1234-1234-000000000002'
UUID_3 = '12345678-1234-1234-1234-000000000003'
UUID_4 = '12345678-1234-1234-1234-000000000004'
CONFIG_OK = {
'san_hosts': ['192.168.0.2'],
'san_api_port': 82,
'driver_use_ssl': 'https',
'jovian_rest_send_repeats': 3,
'jovian_recovery_delay': 60,
'jovian_user': 'admin',
'jovian_password': 'password',
'jovian_ignore_tpath': [],
'target_port': 3260,
'jovian_pool': 'Pool-0',
'target_prefix': 'iqn.2020-04.com.open-e.cinder:',
'chap_password_len': 12,
'san_thin_provision': False,
'jovian_block_size': '128K'
}
CONFIG_BLOCK_SIZE = {
'san_hosts': ['192.168.0.2'],
'san_api_port': 82,
'driver_use_ssl': 'https',
'jovian_rest_send_repeats': 3,
'jovian_recovery_delay': 60,
'jovian_user': 'admin',
'jovian_password': 'password',
'jovian_ignore_tpath': [],
'target_port': 3260,
'jovian_pool': 'Pool-0',
'target_prefix': 'iqn.2020-04.com.open-e.cinder:',
'chap_password_len': 12,
'san_thin_provision': False,
'jovian_block_size': '64K'
}
CONFIG_BACKEND_NAME = {
'san_hosts': ['192.168.0.2'],
'san_api_port': 82,
'driver_use_ssl': 'https',
'jovian_rest_send_repeats': 3,
'jovian_recovery_delay': 60,
'jovian_user': 'admin',
'jovian_password': 'password',
'jovian_ignore_tpath': [],
'target_port': 3260,
'jovian_pool': 'Pool-0',
'target_prefix': 'iqn.2020-04.com.open-e.cinder:',
'chap_password_len': 12,
'san_thin_provision': False,
'volume_backend_name': 'JovianDSS',
'reserved_percentage': 10,
'jovian_block_size': '128K'
}
CONFIG_MULTI_HOST = {
'san_hosts': ['192.168.0.2', '192.168.0.3'],
'san_api_port': 82,
'driver_use_ssl': 'https',
'jovian_rest_send_repeats': 3,
'jovian_recovery_delay': 60,
'jovian_user': 'admin',
'jovian_password': 'password',
'jovian_ignore_tpath': [],
'target_port': 3260,
'jovian_pool': 'Pool-0',
'target_prefix': 'iqn.2020-04.com.open-e.cinder:',
'chap_password_len': 12,
'san_thin_provision': False,
'volume_backend_name': 'JovianDSS',
'reserved_percentage': 10,
'jovian_block_size': '128K'
}
SNAPSHOTS_CASCADE_1 = [
{"name": jcom.sname(UUID_1),
"clones": "Pool-0/" + jcom.sname(UUID_1)},
{"name": jcom.sname(UUID_2),
"clones": "Pool-0/" + jcom.sname(UUID_2)},
{"name": jcom.sname(UUID_3),
"clones": "Pool-0/" + jcom.sname(UUID_3)}]
SNAPSHOTS_CASCADE_2 = [
{"name": jcom.sname(UUID_1),
"clones": "Pool-0/" + jcom.sname(UUID_1)},
{"name": jcom.vname(UUID_2),
"clones": "Pool-0/" + jcom.vname(UUID_2)},
{"name": jcom.sname(UUID_3),
"clones": "Pool-0/" + jcom.sname(UUID_3)}]
SNAPSHOTS_CASCADE_3 = [
{"name": jcom.vname(UUID_4),
"clones": "Pool-0/" + jcom.vname(UUID_4)}]
SNAPSHOTS_EMPTY = []
SNAPSHOTS_CLONE = [
{"name": jcom.vname(UUID_1),
"clones": "Pool-0/" + jcom.vname(UUID_1)}]
SNAPSHOTS_GARBAGE = [
{"name": jcom.sname(UUID_1),
"clones": "Pool-0/" + jcom.vname(UUID_2)},
{"name": jcom.sname(UUID_2),
"clones": ""}]
SNAPSHOTS_RECURSIVE_1 = [
{"name": jcom.sname(UUID_1),
"clones": "Pool-0/" + jcom.sname(UUID_1)},
{"name": jcom.sname(UUID_2),
"clones": "Pool-0/" + jcom.hidden(UUID_2)}]
SNAPSHOTS_RECURSIVE_CHAIN_1 = [
{"name": jcom.sname(UUID_3),
"clones": "Pool-0/" + jcom.hidden(UUID_3)}]
SNAPSHOTS_RECURSIVE_CHAIN_2 = [
{"name": jcom.vname(UUID_2),
"clones": "Pool-0/" + jcom.hidden(UUID_2)}]
def get_jdss_exceptions():
out = [jexc.JDSSException(reason="Testing"),
jexc.JDSSRESTException(request="ra request", reason="Testing"),
jexc.JDSSRESTProxyException(host="test_host", reason="Testing"),
jexc.JDSSResourceNotFoundException(res="test_resource"),
jexc.JDSSVolumeNotFoundException(volume="test_volume"),
jexc.JDSSSnapshotNotFoundException(snapshot="test_snapshot"),
jexc.JDSSResourceExistsException(res="test_resource"),
jexc.JDSSSnapshotExistsException(snapshot="test_snapshot"),
jexc.JDSSVolumeExistsException(volume="test_volume"),
jexc.JDSSSnapshotIsBusyException(snapshot="test_snapshot")]
return out
def fake_safe_get(value):
return CONFIG_OK[value]
class TestOpenEJovianDSSDriver(test.TestCase):
def get_driver(self, config):
ctx = context.get_admin_context()
cfg = mock.Mock()
cfg.append_config_values.return_value = None
cfg.safe_get = lambda val: config[val]
jdssd = iscsi.JovianISCSIDriver()
jdssd.configuration = cfg
jdssd.do_setup(ctx)
jdssd.ra = mock.Mock()
return jdssd, ctx
def start_patches(self, patches):
for p in patches:
p.start()
def stop_patches(self, patches):
for p in patches:
p.stop()
def test_get_provider_location(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
host = CONFIG_OK["san_hosts"][0]
port = CONFIG_OK["target_port"]
target_name = CONFIG_OK["target_prefix"] + UUID_1
patches = [mock.patch.object(
jdssd.ra,
"get_active_host",
return_value=host)]
out = '{host}:{port},1 {name} 0'.format(
host=host,
port=port,
name=target_name
)
self.start_patches(patches)
self.assertEqual(out, jdssd._get_provider_location(UUID_1))
self.stop_patches(patches)
def test_create_volume(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
vol.size = 1
host = CONFIG_OK["san_hosts"][0]
port = CONFIG_OK["target_port"]
target_name = CONFIG_OK["target_prefix"] + UUID_1
jdssd.ra.create_lun.return_value = None
jdssd.ra.get_active_host.return_value = host
ret = jdssd.create_volume(vol)
location = '{host}:{port},1 {name} 0'.format(
host=host,
port=port,
name=target_name
)
self.assertEqual(location, ret['provider_location'])
cred_format = (r"CHAP [0-9,a-z,A-Z]{{{name_len}}} "
"[0-9,a-z,A-Z]{{{pass_len}}}").format(
name_len=8,
pass_len=CONFIG_OK['chap_password_len'])
self.assertIsNotNone(re.match(cred_format, ret['provider_auth']))
def test_create_volume_small_block(self):
jdssd, ctx = self.get_driver(CONFIG_BLOCK_SIZE)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
vol.size = 1
host = CONFIG_OK["san_hosts"][0]
port = CONFIG_OK["target_port"]
target_name = CONFIG_OK["target_prefix"] + UUID_1
vname = jcom.vname(UUID_1)
jdssd.ra.create_lun.return_value = None
jdssd.ra.get_active_host.return_value = host
ret = jdssd.create_volume(vol)
jdssd.ra.create_lun.assert_called_once_with(
vname, o_units.Gi, sparse=False, block_size="64K")
location = '{host}:{port},1 {name} 0'.format(
host=host,
port=port,
name=target_name
)
self.assertEqual(location, ret['provider_location'])
cred_format = (r"CHAP [0-9,a-z,A-Z]{{{name_len}}} "
"[0-9,a-z,A-Z]{{{pass_len}}}").format(
name_len=8,
pass_len=CONFIG_OK['chap_password_len'])
self.assertIsNotNone(re.match(cred_format, ret['provider_auth']))
def test_hide_object(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vname = jcom.vname(UUID_1)
jdssd.ra.modify_lun.return_value = None
jdssd._hide_object(vname)
hidden_volume = {"name": jcom.hidden(UUID_1)}
jdssd.ra.modify_lun.assert_called_once_with(vname, hidden_volume)
def test_clean_garbage_snapshots(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
o_vname = jcom.vname(UUID_1)
o_snaps = SNAPSHOTS_GARBAGE.copy()
jdssd.ra.delete_snapshot.return_value = None
jdssd._clean_garbage_snapshots(o_vname, o_snaps)
jdssd.ra.delete_snapshot.assert_called_once_with(
o_vname,
SNAPSHOTS_GARBAGE[1]["name"])
# Test exception handling
for exc in get_jdss_exceptions():
o_snaps = SNAPSHOTS_GARBAGE.copy()
jdssd.ra.delete_snapshot.side_effect = exc
try:
jdssd._clean_garbage_snapshots(o_vname, o_snaps)
except Exception as err:
self.assertIsInstance(err, exception.VolumeBackendAPIException)
def test_cascade_volume_delete_snapshots(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
o_vname = jcom.vname(UUID_1)
# Volume with 3 snapshots and no descendants
# We should delete snapshots and then cal for volume deletion
o_snaps = SNAPSHOTS_CASCADE_1.copy()
jdssd.ra.modify_lun.return_value = None
jdssd.ra.delete_snapshot.return_value = None
jdssd.ra.get_snapshots.side_effect = [
SNAPSHOTS_EMPTY,
SNAPSHOTS_EMPTY,
SNAPSHOTS_EMPTY]
with mock.patch.object(jdssd, "_gc_delete", return_value=None) as gc:
jdssd._cascade_volume_delete(o_vname, o_snaps)
gc.assert_called_once_with(o_vname)
delete_snapshot_expected = [
mock.call(o_vname,
SNAPSHOTS_CASCADE_1[0]["name"],
recursively_children=True,
recursively_dependents=True,
force_umount=True),
mock.call(o_vname,
SNAPSHOTS_CASCADE_1[1]["name"],
recursively_children=True,
recursively_dependents=True,
force_umount=True),
mock.call(o_vname,
SNAPSHOTS_CASCADE_1[2]["name"],
recursively_children=True,
recursively_dependents=True,
force_umount=True)]
jdssd.ra.delete_snapshot.assert_has_calls(delete_snapshot_expected)
def test_cascade_volume_delete_with_clone(self):
# Volume with 2 snapshots and 1 clone
# We should delete snapshots and then cal for volume hiding
jdssd, ctx = self.get_driver(CONFIG_OK)
o_vname = jcom.vname(UUID_1)
o_snaps = SNAPSHOTS_CASCADE_2.copy()
jdssd.ra.modify_lun.return_value = None
jdssd.ra.delete_snapshot.return_value = None
jdssd.ra.get_snapshots.side_effect = [
SNAPSHOTS_EMPTY,
SNAPSHOTS_EMPTY]
fake_gc = mock.Mock()
fake_hide_object = mock.Mock()
gc = mock.patch.object(jdssd, "_gc_delete", new=fake_gc)
gc.start()
hide = mock.patch.object(jdssd, "_hide_object", new=fake_hide_object)
hide.start()
jdssd._cascade_volume_delete(o_vname, o_snaps)
jdssd._hide_object.assert_called_once_with(o_vname)
hide.stop()
jdssd._gc_delete.assert_not_called()
gc.stop()
delete_snapshot_expected = [
mock.call(o_vname,
SNAPSHOTS_CASCADE_2[0]["name"],
recursively_children=True,
recursively_dependents=True,
force_umount=True),
mock.call(o_vname,
SNAPSHOTS_CASCADE_2[2]["name"],
recursively_children=True,
recursively_dependents=True,
force_umount=True)]
jdssd.ra.delete_snapshot.assert_has_calls(delete_snapshot_expected)
def test_cascade_volume_delete_snapshot_clone(self):
# Volume with 3 snapshots and 1 clone of a snapshots
# We should delete childless snapshots
# and then cal for volume deletion
jdssd, ctx = self.get_driver(CONFIG_OK)
o_vname = jcom.vname(UUID_1)
o_snaps = SNAPSHOTS_CASCADE_1.copy()
jdssd.ra.modify_lun.return_value = None
jdssd.ra.delete_snapshot.return_value = None
jdssd.ra.get_snapshots.side_effect = [
SNAPSHOTS_EMPTY,
SNAPSHOTS_CASCADE_3.copy(),
SNAPSHOTS_EMPTY]
get_snapshots = [
mock.call(SNAPSHOTS_CASCADE_1[0]['name']),
mock.call(SNAPSHOTS_CASCADE_1[1]['name']),
mock.call(SNAPSHOTS_CASCADE_1[2]['name'])
]
hide_object_expected = [
mock.call(SNAPSHOTS_CASCADE_1[1]["name"]),
mock.call(o_vname)]
fake_gc = mock.Mock()
fake_hide_object = mock.Mock()
gc = mock.patch.object(jdssd, "_gc_delete", new=fake_gc)
gc.start()
hide = mock.patch.object(jdssd, "_hide_object", new=fake_hide_object)
hide.start()
jdssd._cascade_volume_delete(o_vname, o_snaps)
jdssd._hide_object.assert_has_calls(hide_object_expected)
hide.stop()
jdssd._gc_delete.assert_not_called()
gc.stop()
jdssd.ra.get_snapshots.assert_has_calls(get_snapshots)
delete_snapshot_expected = [
mock.call(o_vname,
SNAPSHOTS_CASCADE_2[0]["name"],
recursively_children=True,
recursively_dependents=True,
force_umount=True),
mock.call(o_vname,
SNAPSHOTS_CASCADE_2[2]["name"],
recursively_children=True,
recursively_dependents=True,
force_umount=True)]
jdssd.ra.delete_snapshot.assert_has_calls(delete_snapshot_expected)
def test_delete_volume_with_snapshots(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
vname = jcom.vname(UUID_1)
jdssd.ra.get_snapshots.side_effect = [SNAPSHOTS_CASCADE_1.copy()]
patches = [mock.patch.object(jdssd, "_cascade_volume_delete"),
mock.patch.object(jdssd, "_gc_delete"),
mock.patch.object(jdssd, "_hide_object")]
self.start_patches(patches)
jdssd.delete_volume(vol, cascade=False)
jdssd._gc_delete.assert_not_called()
jdssd._cascade_volume_delete.assert_not_called()
jdssd._hide_object.assert_called_once_with(vname)
self.stop_patches(patches)
jdssd.ra.get_snapshots.assert_called_once_with(vname)
def test_delete_volume_without_snapshots(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
vname = jcom.vname(UUID_1)
jdssd.ra.get_snapshots.side_effect = [SNAPSHOTS_EMPTY.copy()]
patches = [mock.patch.object(jdssd, "_cascade_volume_delete"),
mock.patch.object(jdssd, "_gc_delete"),
mock.patch.object(jdssd, "_hide_object")]
self.start_patches(patches)
jdssd.delete_volume(vol, cascade=False)
jdssd._gc_delete.assert_called_once_with(vname)
jdssd._cascade_volume_delete.assert_not_called()
jdssd._hide_object.assert_not_called()
self.stop_patches(patches)
jdssd.ra.get_snapshots.assert_called_once_with(vname)
def test_delete_volume_exceptions(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
patches = [mock.patch.object(jdssd, "_cascade_volume_delete"),
mock.patch.object(jdssd, "_gc_delete"),
mock.patch.object(jdssd, "_hide_object")]
self.start_patches(patches)
for exc in get_jdss_exceptions():
jdssd.ra.get_snapshots.side_effect = exc
try:
jdssd.delete_volume(vol, cascade=False)
except Exception as err:
self.assertIsInstance(err, exception.VolumeBackendAPIException)
jdssd._gc_delete.assert_not_called()
jdssd._cascade_volume_delete.assert_not_called()
jdssd._hide_object.assert_not_called()
self.stop_patches(patches)
def test_gc_delete_not_clone(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
get_lun_resp = {'vscan': None,
'full_name': 'Pool-0/' + jcom.vname(UUID_1),
'userrefs': None,
'primarycache': 'all',
'logbias': 'latency',
'creation': '1591543140',
'sync': 'always',
'is_clone': False,
'dedup': 'off',
'sharenfs': None,
'receive_resume_token': None,
'volsize': '1073741824'}
jdssd.ra.get_lun.return_value = get_lun_resp
jdssd.ra.delete_lun.return_value = None
patches = [mock.patch.object(jdssd, "_delete_back_recursively")]
self.start_patches(patches)
jdssd._gc_delete(jcom.vname(UUID_1))
jdssd._delete_back_recursively.assert_not_called()
jdssd.ra.delete_lun.assert_called_once_with(jcom.vname(UUID_1))
self.stop_patches(patches)
def test_gc_delete_is_clone(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
origin = "Pool-0/{vorig}@{sorig}".format(vorig=jcom.vname(UUID_1),
sorig=jcom.vname(UUID_2))
get_lun_resp = {'origin': origin,
'vscan': None,
'full_name': 'Pool-0/' + jcom.vname(UUID_2),
'userrefs': None,
'primarycache': 'all',
'logbias': 'latency',
'creation': '1591543140',
'sync': 'always',
'is_clone': True,
'dedup': 'off',
'sharenfs': None,
'receive_resume_token': None,
'volsize': '1073741824'}
jdssd.ra.get_lun.return_value = get_lun_resp
jdssd.ra.delete_lun.return_value = None
patches = [mock.patch.object(jdssd, "_delete_back_recursively")]
self.start_patches(patches)
jdssd._gc_delete(jcom.vname(UUID_1))
jdssd._delete_back_recursively.assert_called_once_with(
jcom.vname(UUID_1), jcom.vname(UUID_2))
jdssd.ra.delete_lun.assert_not_called()
self.stop_patches(patches)
def test_delete_back_recursively_res_active(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
opvname = jcom.vname(UUID_1)
opsname = jcom.sname(UUID_2)
jdssd._delete_back_recursively(opvname, opsname)
jdssd.ra.delete_snapshot.assert_called_once_with(
opvname,
opsname,
recursively_children=True,
recursively_dependents=True,
force_umount=True)
def test_delete_back_recursively_hidden_have_snapshots(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
opvname = jcom.hidden(UUID_1)
opsname = jcom.sname(UUID_2)
jdssd.ra.get_snapshots.return_value = SNAPSHOTS_RECURSIVE_1.copy()
jdssd._delete_back_recursively(opvname, opsname)
jdssd.ra.delete_snapshot.assert_called_once_with(
opvname,
opsname,
recursively_children=True,
recursively_dependents=True,
force_umount=True)
def test_delete_back_recursively_single_snapshot(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
opvname = jcom.hidden(UUID_2)
opsname = jcom.sname(UUID_3)
jdssd.ra.get_snapshots.side_effect = [
SNAPSHOTS_RECURSIVE_CHAIN_1.copy(),
SNAPSHOTS_RECURSIVE_CHAIN_2.copy()]
origin = "Pool-0/{vorig}@{sorig}".format(vorig=jcom.vname(UUID_1),
sorig=jcom.vname(UUID_2))
get_lun_resp = {'origin': origin,
'vscan': None,
'full_name': 'Pool-0/' + jcom.hidden(UUID_2),
'userrefs': None,
'primarycache': 'all',
'logbias': 'latency',
'creation': '1591543140',
'sync': 'always',
'is_clone': True,
'dedup': 'off',
'sharenfs': None,
'receive_resume_token': None,
'volsize': '1073741824'}
jdssd.ra.get_lun.return_value = get_lun_resp
jdssd._delete_back_recursively(opvname, opsname)
jdssd.ra.delete_snapshot.assert_called_once_with(
jcom.vname(UUID_1),
jcom.vname(UUID_2),
recursively_children=True,
recursively_dependents=True,
force_umount=True)
get_snapshots_expected = [mock.call(opvname)]
jdssd.ra.get_snapshots.assert_has_calls(get_snapshots_expected)
def test_extend_volume(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
jdssd.ra.extend_lun.return_value = None
jdssd.extend_volume(vol, 2)
jdssd.ra.extend_lun.assert_called_once_with(
jcom.vname(UUID_1),
2147483648)
def test_extend_volume_exceptions(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
for exc in get_jdss_exceptions():
try:
jdssd.extend_volume(vol, 2)
except Exception as err:
self.assertIsInstance(err, exception.VolumeBackendAPIException)
def test_clone_object(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
origin = jcom.vname(UUID_1)
clone = jcom.vname(UUID_2)
jdssd.ra.create_snapshot.return_value = None
jdssd.ra.create_volume_from_snapshot.return_value = None
jdssd._clone_object(origin, clone)
jdssd.ra.create_snapshot.assert_called_once_with(origin, clone)
jdssd.ra.create_volume_from_snapshot.assert_called_once_with(
clone,
clone,
origin,
sparse=False)
def test_clone_object_dne(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
calls = []
origin = jcom.vname(UUID_1)
clone = jcom.vname(UUID_2)
calls.append(mock.call(origin, clone))
jdssd.ra.create_snapshot.side_effect = (
jexc.JDSSResourceNotFoundException(res=origin))
self.assertRaises(exception.VolumeNotFound,
jdssd._clone_object, origin, clone)
origin = jcom.sname(UUID_1)
calls.append(mock.call(origin, clone))
self.assertRaises(exception.SnapshotNotFound,
jdssd._clone_object, origin, clone)
jdssd.ra.create_snapshot.assert_has_calls(calls)
def test_clone_object_exists(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
origin = jcom.vname(UUID_1)
clone = jcom.vname(UUID_2)
jdssd.ra.create_snapshot.side_effect = (
jexc.JDSSSnapshotExistsException(snapshot=clone))
jdssd.ra.delete_snapshot.side_effect = (
jexc.JDSSSnapshotIsBusyException(snapshot=clone))
self.assertRaises(exception.Duplicate,
jdssd._clone_object, origin, clone)
jdssd.ra.delete_snapshot.assert_called_once_with(origin, clone)
jdssd.ra.create_snapshot.assert_called_once_with(origin, clone)
def test_clone_object_volume_exists(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
origin = jcom.vname(UUID_1)
clone = jcom.vname(UUID_2)
jdssd.ra.create_snapshot.return_value = None
jdssd.ra.create_volume_from_snapshot.side_effect = (
jexc.JDSSVolumeExistsException(volume=clone))
self.assertRaises(exception.Duplicate,
jdssd._clone_object, origin, clone)
jdssd.ra.create_snapshot.assert_called_once_with(origin, clone)
jdssd.ra.create_volume_from_snapshot.assert_called_once_with(
clone,
clone,
origin,
sparse=CONFIG_OK['san_thin_provision'])
def test_create_cloned_volume(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
origin_vname = jcom.vname(UUID_1)
clone_vname = jcom.vname(UUID_2)
orig_vol = fake_volume.fake_volume_obj(ctx)
orig_vol.id = UUID_1
orig_vol.size = 1
clone_vol = fake_volume.fake_volume_obj(ctx)
clone_vol.id = UUID_2
clone_vol.size = 1
host = CONFIG_OK["san_hosts"][0]
port = CONFIG_OK["target_port"]
target_name = CONFIG_OK["target_prefix"] + UUID_2
location = '{host}:{port},1 {name} 0'.format(
host=host,
port=port,
name=target_name
)
cred_format = (r"CHAP [0-9,a-z,A-Z]{{{name_len}}} "
"[0-9,a-z,A-Z]{{{pass_len}}}").format(
name_len=8,
pass_len=CONFIG_OK['chap_password_len'])
patches = [
mock.patch.object(jdssd, "_clone_object", return_value=None),
mock.patch.object(jdssd, "extend_volume", return_value=None),
mock.patch.object(
jdssd,
"_get_provider_location",
return_value=location),
mock.patch.object(
jdssd,
"_get_provider_auth",
return_value=cred_format)]
jdssd.ra.get_lun.return_value = {
'vscan': None,
'full_name': 'Pool-0/' + UUID_2,
'userrefs': None,
'primarycache': 'all',
'logbias': 'latency',
'creation': '1591543140',
'sync': 'always',
'is_clone': False,
'dedup': 'off',
'sharenfs': None,
'receive_resume_token': None,
'volsize': '1073741824'}
self.start_patches(patches)
ret = jdssd.create_cloned_volume(clone_vol, orig_vol)
jdssd.extend_volume.assert_not_called()
jdssd._clone_object.assert_called_once_with(origin_vname, clone_vname)
self.stop_patches(patches)
jdssd.ra.get_lun.assert_called_once_with(jcom.vname(clone_vol.id))
self.assertEqual(location, ret['provider_location'])
self.assertEqual(cred_format, ret['provider_auth'])
def test_create_volume_from_snapshot(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
origin_sname = jcom.sname(UUID_1)
clone_vname = jcom.vname(UUID_2)
orig_snap = fake_snapshot.fake_snapshot_obj(ctx)
orig_snap.id = UUID_1
clone_vol = fake_volume.fake_volume_obj(ctx)
clone_vol.id = UUID_2
clone_vol.size = 1
host = CONFIG_OK["san_hosts"][0]
port = CONFIG_OK["target_port"]
target_name = CONFIG_OK["target_prefix"] + UUID_2
location = '{host}:{port},1 {name} 0'.format(
host=host,
port=port,
name=target_name
)
cred_format = (r"CHAP [0-9,a-z,A-Z]{{{name_len}}} "
"[0-9,a-z,A-Z]{{{pass_len}}}").format(
name_len=8,
pass_len=CONFIG_OK['chap_password_len'])
patches = [
mock.patch.object(jdssd, "_clone_object", return_value=None),
mock.patch.object(jdssd, "extend_volume", return_value=None),
mock.patch.object(
jdssd,
"_get_provider_location",
return_value=location),
mock.patch.object(
jdssd,
"_get_provider_auth",
return_value=cred_format)]
jdssd.ra.get_lun.return_value = {
'vscan': None,
'full_name': 'Pool-0/' + UUID_2,
'userrefs': None,
'primarycache': 'all',
'logbias': 'latency',
'creation': '1591543140',
'sync': 'always',
'is_clone': False,
'dedup': 'off',
'sharenfs': None,
'receive_resume_token': None,
'volsize': '1073741824'}
self.start_patches(patches)
ret = jdssd.create_volume_from_snapshot(clone_vol, orig_snap)
jdssd.extend_volume.assert_not_called()
jdssd._clone_object.assert_called_once_with(origin_sname, clone_vname)
self.stop_patches(patches)
jdssd.ra.get_lun.assert_called_once_with(jcom.vname(clone_vol.id))
self.assertEqual(location, ret['provider_location'])
self.assertEqual(cred_format, ret['provider_auth'])
def test_create_volume_from_snapshot_extend(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
origin_sname = jcom.sname(UUID_1)
clone_vname = jcom.vname(UUID_2)
orig_snap = fake_snapshot.fake_snapshot_obj(ctx)
orig_snap.id = UUID_1
clone_vol = fake_volume.fake_volume_obj(ctx)
clone_vol.id = UUID_2
clone_vol.size = 2
host = CONFIG_OK["san_hosts"][0]
port = CONFIG_OK["target_port"]
target_name = CONFIG_OK["target_prefix"] + UUID_2
location = '{host}:{port},1 {name} 0'.format(
host=host,
port=port,
name=target_name
)
cred_format = (r"CHAP [0-9,a-z,A-Z]{{{name_len}}} "
"[0-9,a-z,A-Z]{{{pass_len}}}").format(
name_len=8,
pass_len=CONFIG_OK['chap_password_len'])
patches = [
mock.patch.object(jdssd, "_clone_object", return_value=None),
mock.patch.object(jdssd, "extend_volume", return_value=None),
mock.patch.object(
jdssd,
"_get_provider_location",
return_value=location),
mock.patch.object(
jdssd,
"_get_provider_auth",
return_value=cred_format)]
jdssd.ra.get_lun.return_value = {
'vscan': None,
'full_name': 'Pool-0/' + UUID_2,
'userrefs': None,
'primarycache': 'all',
'logbias': 'latency',
'creation': '1591543140',
'sync': 'always',
'is_clone': False,
'dedup': 'off',
'sharenfs': None,
'receive_resume_token': None,
'volsize': '1073741824'}
self.start_patches(patches)
ret = jdssd.create_volume_from_snapshot(clone_vol, orig_snap)
jdssd.extend_volume.assert_called_once_with(clone_vol, clone_vol.size)
jdssd._clone_object.assert_called_once_with(origin_sname, clone_vname)
self.stop_patches(patches)
jdssd.ra.get_lun.assert_called_once_with(jcom.vname(clone_vol.id))
self.assertEqual(location, ret['provider_location'])
self.assertEqual(cred_format, ret['provider_auth'])
def test_create_snapshot(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vname = jcom.vname(UUID_1)
sname = jcom.sname(UUID_2)
snap = fake_snapshot.fake_snapshot_obj(ctx, id=UUID_2)
snap.volume_id = UUID_1
patches = [
mock.patch.object(jdssd, "_clone_object", return_value=None)]
self.start_patches(patches)
jdssd.create_snapshot(snap)
jdssd._clone_object.assert_called_once_with(vname, sname)
self.stop_patches(patches)
def test_delete_snapshot_no_child(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
sname = jcom.sname(UUID_2)
snap = fake_snapshot.fake_snapshot_obj(ctx, id=UUID_2)
jdssd.ra.get_snapshots.return_value = SNAPSHOTS_EMPTY
patches = [
mock.patch.object(
jdssd,
"_clean_garbage_snapshots",
return_value=SNAPSHOTS_EMPTY),
mock.patch.object(jdssd, "_clone_object", return_value=None),
mock.patch.object(jdssd, "_hide_object", return_value=None),
mock.patch.object(jdssd, "_gc_delete", return_value=None)]
self.start_patches(patches)
jdssd.create_snapshot(snap)
jdssd.delete_snapshot(snap)
jdssd._gc_delete.assert_called_once_with(sname)
jdssd._hide_object.assert_not_called()
jdssd._clean_garbage_snapshots.assert_called_once_with(
sname,
SNAPSHOTS_EMPTY)
self.stop_patches(patches)
def test_delete_snapshot_has_clone(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
sname = jcom.sname(UUID_2)
snap = fake_snapshot.fake_snapshot_obj(ctx, id=UUID_2)
jdssd.ra.get_snapshots.return_value = SNAPSHOTS_EMPTY
patches = [
mock.patch.object(
jdssd,
"_clean_garbage_snapshots",
return_value=SNAPSHOTS_CLONE),
mock.patch.object(jdssd, "_clone_object", return_value=None),
mock.patch.object(jdssd, "_hide_object", return_value=None),
mock.patch.object(jdssd, "_gc_delete", return_value=None)]
self.start_patches(patches)
jdssd.create_snapshot(snap)
jdssd.delete_snapshot(snap)
jdssd._gc_delete.assert_not_called()
jdssd._hide_object.assert_called_once_with(sname)
jdssd._clean_garbage_snapshots.assert_called_once_with(
sname,
SNAPSHOTS_EMPTY)
self.stop_patches(patches)
def test_local_path(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_snapshot.fake_snapshot_obj(ctx, id=UUID_1)
self.assertRaises(NotImplementedError, jdssd.local_path, vol)
def test_get_provider_auth(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
auth = jdssd._get_provider_auth()
cred_format = (r"CHAP [0-9,a-z,A-Z]{{{name_len}}} "
"[0-9,a-z,A-Z]{{{pass_len}}}").format(
name_len=8,
pass_len=CONFIG_OK['chap_password_len'])
self.assertIsNotNone(re.match(cred_format, auth))
def test_get_provider_auth_long(self):
long_pass_config = CONFIG_OK.copy()
long_pass_config['chap_password_len'] = 16
jdssd, ctx = self.get_driver(long_pass_config)
auth = jdssd._get_provider_auth()
cred_format = (r"CHAP [0-9,a-z,A-Z]{{{name_len}}} "
"[0-9,a-z,A-Z]{{{pass_len}}}").format(
name_len=8,
pass_len=16)
self.assertIsNotNone(re.match(cred_format, auth))
def test_create_export(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
patches = [
mock.patch.object(
jdssd,
"_create_target_volume",
return_value=None),
mock.patch.object(
jdssd,
"_get_provider_location",
return_value='provider_location')]
self.start_patches(patches)
ret = jdssd.create_export(ctx, vol, "connector")
jdssd._create_target_volume.assert_called_once_with(vol)
self.stop_patches(patches)
self.assertEqual('provider_location', ret["provider_location"])
def test_ensure_export(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
patches = [
mock.patch.object(
jdssd,
"_ensure_target_volume",
return_value=None)]
self.start_patches(patches)
jdssd.ensure_export(ctx, vol)
jdssd._ensure_target_volume.assert_called_once_with(vol)
self.stop_patches(patches)
def test_remove_export(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx, id=UUID_1)
patches = [
mock.patch.object(
jdssd,
"_remove_target_volume",
return_value=None)]
self.start_patches(patches)
jdssd.remove_export(ctx, vol)
jdssd._remove_target_volume.assert_called_once_with(vol)
self.stop_patches(patches)
def test_update_volume_stats(self):
jdssd, ctx = self.get_driver(CONFIG_BACKEND_NAME)
location_info = 'JovianISCSIDriver:192.168.0.2:Pool-0'
correct_out = {
'vendor_name': 'Open-E',
'driver_version': "1.0.0",
'storage_protocol': 'iSCSI',
'total_capacity_gb': 100,
'free_capacity_gb': 50,
'reserved_percentage': 10,
'volume_backend_name': CONFIG_BACKEND_NAME['volume_backend_name'],
'QoS_support': False,
'location_info': location_info
}
jdssd.ra.get_pool_stats.return_value = {
'size': 100 * o_units.Gi,
'available': 50 * o_units.Gi}
jdssd.ra.get_active_host.return_value = CONFIG_OK['san_hosts']
jdssd._update_volume_stats()
self.assertEqual(correct_out, jdssd._stats)
def test_create_target(self):
jdssd, ctx = self.get_driver(CONFIG_BACKEND_NAME)
target_name = CONFIG_OK['target_prefix'] + UUID_1
jdssd.ra.create_target.return_value = None
jdssd._create_target(target_name, use_chap=True)
jdssd.ra.create_target.assert_called_once_with(
target_name, use_chap=True)
jdssd.ra.create_target.side_effect = jexc.JDSSResourceExistsException(
res=target_name)
self.assertRaises(exception.Duplicate,
jdssd._create_target,
target_name,
use_chap=True)
def test_attach_target_volume(self):
jdssd, ctx = self.get_driver(CONFIG_BACKEND_NAME)
target_name = CONFIG_OK['target_prefix'] + UUID_1
vname = jcom.vname(UUID_1)
jdssd.ra.attach_target_vol.return_value = None
jdssd.ra.delete_target.return_value = None
jdssd._attach_target_volume(target_name, vname)
jdssd.ra.attach_target_vol.assert_called_once_with(
target_name, vname)
jdssd.ra.delete_target.assert_not_called()
ex = jexc.JDSSResourceExistsException(res=target_name)
jdssd.ra.attach_target_vol.side_effect = ex
self.assertRaises(exception.VolumeBackendAPIException,
jdssd._attach_target_volume,
target_name,
vname)
jdssd.ra.delete_target.assert_called_once_with(target_name)
def test_set_target_credentials(self):
jdssd, ctx = self.get_driver(CONFIG_BACKEND_NAME)
target_name = CONFIG_BACKEND_NAME['target_prefix'] + UUID_1
cred = {'name': 'user_name', 'password': '123456789012'}
jdssd.ra.create_target_user.return_value = None
jdssd.ra.delete_target.return_value = None
jdssd._set_target_credentials(target_name, cred)
jdssd.ra.create_target_user.assert_called_once_with(
target_name, cred)
jdssd.ra.delete_target.assert_not_called()
ex = jexc.JDSSResourceExistsException(res=target_name)
jdssd.ra.create_target_user.side_effect = ex
self.assertRaises(exception.VolumeBackendAPIException,
jdssd._set_target_credentials,
target_name,
cred)
jdssd.ra.delete_target.assert_called_once_with(target_name)
def test_create_target_volume(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
target_name = CONFIG_OK['target_prefix'] + UUID_1
vol.provider_auth = 'chap user_name 123456789012'
cred = {'name': 'user_name', 'password': '123456789012'}
patches = [
mock.patch.object(jdssd, "_create_target"),
mock.patch.object(jdssd, "_attach_target_volume"),
mock.patch.object(jdssd, "_set_target_credentials")]
self.start_patches(patches)
jdssd._create_target_volume(vol)
jdssd._create_target.assert_called_once_with(target_name, True)
jdssd._attach_target_volume.assert_called_once_with(
target_name, jcom.vname(UUID_1))
jdssd._set_target_credentials.assert_called_once_with(
target_name, cred)
self.stop_patches(patches)
def test_ensure_target_volume(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
target_name = CONFIG_OK['target_prefix'] + UUID_1
vol.provider_auth = 'chap user_name 123456789012'
cred = {'name': 'user_name'}
patches = [
mock.patch.object(jdssd, "_create_target"),
mock.patch.object(jdssd, "_attach_target_volume"),
mock.patch.object(jdssd, "_set_target_credentials"),
mock.patch.object(jdssd, "_attach_target_volume")]
jdssd.ra.is_target.return_value = True
jdssd.ra.is_target_lun.return_value = True
jdssd.ra.get_target_user.return_value = [cred]
self.start_patches(patches)
jdssd._ensure_target_volume(vol)
jdssd.ra.is_target.assert_called_once_with(target_name)
jdssd.ra.is_target_lun.assert_called_once_with(target_name, UUID_1)
jdssd.ra.get_target_user.assert_called_once_with(target_name)
jdssd.ra.delete_target_user.assert_not_called()
jdssd._set_target_credentials.assert_not_called()
self.stop_patches(patches)
def test_ensure_target_volume_not_attached(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
vname = jcom.vname(UUID_1)
target_name = CONFIG_OK['target_prefix'] + UUID_1
vol.provider_auth = 'chap user_name 123456789012'
cred = {'name': 'user_name'}
patches = [
mock.patch.object(jdssd, "_create_target"),
mock.patch.object(jdssd, "_attach_target_volume"),
mock.patch.object(jdssd, "_set_target_credentials"),
mock.patch.object(jdssd, "_attach_target_volume")]
jdssd.ra.is_target.return_value = True
jdssd.ra.is_target_lun.return_value = False
jdssd.ra.get_target_user.return_value = [cred]
self.start_patches(patches)
jdssd._ensure_target_volume(vol)
jdssd.ra.is_target.assert_called_once_with(target_name)
jdssd.ra.is_target_lun.assert_called_once_with(target_name, UUID_1)
jdssd._attach_target_volume.assert_called_once_with(
target_name, vname)
jdssd.ra.get_target_user.assert_called_once_with(target_name)
jdssd.ra.delete_target_user.assert_not_called()
jdssd._set_target_credentials.assert_not_called()
self.stop_patches(patches)
def test_ensure_target_volume_no_target(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
target_name = CONFIG_OK['target_prefix'] + UUID_1
vol.provider_auth = 'chap user_name 123456789012'
cred = {'name': 'user_name'}
patches = [
mock.patch.object(jdssd, "_create_target_volume"),
mock.patch.object(jdssd, "_attach_target_volume"),
mock.patch.object(jdssd, "_set_target_credentials"),
mock.patch.object(jdssd, "_attach_target_volume")]
jdssd.ra.is_target.return_value = False
jdssd.ra.get_target_user.return_value = cred['name']
self.start_patches(patches)
jdssd._ensure_target_volume(vol)
jdssd.ra.is_target.assert_called_once_with(target_name)
jdssd._create_target_volume.assert_called_once_with(vol)
jdssd.ra.is_target_lun.assert_not_called()
def test_remove_target_volume(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
target_name = CONFIG_OK['target_prefix'] + UUID_1
jdssd.ra.detach_target_vol.return_value = None
jdssd.ra.delete_target.return_value = None
jdssd._remove_target_volume(vol)
jdssd.ra.detach_target_vol.assert_called_once_with(target_name,
jcom.vname(UUID_1))
jdssd.ra.delete_target.assert_called_with(target_name)
def test_remove_target_volume_no_target(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
target_name = CONFIG_OK['target_prefix'] + UUID_1
jdssd.ra.detach_target_vol.return_value = None
jdssd.ra.detach_target_vol.side_effect = (
jexc.JDSSResourceNotFoundException(res=target_name))
jdssd.ra.delete_target.return_value = None
jdssd._remove_target_volume(vol)
jdssd.ra.detach_target_vol.assert_called_once_with(target_name,
jcom.vname(UUID_1))
jdssd.ra.delete_target.assert_called_with(target_name)
def test_remove_target_volume_fail_to_detach(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
target_name = CONFIG_OK['target_prefix'] + UUID_1
jdssd.ra.detach_target_vol.side_effect = (
jexc.JDSSRESTException(reason='running test', request='test'))
jdssd.ra.delete_target.return_value = None
self.assertRaises(exception.VolumeBackendAPIException,
jdssd._remove_target_volume, vol)
jdssd.ra.detach_target_vol.assert_called_once_with(
target_name, jcom.vname(UUID_1))
jdssd.ra.delete_target.assert_not_called()
def test_remove_target_volume_fail_to_delete(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
target_name = CONFIG_OK['target_prefix'] + UUID_1
jdssd.ra.detach_target_vol.return_value = None
jdssd.ra.delete_target.side_effect = (
jexc.JDSSRESTException(reason='running test', request='test'))
self.assertRaises(exception.VolumeBackendAPIException,
jdssd._remove_target_volume, vol)
jdssd.ra.detach_target_vol.assert_called_once_with(target_name,
jcom.vname(UUID_1))
jdssd.ra.delete_target.assert_called_with(target_name)
def test_get_iscsi_properties(self):
jdssd, ctx = self.get_driver(CONFIG_OK)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
vol.provider_auth = 'chap user_name 123456789012'
connector = {'multipath': True}
target_name = CONFIG_OK['target_prefix'] + UUID_1
ret = jdssd._get_iscsi_properties(vol, connector)
expected = {'auth_method': 'chap',
'auth_password': '123456789012',
'auth_username': 'user_name',
'target_discovered': False,
'target_iqns': [target_name],
'target_lun': 0,
'target_luns': [0],
'target_portals': ['192.168.0.2:3260']}
self.assertEqual(expected, ret)
def test_get_iscsi_properties_multipath(self):
jdssd, ctx = self.get_driver(CONFIG_MULTI_HOST)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
vol.provider_auth = 'chap user_name 123456789012'
connector = {'multipath': True}
target_name = CONFIG_OK['target_prefix'] + UUID_1
ret = jdssd._get_iscsi_properties(vol, connector)
expected = {'auth_method': 'chap',
'auth_password': '123456789012',
'auth_username': 'user_name',
'target_discovered': False,
'target_iqns': [target_name, target_name],
'target_lun': 0,
'target_luns': [0, 0],
'target_portals': ['192.168.0.2:3260', '192.168.0.3:3260']}
self.assertEqual(expected, ret)
def test_initialize_connection(self):
jdssd, ctx = self.get_driver(CONFIG_MULTI_HOST)
vol = fake_volume.fake_volume_obj(ctx)
vol.id = UUID_1
vol.provider_auth = 'chap user_name 123456789012'
connector = {'multipath': True, 'ip': '172.16.0.2'}
target_name = CONFIG_OK['target_prefix'] + UUID_1
properties = {'auth_method': 'chap',
'auth_password': '123456789012',
'auth_username': 'user_name',
'target_discovered': False,
'target_iqns': [target_name, target_name],
'target_lun': 0,
'target_luns': [0, 0],
'target_portals': ['192.168.0.2:3260',
'192.168.0.3:3260']}
con_info = {
'driver_volume_type': 'iscsi',
'data': properties,
}
jdssd.ra.activate_target.return_value = None
ret = jdssd.initialize_connection(vol, connector)
self.assertEqual(con_info, ret)