def test_create_volume(self):
self.nms_mock.zvol.create('cinder/volume1', '1G', '8K', True)
+ self.nms_mock.stmf.list_targets()
+ self.nms_mock.iscsitarget.create_target({'target_name': 'iqn:volume1'})
+ self.nms_mock.stmf.list_targetgroups()
+ self.nms_mock.stmf.create_targetgroup('cinder/volume1')
+ self.nms_mock.stmf.list_targetgroup_members('cinder/volume1')
+ self.nms_mock.stmf.add_targetgroup_member('cinder/volume1',
+ 'iqn:volume1')
+ self.nms_mock.scsidisk.lu_exists('cinder/volume1')
+ self.nms_mock.scsidisk.create_lu('cinder/volume1', {})
+ self.nms_mock.scsidisk.lu_shared('cinder/volume1')
+ self.nms_mock.scsidisk.add_lun_mapping_entry(
+ 'cinder/volume1', {'target_group': 'cinder/volume1', 'lun': '0'})
self.mox.ReplayAll()
self.drv.create_volume(self.TEST_VOLUME_REF)
self.drv.delete_snapshot(self.TEST_SNAPSHOT_REF)
_CREATE_EXPORT_METHODS = [
+ ('stmf', 'list_targets', tuple(), [], False, ),
('iscsitarget', 'create_target', ({'target_name': 'iqn:volume1'},),
u'Unable to create iscsi target\n'
u' iSCSI target iqn.1986-03.com.sun:02:cinder-volume1 already'
u' configured\n'
- u' itadm create-target failed with error 17\n', ),
+ u' itadm create-target failed with error 17\n', True, ),
+ ('stmf', 'list_targetgroups', tuple(), [], False, ),
('stmf', 'create_targetgroup', ('cinder/volume1',),
u'Unable to create targetgroup: stmfadm: cinder/volume1:'
- u' already exists\n', ),
+ u' already exists\n', True, ),
+ ('stmf', 'list_targetgroup_members', ('cinder/volume1', ), [],
+ False, ),
('stmf', 'add_targetgroup_member', ('cinder/volume1', 'iqn:volume1'),
u'Unable to add member to targetgroup: stmfadm:'
- u' iqn.1986-03.com.sun:02:cinder-volume1: already exists\n', ),
+ u' iqn.1986-03.com.sun:02:cinder-volume1: already exists\n',
+ True, ),
+ ('scsidisk', 'lu_exists', ('cinder/volume1', ), 0, False, ),
('scsidisk', 'create_lu', ('cinder/volume1', {}),
u"Unable to create lu with zvol 'cinder/volume1':\n"
- u" sbdadm: filename /dev/zvol/rdsk/cinder/volume1: in use\n", ),
+ u" sbdadm: filename /dev/zvol/rdsk/cinder/volume1: in use\n",
+ True, ),
+ ('scsidisk', 'lu_shared', ('cinder/volume1', ), 0, False, ),
('scsidisk', 'add_lun_mapping_entry', ('cinder/volume1', {
'target_group': 'cinder/volume1', 'lun': '0'}),
u"Unable to add view to zvol 'cinder/volume1' (LUNs in use: ):\n"
- u" stmfadm: view entry exists\n", ),
+ u" stmfadm: view entry exists\n", True, ),
]
- def _stub_export_method(self, module, method, args, error, fail=False):
+ def _stub_export_method(self, module, method, args, error, raise_exception,
+ fail=False):
m = getattr(self.nms_mock, module)
m = getattr(m, method)
mock = m(*args)
- if fail:
+ if raise_exception and fail:
mock.AndRaise(nexenta.NexentaException(error))
+ else:
+ mock.AndReturn(error)
def _stub_all_export_methods(self, fail=False):
for params in self._CREATE_EXPORT_METHODS:
return _test_create_export_fail
for i in range(len(_CREATE_EXPORT_METHODS)):
- locals()['test_create_export_fail_%d' % i] = __get_test(i)
+ if i % 2:
+ locals()['test_create_export_fail_%d' % i] = __get_test(i)
def test_ensure_export(self):
self._stub_all_export_methods(fail=True)
"""Create a zvol on appliance.
:param volume: volume reference
+ :return: model update dict for volume reference
"""
self.nms.zvol.create(
self._get_zvol_name(volume['name']),
'%sG' % (volume['size'],),
self.configuration.nexenta_blocksize,
self.configuration.nexenta_sparse)
+ return self.create_export(None, volume)
def extend_volume(self, volume, new_size):
"""Extend an existing volume.
"""
raise NotImplementedError
+ def _target_exists(self, target):
+ """Check if iSCSI target exist.
+
+ :param target: target name
+ :return: True if target exist, else False
+ """
+ targets = self.nms.stmf.list_targets()
+ if not targets:
+ return False
+ return target in self.nms.stmf.list_targets()
+
+ def _target_group_exists(self, target_group):
+ """Check if target group exist.
+
+ :param target_group: target group
+ :return: True if target group exist, else False
+ """
+ groups = self.nms.stmf.list_targetgroups()
+ if not groups:
+ return False
+ return target_group in groups
+
+ def _target_member_in_target_group(self, target_group, target_member):
+ """Check if target member in target group.
+
+ :param target_group: target group
+ :param target_member: target member
+ :return: True if target member in target group, else False
+ :raises: NexentaException if target group doesn't exist
+ """
+ members = self.nms.stmf.list_targetgroup_members(target_group)
+ if not members:
+ return False
+ return target_member in members
+
+ def _lu_exists(self, zvol_name):
+ """Check if LU exists on appliance.
+
+ :param zvol_name: Zvol name
+ :raises: NexentaException if zvol not exists
+ :return: True if LU exists, else False
+ """
+ return bool(self.nms.scsidisk.lu_exists(zvol_name))
+
+ def _is_lu_shared(self, zvol_name):
+ """Check if LU exists on appliance and shared.
+
+ :param zvol_name: Zvol name
+ :raises: NexentaException if Zvol not exist
+ :return: True if LU exists and shared, else False
+ """
+ try:
+ shared = self.nms.scsidisk.lu_shared(zvol_name) > 0
+ except nexenta.NexentaException as exc:
+ if 'does not exist for zvol' not in exc.args[0]:
+ raise # Zvol does not exists
+ shared = False # LU does not exist
+ return shared
+
+ def _is_volume_exported(self, volume):
+ """Check if volume exported.
+
+ :param volume: volume object
+ :return: True if volume exported, else False
+ """
+ zvol_name = self._get_zvol_name(volume['name'])
+ target_name = self._get_target_name(volume['name'])
+ target_group_name = self._get_target_group_name(volume['name'])
+ return (self._target_exists(target_name) and
+ self._target_group_exists(target_group_name) and
+ self._target_member_in_target_group(target_group_name,
+ target_name) and
+ self._lu_exists(zvol_name) and
+ self._is_lu_shared(zvol_name))
+
+ def _get_provider_location(self, volume):
+ """Returns volume iscsiadm-formatted provider location string."""
+ return '%(host)s:%(port)s,1 %(name)s 0' % {
+ 'host': self.configuration.nexenta_host,
+ 'port': self.configuration.nexenta_iscsi_target_portal_port,
+ 'name': self._get_target_name(volume['name'])
+ }
+
def _do_export(self, _ctx, volume, ensure=False):
"""Do all steps to get zvol exported as LUN 0 at separate target.
:param volume: reference of volume to be exported
:param ensure: if True, ignore errors caused by already existing
resources
- :return: iscsiadm-formatted provider location string
"""
zvol_name = self._get_zvol_name(volume['name'])
target_name = self._get_target_name(volume['name'])
target_group_name = self._get_target_group_name(volume['name'])
- target_already_configured = False
- try:
- self.nms.iscsitarget.create_target({'target_name': target_name})
- except nexenta.NexentaException as exc:
- if ensure and 'already configured' in exc.args[0]:
- target_already_configured = True
- LOG.info(_('Ignored target creation error "%s" while ensuring '
+ if not self._target_exists(target_name):
+ try:
+ self.nms.iscsitarget.create_target({
+ 'target_name': target_name})
+ except nexenta.NexentaException as exc:
+ if ensure and 'already configured' in exc.args[0]:
+ LOG.info(_('Ignored target creation error "%s" while '
+ 'ensuring export'), exc)
+ else:
+ raise
+ if not self._target_group_exists(target_group_name):
+ try:
+ self.nms.stmf.create_targetgroup(target_group_name)
+ except nexenta.NexentaException as exc:
+ if ((ensure and 'already exists' in exc.args[0]) or
+ 'target must be offline' in exc.args[0]):
+ LOG.info(_('Ignored target group creation error "%s" '
+ 'while ensuring export'), exc)
+ else:
+ raise
+ if not self._target_member_in_target_group(target_group_name,
+ target_name):
+ try:
+ self.nms.stmf.add_targetgroup_member(target_group_name,
+ target_name)
+ except nexenta.NexentaException as exc:
+ if ((ensure and 'already exists' in exc.args[0]) or
+ 'target must be offline' in exc.args[0]):
+ LOG.info(_('Ignored target group member addition error '
+ '"%s" while ensuring export'), exc)
+ else:
+ raise
+ if not self._lu_exists(zvol_name):
+ try:
+ self.nms.scsidisk.create_lu(zvol_name, {})
+ except nexenta.NexentaException as exc:
+ if not ensure or 'in use' not in exc.args[0]:
+ raise
+ LOG.info(_('Ignored LU creation error "%s" while ensuring '
'export'), exc)
- else:
- raise
- try:
- self.nms.stmf.create_targetgroup(target_group_name)
- except nexenta.NexentaException as exc:
- if ((ensure and 'already exists' in exc.args[0]) or
- (target_already_configured and
- 'target must be offline' in exc.args[0])):
- LOG.info(_('Ignored target group creation error "%s" while '
- 'ensuring export'), exc)
- else:
- raise
- try:
- self.nms.stmf.add_targetgroup_member(target_group_name,
- target_name)
- except nexenta.NexentaException as exc:
- if ensure and ('already exists' in exc.args[0] or
- 'target must be offline' in exc.args[0]):
- LOG.info(_('Ignored target group member addition error "%s" '
+ if not self._is_lu_shared(zvol_name):
+ try:
+ self.nms.scsidisk.add_lun_mapping_entry(zvol_name, {
+ 'target_group': target_group_name,
+ 'lun': '0'})
+ except nexenta.NexentaException as exc:
+ if not ensure or 'view entry exists' not in exc.args[0]:
+ raise
+ LOG.info(_('Ignored LUN mapping entry addition error "%s" '
'while ensuring export'), exc)
- else:
- raise
- try:
- self.nms.scsidisk.create_lu(zvol_name, {})
- except nexenta.NexentaException as exc:
- if not ensure or 'in use' not in exc.args[0]:
- raise
- LOG.info(_('Ignored LU creation error "%s" while ensuring export'),
- exc)
- try:
- self.nms.scsidisk.add_lun_mapping_entry(zvol_name, {
- 'target_group': target_group_name,
- 'lun': '0'
- })
- except nexenta.NexentaException as exc:
- if not ensure or 'view entry exists' not in exc.args[0]:
- raise
- LOG.info(_('Ignored LUN mapping entry addition error "%s" while '
- 'ensuring export'), exc)
- return '%(host)s:%(port)s,1 %(name)s 0' % {
- 'host': self.configuration.nexenta_host,
- 'port': self.configuration.nexenta_iscsi_target_portal_port,
- 'name': target_name
- }
def create_export(self, _ctx, volume):
"""Create new export for zvol.
:param volume: reference of volume to be exported
:return: iscsiadm-formatted provider location string
"""
- loc = self._do_export(_ctx, volume, ensure=False)
- return {'provider_location': loc}
+ self._do_export(_ctx, volume, ensure=False)
+ return {'provider_location': self._get_provider_location(volume)}
def ensure_export(self, _ctx, volume):
"""Recreate parts of export if necessary.