예제 #1
0
    def initialize_connection(self, volume, connector):
        """Allow connection to connector and return connection info."""
        resp = {}

        LOG.debug(
            "Initialize_connection: initiator - %(initiator)s  host - "
            "%(host)s ip - %(ip)s", {
                'initiator': connector['initiator'],
                'host': connector['host'],
                'ip': connector['ip']
            })

        iqn = self._get_iqn(connector)

        # Pick a random single target to give the connector since
        # there is no multipathing support
        tgt = random.choice(self.gateway_iscsi_ip_addresses)

        resp = self.common.vmem_mg.client.create_client(
            name=connector['host'],
            proto='iSCSI',
            iscsi_iqns=connector['initiator'])

        # Raise if we failed for any reason other than a 'client
        # already exists' error code
        if not resp['success'] and 'Error: 0x900100cd' not in resp['msg']:
            msg = _("Failed to create iscsi client")
            raise exception.ViolinBackendErr(message=msg)

        resp = self.common.vmem_mg.client.create_iscsi_target(
            name=iqn,
            client_name=connector['host'],
            ip=self.gateway_iscsi_ip_addresses,
            access_mode='ReadWrite')

        # Same here, raise for any failure other than a 'target
        # already exists' error code
        if not resp['success'] and 'Error: 0x09024309' not in resp['msg']:
            msg = (_("Failed to create iscsi target: %(msg)s") % {
                'msg': resp['msg']
            })
            raise exception.ViolinBackendErr(message=msg)

        lun_id = self._export_lun(volume, iqn, connector)

        properties = {}
        properties['target_discovered'] = False
        properties['target_iqn'] = iqn
        properties['target_portal'] = '%s:%s' % (tgt, '3260')
        properties['target_lun'] = lun_id
        properties['volume_id'] = volume['id']
        properties['access_mode'] = 'rw'

        LOG.debug("Return ISCSI data: %(properties)s.",
                  {'properties': properties})

        return {'driver_volume_type': 'iscsi', 'data': properties}
예제 #2
0
        def _loop_func():
            LOG.debug("Entering _wait_run_delete_lun_snapshot loop: "
                      "vol=%(vol)s, snap_id=%(snap_id)s, oid=%(oid)s",
                      {'vol': cinder_volume_id,
                       'oid': oid,
                       'snap_id': cinder_snapshot_id})

            try:
                ans = self.vmem_mg.snapshot.delete_lun_snapshot(
                    snapshot_object_id=oid)
            except Exception:
                msg = (_("Failed to delete snapshot "
                         "%(snap)s of volume %(vol)s") %
                       {'snap': cinder_snapshot_id, 'vol': cinder_volume_id})
                raise exception.ViolinBackendErr(msg)

            if ans['success']:
                LOG.debug("Delete snapshot %(snap_id)s of %(vol)s: "
                          "success", {'vol': cinder_volume_id,
                                      'snap_id': cinder_snapshot_id})
                raise loopingcall.LoopingCallDone(retvalue=True)
            else:
                LOG.warning("Delete snapshot %(snap)s of %(vol)s "
                            "encountered temporary error: %(msg)s",
                            {'snap': cinder_snapshot_id,
                             'vol': cinder_volume_id,
                             'msg': ans['msg']})
예제 #3
0
    def _unexport_lun(self, volume, target, connector):
        """Removes the export configuration for the given volume.

        The equivalent CLI command is "no lun export container
        <container_name> name <lun_name>"

        Arguments:
            volume -- volume object provided by the Manager
        """
        v = self.common.vmem_mg

        LOG.info(_LI("Unexporting lun %(vol)s host is %(host)s."), {
            'vol': volume['id'],
            'host': connector['host']
        })

        try:
            self.common._send_cmd(v.lun.unassign_lun_from_iscsi_target,
                                  "Unassign device successfully", volume['id'],
                                  target, True)

        except exception.ViolinBackendErrNotFound:
            LOG.info(_LI("Lun %s already unexported, continuing..."),
                     volume['id'])

        except Exception:
            LOG.exception(_LE("LUN unexport failed!"))
            msg = _("LUN unexport failed")
            raise exception.ViolinBackendErr(message=msg)
