<commit_before># vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Nexenta Systems, 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.
"""
:mod:`nexenta.volume` -- Driver to store volumes on Nexenta Appliance
=====================================================================

.. automodule:: nexenta.volume
.. moduleauthor:: <NAME> <<EMAIL>>
.. moduleauthor:: <NAME> <<EMAIL>>
.. moduleauthor:: <NAME> <<EMAIL>>
"""

from oslo.config import cfg

from cinder import exception
from cinder.openstack.common import log as logging
from cinder import units
from cinder.volume import driver
from cinder.volume.drivers import nexenta
from cinder.volume.drivers.nexenta import jsonrpc
from cinder.volume.drivers.nexenta import options

LOG = logging.getLogger(__name__)

CONF = cfg.CONF
CONF.register_opts(options.NEXENTA_CONNECTION_OPTIONS)
CONF.register_opts(options.NEXENTA_ISCSI_OPTIONS)
CONF.register_opts(options.NEXENTA_VOLUME_OPTIONS)


class NexentaDriver(driver.ISCSIDriver):  # pylint: disable=R0921
    """Executes volume driver commands on Nexenta Appliance."""

    VERSION = '1.0.0'

    def __init__(self, *args, **kwargs):
        super(NexentaDriver, self).__init__(*args, **kwargs)
        self.nms = None
        if self.configuration:
            self.configuration.append_config_values(
                options.NEXENTA_CONNECTION_OPTIONS)
            self.configuration.append_config_values(
                options.NEXENTA_ISCSI_OPTIONS)
            self.configuration.append_config_values(
                options.NEXENTA_VOLUME_OPTIONS)

    def do_setup(self, context):
        protocol = self.configuration.nexenta_rest_protocol
        auto = protocol == 'auto'
        if auto:
            protocol = 'http'
        url = '%s://%s:%s/rest/nms/' % (protocol,
                                        self.configuration.nexenta_host,
                                        self.configuration.nexenta_rest_port)
        self.nms = jsonrpc.NexentaJSONProxy(
            url, self.configuration.nexenta_user,
            self.configuration.nexenta_password, auto=auto)

    def check_for_setup_error(self):
        """Verify that the volume for our zvols exists.

        :raise: :py:exc:`LookupError`
        """
        if not self.nms.volume.object_exists(
                self.configuration.nexenta_volume):
            raise LookupError(_("Volume %s does not exist in Nexenta SA"),
                              self.configuration.nexenta_volume)

    def _get_zvol_name(self, volume_name):
        """Return zvol name that corresponds given volume name."""
        return '%s/%s' % (self.configuration.nexenta_volume, volume_name)

    def _get_target_name(self, volume_name):
        """Return iSCSI target name to access volume."""
        return '%s%s' % (self.configuration.nexenta_target_prefix, volume_name)

    def _get_target_group_name(self, volume_name):
        """Return Nexenta iSCSI target group name for volume."""
        return '%s%s' % (self.configuration.nexenta_target_group_prefix,
                         volume_name)

    def _get_clone_snap_name(self, volume):
        """Return name for snapshot that will be used to clone the volume."""
        return 'cinder-clone-snap-%(id)s' % volume

    def create_volume(self, volume):
        """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.

        :param volume: volume reference
        :param new_size: volume new size in GB
        """
        LOG.info(_('Extending volume: %(id)s New size: %(size)s GB'),
                 {'id': volume['id'], 'size': new_size})
        self.nms.zvol.set_child_prop(self._get_zvol_name(volume['name']),
                                     'volsize', '%sG' % new_size)

    def delete_volume(self, volume):
        """Destroy a zvol on appliance.

        :param volume: volume reference
        """
        try:
            self.nms.zvol.destroy(self._get_zvol_name(volume['name']), '')
        except nexenta.NexentaException as exc:
            if "does not exist" in exc.args[0]:
                LOG.info(_('Volume %s does not exist, it seems it was already '
                           'deleted'), volume['name'])
                return
            if "zvol has children" in exc.args[0]:
                raise exception.VolumeIsBusy(volume_name=volume['name'])
            raise

    def create_cloned_volume(self, volume, src_vref):
        """Creates a clone of the specified volume.

        :param volume: new volume reference
        :param src_vref: source volume reference
        """
        snapshot = {'volume_name': src_vref['name'],
                    'name': self._get_clone_snap_name(volume)}
        LOG.debug(_('Creating temp snapshot of the original volume: '
                    '%(volume_name)s@%(name)s'), snapshot)
        self.create_snapshot(snapshot)
        try:
            cmd = 'zfs send %(src_vol)s@%(src_snap)s | zfs recv %(volume)s' % {
                'src_vol': self._get_zvol_name(src_vref['name']),
                'src_snap': snapshot['name'],
                'volume': self._get_zvol_name(volume['name'])
            }
            LOG.debug(_('Executing zfs send/recv on the appliance'))
            self.nms.appliance.execute(cmd)
            LOG.debug(_('zfs send/recv done, new volume %s created'),
                      volume['name'])
        finally:
            try:
                # deleting temp snapshot of the original volume
                self.delete_snapshot(snapshot)
            except (nexenta.NexentaException, exception.SnapshotIsBusy):
                LOG.warning(_('Failed to delete temp snapshot '
                              '%(volume)s@%(snapshot)s'),
                            {'volume': src_vref['name'],
                             'snapshot': snapshot['name']})
        try:
            # deleting snapshot resulting from zfs recv
            self.delete_snapshot({'volume_name': volume['name'],
                                  'name': snapshot['name']})
        except (nexenta.NexentaException, exception.SnapshotIsBusy):
            LOG.warning(_('Failed to delete zfs recv snapshot '
                          '%(volume)s@%(snapshot)s'),
                        {'volume': volume['name'],
                         'snapshot': snapshot['name']})

    def create_snapshot(self, snapshot):
        """Create snapshot of existing zvol on appliance.

        :param snapshot: shapshot reference
        """
        self.nms.zvol.create_snapshot(
            self._get_zvol_name(snapshot['volume_name']),
            snapshot['name'], '')

    def create_volume_from_snapshot(self, volume, snapshot):
        """Create new volume from other's snapshot on appliance.

        :param volume: reference of volume to be created
        :param snapshot: reference of source snapshot
        """
        self.nms.zvol.clone(
            '%s@%s' % (self._get_zvol_name(snapshot['volume_name']),
                       snapshot['name']),
            self._get_zvol_name(volume['name']))

    def delete_snapshot(self, snapshot):
        """Delete volume's snapshot on appliance.

        :param snapshot: snapshot reference
        """
        try:
            self.nms.snapshot.destroy(
                '%s@%s' % (self._get_zvol_name(snapshot['volume_name']),
                           snapshot['name']),
                '')
        except nexenta.NexentaException as exc:
            if "snapshot has dependent clones" in exc.args[0]:
                raise exception.SnapshotIsBusy(snapshot_name=snapshot['name'])
            else:
                raise

    def local_path(self, volume):
        """Return local path to existing local volume.

        We never have local volumes, so it raises NotImplementedError.

        :raise: :py:exc:`NotImplementedError`
        """
        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<commit_msg>Nexenta iSCSI driver fix _lu_exists<commit_after># vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Nexenta Systems, 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.