예제 #4
0
    def test_export_lun_fails_with_exception(self):
        lun_id = '1'
        response = {'status': False, 'msg': 'Generic error'}
        failure = exception.ViolinBackendErr

        self.driver.common.vmem_mg = self.setup_mock_concerto()
        self.driver.common._send_cmd_and_verify = mock.Mock(
            side_effect=exception.ViolinBackendErr(response['msg']))
        self.driver._get_lun_id = mock.Mock(return_value=lun_id)

        self.assertRaises(failure, self.driver._export_lun, VOLUME, CONNECTOR)
예제 #5
0
    def _create_lun(self, volume):
        """Creates a new lun.

        :param volume:  volume object provided by the Manager
        """
        spec_dict = {}
        selected_pool = {}

        size_mb = volume['size'] * units.Ki
        full_size_mb = size_mb

        LOG.debug("Creating LUN %(name)s, %(size)s MB.",
                  {'name': volume['name'], 'size': size_mb})

        spec_dict = self._process_extra_specs(volume)

        try:
            selected_pool = self._get_storage_pool(
                volume,
                size_mb,
                spec_dict['pool_type'],
                "create_lun")

        except exception.ViolinResourceNotFound:
            raise
        except Exception:
            msg = _('No suitable storage pool found')
            LOG.exception(msg)
            raise exception.ViolinBackendErr(message=msg)

        try:
            # Note: In the following create_lun command for setting up a dedup
            # or thin lun the size_mb parameter is ignored and 10% of the
            # full_size_mb specified is the size actually allocated to
            # the lun. full_size_mb is the size the lun is allowed to
            # grow. On the other hand, if it is a thick lun, the
            # full_size_mb is ignored and size_mb is the actual
            # allocated size of the lun.

            self._send_cmd(self.vmem_mg.lun.create_lun,
                           "Create resource successfully.",
                           volume['id'],
                           spec_dict['size_mb'],
                           selected_pool['dedup'],
                           selected_pool['thin'],
                           full_size_mb,
                           storage_pool_id=selected_pool['storage_pool_id'])

        except exception.ViolinBackendErrExists:
            LOG.debug("Lun %s already exists, continuing.", volume['id'])

        except Exception:
            LOG.exception("Lun create for %s failed!", volume['id'])
            raise
예제 #6
0
    def _validate_lun_type_for_copy(self, lun_type):
        """Make sure volume type is thick.

        :param lun_type:  Cinder volume type

        :raises:
            VolumeBackendAPIException: if volume type is not thick,
               copying the lun is not possible.
        """
        if lun_type != CONCERTO_LUN_TYPE_THICK:
            msg = _('Lun copy currently only supported for thick luns')
            LOG.error(msg)
            raise exception.ViolinBackendErr(message=msg)
예제 #7
0
    def test_extend_lun_new_size_is_too_small(self):
        """Volume extend fails when new size would shrink the volume."""
        new_volume_size = 0
        response = {'code': 14036, 'message': 'Failure'}

        conf = {
            'lun.resize_lun.return_value': response,
        }
        self.driver.vip = self.setup_mock_vshare(m_conf=conf)
        self.driver._send_cmd = mock.Mock(
            side_effect=exception.ViolinBackendErr(message='fail'))

        self.assertRaises(exception.ViolinBackendErr, self.driver._extend_lun,
                          VOLUME, new_volume_size)
예제 #8
0
    def _check_error_code(self, response):
        """Raise an exception when backend returns certain errors.

        Error codes returned from the backend have to be examined
        individually. Not all of them are fatal. For example, lun attach
        failing becase the client is already attached is not a fatal error.

        :param response: a response dict result from the vmemclient request
        """
        if "Error: 0x9001003c" in response['msg']:
            # This error indicates a duplicate attempt to attach lun,
            # non-fatal error
            pass
        elif "Error: 0x9002002b" in response['msg']:
            # lun unexport failed - lun is not exported to any clients,
            # non-fatal error
            pass
        elif "Error: 0x09010023" in response['msg']:
            # lun delete failed - dependent snapshot copy in progress,
            # fatal error
            raise exception.ViolinBackendErr(message=response['msg'])
        elif "Error: 0x09010048" in response['msg']:
            # lun delete failed - dependent snapshots still exist,
            # fatal error
            raise exception.ViolinBackendErr(message=response['msg'])
        elif "Error: 0x90010022" in response['msg']:
            # lun create failed - lun with same name already exists,
            # fatal error
            raise exception.ViolinBackendErrExists()
        elif "Error: 0x90010089" in response['msg']:
            # lun export failed - lun is still being created as copy,
            # fatal error
            raise exception.ViolinBackendErr(message=response['msg'])
        else:
            # assume any other error is fatal
            raise exception.ViolinBackendErr(message=response['msg'])
    def _get_storage_pool(self, volume, size_in_mb, pool_type, usage):
        # User-specified pool takes precedence over others

        pool = None
        typeid = volume['volume_type_id']
        if typeid:
            # Extract the storage_pool name if one is specified
            pool = self._get_violin_extra_spec(volume, "storage_pool")

            #Select a storage pool
        selected_pool = self.vmem_mg.pool.select_storage_pool(
            size_in_mb, pool_type, pool, self.config.dedup_only_pools,
            self.config.dedup_capable_pools,
            self.config.pool_allocation_method, usage)
        if (selected_pool is None):
            # Backend has not provided a suitable storage pool
            msg = _("Backend does not have a suitable storage pool.")
            raise exception.ViolinBackendErr(message=msg)

        LOG.debug("Storage pool returned is %s" %
                  selected_pool['storage_pool'])

        return selected_pool