"""
:mod:`nexenta.volume` -- Driver to store volumes on Nexenta Appliance
=====================================================================

.. automodule:: nexenta.volume
.. moduleauthor:: <NAME> <<EMAIL>>
.. moduleauthor:: <NAME> <<EMAIL>>
.. moduleauthor:: <NAME> <<EMAIL>>
"""

from oslo.config import cfg

from cinder import exception
from cinder.openstack.common import log as logging
from cinder import units
from cinder.volume import driver
from cinder.volume.drivers import nexenta
from cinder.volume.drivers.nexenta import jsonrpc
from cinder.volume.drivers.nexenta import options

LOG = logging.getLogger(__name__)

CONF = cfg.CONF
CONF.register_opts(options.NEXENTA_CONNECTION_OPTIONS)
CONF.register_opts(options.NEXENTA_ISCSI_OPTIONS)
CONF.register_opts(options.NEXENTA_VOLUME_OPTIONS)


class NexentaDriver(driver.ISCSIDriver):  # pylint: disable=R0921
    """Executes volume driver commands on Nexenta Appliance.

    Version history:
        1.0.0 - Initial driver version.
        1.0.1 - Fixed bug #1236626: catch "does not exist" exception of
                lu_exists.
    """

    VERSION = '1.0.1'

    def __init__(self, *args, **kwargs):
        super(NexentaDriver, self).__init__(*args, **kwargs)
        self.nms = None
        if self.configuration:
            self.configuration.append_config_values(
                options.NEXENTA_CONNECTION_OPTIONS)
            self.configuration.append_config_values(
                options.NEXENTA_ISCSI_OPTIONS)
            self.configuration.append_config_values(
                options.NEXENTA_VOLUME_OPTIONS)

    def do_setup(self, context):
        protocol = self.configuration.nexenta_rest_protocol
        auto = protocol == 'auto'
        if auto:
            protocol = 'http'
        url = '%s://%s:%s/rest/nms/' % (protocol,
                                        self.configuration.nexenta_host,
                                        self.configuration.nexenta_rest_port)
        self.nms = jsonrpc.NexentaJSONProxy(
            url, self.configuration.nexenta_user,
            self.configuration.nexenta_password, auto=auto)

    def check_for_setup_error(self):
        """Verify that the volume for our zvols exists.

        :raise: :py:exc:`LookupError`
        """
        if not self.nms.volume.object_exists(
                self.configuration.nexenta_volume):
            raise LookupError(_("Volume %s does not exist in Nexenta SA"),
                              self.configuration.nexenta_volume)

    def _get_zvol_name(self, volume_name):
        """Return zvol name that corresponds given volume name."""
        return '%s/%s' % (self.configuration.nexenta_volume, volume_name)

    def _get_target_name(self, volume_name):
        """Return iSCSI target name to access volume."""
        return '%s%s' % (self.configuration.nexenta_target_prefix, volume_name)

    def _get_target_group_name(self, volume_name):
        """Return Nexenta iSCSI target group name for volume."""
        return '%s%s' % (self.configuration.nexenta_target_group_prefix,
                         volume_name)

    def _get_clone_snap_name(self, volume):
        """Return name for snapshot that will be used to clone the volume."""
        return 'cinder-clone-snap-%(id)s' % volume

    def create_volume(self, volume):
        """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.

        :param volume: volume reference
        :param new_size: volume new size in GB
        """
        LOG.info(_('Extending volume: %(id)s New size: %(size)s GB'),
                 {'id': volume['id'], 'size': new_size})
        self.nms.zvol.set_child_prop(self._get_zvol_name(volume['name']),
                                     'volsize', '%sG' % new_size)

    def delete_volume(self, volume):
        """Destroy a zvol on appliance.

        :param volume: volume reference
        """
        try:
            self.nms.zvol.destroy(self._get_zvol_name(volume['name']), '')
        except nexenta.NexentaException as exc:
            if "does not exist" in exc.args[0]:
                LOG.info(_('Volume %s does not exist, it seems it was already '
                           'deleted'), volume['name'])
                return
            if "zvol has children" in exc.args[0]:
                raise exception.VolumeIsBusy(volume_name=volume['name'])
            raise

    def create_cloned_volume(self, volume, src_vref):
        """Creates a clone of the specified volume.

        :param volume: new volume reference
        :param src_vref: source volume reference
        """
        snapshot = {'volume_name': src_vref['name'],
                    'name': self._get_clone_snap_name(volume)}
        LOG.debug(_('Creating temp snapshot of the original volume: '
                    '%(volume_name)s@%(name)s'), snapshot)
        self.create_snapshot(snapshot)
        try:
            cmd = 'zfs send %(src_vol)s@%(src_snap)s | zfs recv %(volume)s' % {
                'src_vol': self._get_zvol_name(src_vref['name']),
                'src_snap': snapshot['name'],
                'volume': self._get_zvol_name(volume['name'])
            }
            LOG.debug(_('Executing zfs send/recv on the appliance'))
            self.nms.appliance.execute(cmd)
            LOG.debug(_('zfs send/recv done, new volume %s created'),
                      volume['name'])
        finally:
            try:
                # deleting temp snapshot of the original volume
                self.delete_snapshot(snapshot)
            except (nexenta.NexentaException, exception.SnapshotIsBusy):
                LOG.warning(_('Failed to delete temp snapshot '
                              '%(volume)s@%(snapshot)s'),
                            {'volume': src_vref['name'],
                             'snapshot': snapshot['name']})
        try:
            # deleting snapshot resulting from zfs recv
            self.delete_snapshot({'volume_name': volume['name'],
                                  'name': snapshot['name']})
        except (nexenta.NexentaException, exception.SnapshotIsBusy):
            LOG.warning(_('Failed to delete zfs recv snapshot '
                          '%(volume)s@%(snapshot)s'),
                        {'volume': volume['name'],
                         'snapshot': snapshot['name']})

    def create_snapshot(self, snapshot):
        """Create snapshot of existing zvol on appliance.

        :param snapshot: shapshot reference
        """
        self.nms.zvol.create_snapshot(
            self._get_zvol_name(snapshot['volume_name']),
            snapshot['name'], '')

    def create_volume_from_snapshot(self, volume, snapshot):
        """Create new volume from other's snapshot on appliance.

        :param volume: reference of volume to be created
        :param snapshot: reference of source snapshot
        """
        self.nms.zvol.clone(
            '%s@%s' % (self._get_zvol_name(snapshot['volume_name']),
                       snapshot['name']),
            self._get_zvol_name(volume['name']))

    def delete_snapshot(self, snapshot):
        """Delete volume's snapshot on appliance.

        :param snapshot: snapshot reference
        """
        try:
            self.nms.snapshot.destroy(
                '%s@%s' % (self._get_zvol_name(snapshot['volume_name']),
                           snapshot['name']),
                '')
        except nexenta.NexentaException as exc:
            if "snapshot has dependent clones" in exc.args[0]:
                raise exception.SnapshotIsBusy(snapshot_name=snapshot['name'])
            else:
                raise

    def local_path(self, volume):
        """Return local path to existing local volume.

        We never have local volumes, so it raises NotImplementedError.

        :raise: :py:exc:`NotImplementedError`
        """
        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
        """
        try:
            return bool(self.nms.scsidisk.lu_exists(zvol_name))
        except nexenta.NexentaException as exc:
            if 'does not exist' not in exc.args[0]:
                raise
            return False

    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
{'fraction_non_alphanumeric': 0.10600314494496346, 'fraction_numerical': 0.002682453057071501, 'mean_word_length': 2.2394007490636705, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3855246', 'n_tokens_mistral': 6044, 'n_tokens_neox': 5858, 'n_words': 1607}