예제 #10
0
    def _fatal_error_code(self, response):
        """Raise an exception for certain errors in a XG response.

        Error codes are extracted from vdmd_mgmt.c.

        Arguments:
            response -- a response dict result from an XG request
        """
        # known non-fatal response codes:
        # 1024: 'lun deletion in progress, try again later'
        # 14032: 'lc_err_lock_busy'

        if response['code'] == 14000:
            # lc_generic_error
            raise exception.ViolinBackendErr(message=response['message'])
        elif response['code'] == 14002:
            # lc_err_assertion_failed
            raise exception.ViolinBackendErr(message=response['message'])
        elif response['code'] == 14004:
            # lc_err_not_found
            raise exception.ViolinBackendErrNotFound()
        elif response['code'] == 14005:
            # lc_err_exists
            raise exception.ViolinBackendErrExists()
        elif response['code'] == 14008:
            # lc_err_unexpected_arg
            raise exception.ViolinBackendErr(message=response['message'])
        elif response['code'] == 14014:
            # lc_err_io_error
            raise exception.ViolinBackendErr(message=response['message'])
        elif response['code'] == 14016:
            # lc_err_io_closed
            raise exception.ViolinBackendErr(message=response['message'])
        elif response['code'] == 14017:
            # lc_err_io_timeout
            raise exception.ViolinBackendErr(message=response['message'])
        elif response['code'] == 14021:
            # lc_err_unexpected_case
            raise exception.ViolinBackendErr(message=response['message'])
        elif response['code'] == 14025:
            # lc_err_no_fs_space
            raise exception.ViolinBackendErr(message=response['message'])
        elif response['code'] == 14035:
            # lc_err_range
            raise exception.ViolinBackendErr(message=response['message'])
        elif response['code'] == 14036:
            # lc_err_invalid_param
            raise exception.ViolinBackendErr(message=response['message'])
        elif response['code'] == 14121:
            # lc_err_cancelled_err
            raise exception.ViolinBackendErr(message=response['message'])
        elif response['code'] == 512:
            # Not enough free space in container (vdmd bug)
            raise exception.ViolinBackendErr(message=response['message'])
        elif response['code'] == 1 and 'LUN ID conflict' \
                in response['message']:
            # lun id conflict while attempting to export
            raise exception.ViolinBackendErr(message=response['message'])
예제 #11
0
    def _ensure_snapshot_resource_area(self, volume_id):
        """Make sure concerto snapshot resource area exists on volume.

        :param volume_id:  Cinder volume ID corresponding to the backend LUN

        :raises:
            VolumeBackendAPIException: if cinder volume does not exist
               on backnd, or SRA could not be created.
        """
        ctxt = context.get_admin_context()
        volume = api.volume_get(ctxt, volume_id)
        spec_dict = {}

        if not volume:
            msg = (_("Failed to ensure snapshot resource area, could not "
                   "locate volume for id %s") % volume_id)
            raise exception.VolumeBackendAPIException(data=msg)

        if not self.vmem_mg.snapshot.lun_has_a_snapshot_resource(
           lun=volume_id):
            # Per Concerto documentation, the SRA size should be computed
            # as follows
            #  Size-of-original-LUN        Reserve for SRA
            #   < 500MB                    100%
            #   500MB to 2G                50%
            #   >= 2G                      20%
            # Note: cinder volume.size is in GB, vmemclient wants MB.
            lun_size_mb = volume['size'] * units.Ki
            if lun_size_mb < 500:
                snap_size_mb = lun_size_mb
            elif lun_size_mb < 2000:
                snap_size_mb = 0.5 * lun_size_mb
            else:
                snap_size_mb = 0.2 * lun_size_mb

            snap_size_mb = int(math.ceil(snap_size_mb))

            spec_dict = self._process_extra_specs(volume)

            try:
                selected_pool = self._get_storage_pool(
                    volume,
                    snap_size_mb,
                    spec_dict['pool_type'],
                    None)

                LOG.debug("Creating SRA of %(ssmb)sMB for lun of %(lsmb)sMB "
                          "on %(vol_id)s",
                          {'ssmb': snap_size_mb,
                           'lsmb': lun_size_mb,
                           'vol_id': volume_id})

            except exception.ViolinResourceNotFound:
                raise
            except Exception:
                msg = _('No suitable storage pool found')
                LOG.exception(msg)
                raise exception.ViolinBackendErr(message=msg)

            res = self.vmem_mg.snapshot.create_snapshot_resource(
                lun=volume_id,
                size=snap_size_mb,
                enable_notification=False,
                policy=CONCERTO_DEFAULT_SRA_POLICY,
                enable_expansion=CONCERTO_DEFAULT_SRA_ENABLE_EXPANSION,
                expansion_threshold=CONCERTO_DEFAULT_SRA_EXPANSION_THRESHOLD,
                expansion_increment=CONCERTO_DEFAULT_SRA_EXPANSION_INCREMENT,
                expansion_max_size=CONCERTO_DEFAULT_SRA_EXPANSION_MAX_SIZE,
                enable_shrink=CONCERTO_DEFAULT_SRA_ENABLE_SHRINK,
                storage_pool_id=selected_pool['storage_pool_id'])

            if (not res['success']):
                msg = (_("Failed to create snapshot resource area on "
                       "volume %(vol)s: %(res)s.") %
                       {'vol': volume_id, 'res': res['msg']})
                raise exception.VolumeBackendAPIException(data=msg)
예제 #12
0
    def _create_volume_from_snapshot(self, snapshot, volume):
        """Create a new cinder volume from a given snapshot of a lun

        This maps onto a Concerto 'copy  snapshot to lun'. Concerto
        creates the lun and then copies the snapshot into it.

        :param snapshot:  cinder snapshot object provided by the Manager
        :param volume:  cinder volume to be created
        """
        cinder_volume_id = volume['id']
        cinder_snapshot_id = snapshot['id']
        size_mb = volume['size'] * units.Ki
        result = None
        spec_dict = {}

        LOG.debug("Copying snapshot %(snap_id)s onto volume %(vol_id)s "
                  "%(dpy_name)s",
                  {'snap_id': cinder_snapshot_id,
                   'vol_id': cinder_volume_id,
                   'dpy_name': snapshot['display_name']})

        source_lun_info = self.vmem_mg.lun.get_lun_info(snapshot['volume_id'])
        self._validate_lun_type_for_copy(source_lun_info['subType'])

        spec_dict = self._process_extra_specs(volume)
        try:
            selected_pool = self._get_storage_pool(volume,
                                                   size_mb,
                                                   spec_dict['pool_type'],
                                                   "create_lun")

        except exception.ViolinResourceNotFound:
            raise
        except Exception:
            msg = _('No suitable storage pool found')
            LOG.exception(msg)
            raise exception.ViolinBackendErr(message=msg)

        try:
            result = self.vmem_mg.lun.copy_snapshot_to_new_lun(
                source_lun=snapshot['volume_id'],
                source_snapshot_comment=self._compress_snapshot_id(
                    cinder_snapshot_id),
                destination=cinder_volume_id,
                storage_pool_id=selected_pool['storage_pool_id'])

            if not result['success']:
                self._check_error_code(result)

        except Exception:
            LOG.exception("Copy snapshot to volume for "
                          "snapshot %(snap)s volume %(vol)s failed!",
                          {'snap': cinder_snapshot_id,
                           'vol': cinder_volume_id})
            raise

        # get the destination lun info and extract virtualdeviceid
        info = self.vmem_mg.lun.get_lun_info(object_id=result['object_id'])

        self._wait_for_lun_or_snap_copy(
            snapshot['volume_id'], dest_vdev_id=info['virtualDeviceID'])