Beispiel #1
0
    def manage_existing(self, volume, external_ref):
        """Manages an existing SolidFire Volume (import to Cinder).

        Renames the Volume to match the expected name for the volume.
        Also need to consider things like QoS, Emulation, account/tenant.
        """

        sfid = external_ref.get('source-id', None)
        sfname = external_ref.get('name', None)
        if sfid is None:
            raise exception.SolidFireAPIException("Manage existing volume "
                                                  "requires 'source-id'.")

        # First get the volume on the SF cluster (MUST be active)
        params = {'startVolumeID': sfid, 'limit': 1}
        data = self._issue_api_request('ListActiveVolumes', params)
        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)
        sf_ref = data['result']['volumes'][0]

        sfaccount = self._create_sfaccount(volume['project_id'])

        attributes = {}
        qos = {}
        if (self.configuration.sf_allow_tenant_qos
                and volume.get('volume_metadata') is not None):
            qos = self._set_qos_presets(volume)

        ctxt = context.get_admin_context()
        type_id = volume.get('volume_type_id', None)
        if type_id is not None:
            qos = self._set_qos_by_volume_type(ctxt, type_id)

        import_time = timeutils.strtime(volume['created_at'])
        attributes = {
            'uuid': volume['id'],
            'is_clone': 'False',
            'os_imported_at': import_time,
            'old_name': sfname
        }
        if qos:
            for k, v in qos.items():
                attributes[k] = str(v)

        params = {
            'name': volume['name'],
            'volumeID': sf_ref['volumeID'],
            'accountID': sfaccount['accountID'],
            'enable512e': self.configuration.sf_emulate_512,
            'attributes': attributes,
            'qos': qos
        }

        data = self._issue_api_request('ModifyVolume', params, version='5.0')
        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)

        return self._get_model_info(sfaccount, sf_ref['volumeID'])
Beispiel #2
0
    def _do_clone_volume(self, src_uuid, src_project_id, v_ref):
        """Create a clone of an existing volume.

        Currently snapshots are the same as clones on the SF cluster.
        Due to the way the SF cluster works there's no loss in efficiency
        or space usage between the two.  The only thing different right
        now is the restore snapshot functionality which has not been
        implemented in the pre-release version of the SolidFire Cluster.

        """
        attributes = {}
        qos = {}

        sfaccount = self._get_sfaccount(src_project_id)
        params = {'accountID': sfaccount['accountID']}

        sf_vol = self._get_sf_volume(src_uuid, params)
        if sf_vol is None:
            raise exception.VolumeNotFound(volume_id=src_uuid)

        if src_project_id != v_ref['project_id']:
            sfaccount = self._create_sfaccount(v_ref['project_id'])

        if 'qos' in sf_vol:
            qos = sf_vol['qos']

        attributes = {
            'uuid': v_ref['id'],
            'is_clone': 'True',
            'src_uuid': src_uuid
        }

        if qos:
            for k, v in qos.items():
                attributes[k] = str(v)

        params = {
            'volumeID': int(sf_vol['volumeID']),
            'name': 'UUID-%s' % v_ref['id'],
            'attributes': attributes,
            'newAccountID': sfaccount['accountID'],
            'qos': qos
        }

        data = self._issue_api_request('CloneVolume', params)

        if (('result' not in data) or ('volumeID' not in data['result'])):
            raise exception.SolidFireAPIDataException(data=data)

        sf_volume_id = data['result']['volumeID']
        model_update = self._get_model_info(sfaccount, sf_volume_id)
        if model_update is None:
            mesg = _('Failed to get model update from clone')
            raise exception.SolidFireAPIDataException(mesg)

        return (data, sfaccount, model_update)
Beispiel #3
0
    def _get_cluster_info(self):
        params = {}
        data = self._issue_api_request('GetClusterInfo', params)
        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)

        return data['result']
Beispiel #4
0
    def _do_volume_create(self, project_id, params):
        cluster_info = self._get_cluster_info()
        iscsi_portal = cluster_info['clusterInfo']['svip'] + ':3260'
        sfaccount = self._create_sfaccount(project_id)
        chap_secret = sfaccount['targetSecret']

        params['accountID'] = sfaccount['accountID']
        data = self._issue_api_request('CreateVolume', params)

        if 'result' not in data or 'volumeID' not in data['result']:
            raise exception.SolidFireAPIDataException(data=data)

        volume_id = data['result']['volumeID']

        volume_list = self._get_volumes_by_sfaccount(sfaccount['accountID'])
        iqn = None
        for v in volume_list:
            if v['volumeID'] == volume_id:
                iqn = v['iqn']
                break

        model_update = {}

        # NOTE(john-griffith): SF volumes are always at lun 0
        model_update['provider_location'] = ('%s %s %s' %
                                             (iscsi_portal, iqn, 0))
        model_update['provider_auth'] = ('CHAP %s %s' %
                                         (sfaccount['username'], chap_secret))

        return model_update
Beispiel #5
0
    def create_volume_from_snapshot(self, volume, snapshot):
        cluster_info = self._get_cluster_info()
        iscsi_portal = cluster_info['clusterInfo']['svip'] + ':3260'
        sfaccount = self._create_sfaccount(snapshot['project_id'])
        chap_secret = sfaccount['targetSecret']
        snapshot_name = 'OS-VOLID-%s' % volume['id']

        (data, sf_account) = self._do_create_snapshot(snapshot, snapshot_name)

        if 'result' not in data or 'volumeID' not in data['result']:
            raise exception.SolidFireAPIDataException(data=data)

        volume_id = data['result']['volumeID']
        volume_list = self._get_volumes_by_sfaccount(sf_account['accountID'])
        iqn = None
        for v in volume_list:
            if v['volumeID'] == volume_id:
                iqn = v['iqn']
                break

        model_update = {}

        # NOTE(john-griffith): SF volumes are always at lun 0
        model_update['provider_location'] = ('%s %s %s' %
                                             (iscsi_portal, iqn, 0))
        model_update['provider_auth'] = ('CHAP %s %s' %
                                         (sfaccount['username'], chap_secret))
        return model_update
Beispiel #6
0
    def accept_transfer(self, context, volume,
                        new_user, new_project):

        sfaccount = self._get_sfaccount(volume['project_id'])
        params = {'accountID': sfaccount['accountID']}
        sf_vol = self._get_sf_volume(volume['id'], params)
        if sf_vol is None:
            LOG.error(_LE("Volume ID %s was not found on "
                          "the SolidFire Cluster while attempting "
                          "accept_transfer operation!"), volume['id'])
            raise exception.VolumeNotFound(volume_id=volume['id'])
        if new_project != volume['project_id']:
            # do a create_sfaccount here as this tenant
            # may not exist on the cluster yet
            sfaccount = self._create_sfaccount(new_project)

        params = {
            'volumeID': sf_vol['volumeID'],
            'accountID': sfaccount['accountID']
        }
        data = self._issue_api_request('ModifyVolume',
                                       params, version='5.0')

        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)

        volume['project_id'] = new_project
        volume['user_id'] = new_user
        model_update = self._do_export(volume)
        LOG.debug("Leaving SolidFire transfer volume")
        return model_update
Beispiel #7
0
    def detach_volume(self, context, volume):

        LOG.debug("Entering SolidFire attach_volume...")
        sfaccount = self._get_sfaccount(volume['project_id'])
        params = {'accountID': sfaccount['accountID']}

        sf_vol = self._get_sf_volume(volume['id'], params)
        if sf_vol is None:
            LOG.error(_LE("Volume ID %s was not found on "
                          "the SolidFire Cluster while attempting "
                          "detach_volume operation!"), volume['id'])
            raise exception.VolumeNotFound(volume_id=volume['id'])

        attributes = sf_vol['attributes']
        attributes['attach_time'] = None
        attributes['attached_to'] = None
        params = {
            'volumeID': sf_vol['volumeID'],
            'attributes': attributes
        }

        data = self._issue_api_request('ModifyVolume', params)

        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)
Beispiel #8
0
    def attach_volume(self, context, volume,
                      instance_uuid, host_name,
                      mountpoint):

        LOG.debug(_("Entering SolidFire attach_volume..."))
        sfaccount = self._get_sfaccount(volume['project_id'])
        params = {'accountID': sfaccount['accountID']}

        sf_vol = self._get_sf_volume(volume['id'], params)
        if sf_vol is None:
            LOG.error(_("Volume ID %s was not found on "
                        "the SolidFire Cluster!"), volume['id'])
            raise exception.VolumeNotFound(volume_id=volume['id'])

        attributes = sf_vol['attributes']
        attributes['attach_time'] = volume.get('attach_time', None)
        attributes['attached_to'] = instance_uuid
        params = {
            'volumeID': sf_vol['volumeID'],
            'attributes': attributes
        }

        data = self._issue_api_request('ModifyVolume', params)

        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)
Beispiel #9
0
    def delete_volume(self, volume):
        """Delete SolidFire Volume from device.

        SolidFire allows multipe volumes with same name,
        volumeID is what's guaranteed unique.

        """

        LOG.debug(_("Enter SolidFire delete_volume..."))

        sfaccount = self._get_sfaccount(volume['project_id'])
        if sfaccount is None:
            LOG.error(_("Account for Volume ID %s was not found on "
                        "the SolidFire Cluster!") % volume['id'])
            LOG.error(_("This usually means the volume was never "
                        "succesfully created."))
            return

        params = {'accountID': sfaccount['accountID']}

        sf_vol = self._get_sf_volume(volume['id'], params)

        if sf_vol is not None:
            params = {'volumeID': sf_vol['volumeID']}
            data = self._issue_api_request('DeleteVolume', params)

            if 'result' not in data:
                raise exception.SolidFireAPIDataException(data=data)
        else:
            LOG.error(_("Volume ID %s was not found on "
                        "the SolidFire Cluster!"), volume['id'])

        LOG.debug(_("Leaving SolidFire delete_volume"))
Beispiel #10
0
    def unmanage(self, volume):
        """Mark SolidFire Volume as unmanaged (export from Cinder)."""

        LOG.debug("Enter SolidFire unmanage...")
        sfaccount = self._get_sfaccount(volume['project_id'])
        if sfaccount is None:
            LOG.error(_LE("Account for Volume ID %s was not found on "
                          "the SolidFire Cluster while attempting "
                          "unmanage operation!") % volume['id'])
            raise exception.SolidFireAPIException("Failed to find account "
                                                  "for volume.")

        params = {'accountID': sfaccount['accountID']}
        sf_vol = self._get_sf_volume(volume['id'], params)
        if sf_vol is None:
            raise exception.VolumeNotFound(volume_id=volume['id'])

        export_time = timeutils.strtime()
        attributes = sf_vol['attributes']
        attributes['os_exported_at'] = export_time
        params = {'volumeID': int(sf_vol['volumeID']),
                  'attributes': attributes}

        data = self._issue_api_request('ModifyVolume',
                                       params, version='5.0')
        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)
Beispiel #11
0
    def extend_volume(self, volume, new_size):
        """Extend an existing volume."""
        LOG.debug(_("Entering SolidFire extend_volume..."))

        sfaccount = self._get_sfaccount(volume['project_id'])
        params = {'accountID': sfaccount['accountID']}

        sf_vol = self._get_sf_volume(volume['id'], params)

        if sf_vol is None:
            LOG.error(_("Volume ID %s was not found on "
                        "the SolidFire Cluster!"), volume['id'])
            raise exception.VolumeNotFound(volume_id=volume['id'])

        params = {
            'volumeID': sf_vol['volumeID'],
            'totalSize': int(new_size * self.GB)
        }
        data = self._issue_api_request('ModifyVolume',
                                       params, version='5.0')

        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)

        LOG.debug(_("Leaving SolidFire extend_volume"))
Beispiel #12
0
    def _get_sf_volume(self, uuid, params):
        data = self._issue_api_request('ListVolumesForAccount', params)
        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)

        found_count = 0
        sf_volref = None
        for v in data['result']['volumes']:
            if uuid in v['name']:
                found_count += 1
                sf_volref = v
                LOG.debug(_("Mapped SolidFire volumeID %(sfid)s "
                            "to cinder ID %(uuid)s.") %
                          {'sfid': v['volumeID'],
                           'uuid': uuid})

        if found_count == 0:
            # NOTE(jdg): Previously we would raise here, but there are cases
            # where this might be a cleanup for a failed delete.
            # Until we get better states we'll just log an error
            LOG.error(_("Volume %s, not found on SF Cluster."), uuid)

        if found_count > 1:
            LOG.error(_("Found %(count)s volumes mapped to id: %(uuid)s.") %
                      {'count': found_count,
                       'uuid': uuid})
            raise exception.DuplicateSfVolumeNames(vol_name=uuid)

        return sf_volref
Beispiel #13
0
    def _get_cluster_info(self):
        """Query the SolidFire cluster for some property info."""
        params = {}
        data = self._issue_api_request('GetClusterInfo', params)
        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)

        return data['result']
Beispiel #14
0
    def delete_volume(self, volume, is_snapshot=False):
        """Delete SolidFire Volume from device.

        SolidFire allows multipe volumes with same name,
        volumeID is what's guaranteed unique.

        """

        LOG.debug(_("Enter SolidFire delete_volume..."))
        sf_account_name = socket.gethostname() + '-' + volume['project_id']
        sfaccount = self._get_sfaccount_by_name(sf_account_name)
        if sfaccount is None:
            raise exception.SfAccountNotFound(account_name=sf_account_name)

        params = {'accountID': sfaccount['accountID']}
        data = self._issue_api_request('ListVolumesForAccount', params)
        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)

        if is_snapshot:
            seek = 'OS-SNAPID-%s' % (volume['id'])
        else:
            seek = 'OS-VOLID-%s' % volume['id']
            #params = {'name': 'OS-VOLID-:%s' % volume['id'],

        found_count = 0
        volid = -1
        for v in data['result']['volumes']:
            if v['name'] == seek:
                found_count += 1
                volid = v['volumeID']

        if found_count == 0:
            raise exception.VolumeNotFound(volume_id=volume['id'])

        if found_count > 1:
            LOG.debug(_("Deleting volumeID: %s"), volid)
            raise exception.DuplicateSfVolumeNames(vol_name=volume['id'])

        params = {'volumeID': volid}
        data = self._issue_api_request('DeleteVolume', params)
        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)

        LOG.debug(_("Leaving SolidFire delete_volume"))
Beispiel #15
0
    def _do_volume_create(self, project_id, params):
        sfaccount = self._create_sfaccount(project_id)

        params['accountID'] = sfaccount['accountID']
        data = self._issue_api_request('CreateVolume', params)

        if (('result' not in data) or ('volumeID' not in data['result'])):
            raise exception.SolidFireAPIDataException(data=data)

        sf_volume_id = data['result']['volumeID']
        return self._get_model_info(sfaccount, sf_volume_id)
Beispiel #16
0
    def _do_create_snapshot(self, snapshot, snapshot_name):
        """Creates a snapshot."""
        LOG.debug(_("Enter SolidFire create_snapshot..."))
        sf_account_name = socket.gethostname() + '-' + snapshot['project_id']
        sfaccount = self._get_sfaccount_by_name(sf_account_name)
        if sfaccount is None:
            raise exception.SfAccountNotFound(account_name=sf_account_name)

        params = {'accountID': sfaccount['accountID']}
        data = self._issue_api_request('ListVolumesForAccount', params)
        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)

        found_count = 0
        volid = -1
        for v in data['result']['volumes']:
            if v['name'] == 'OS-VOLID-%s' % snapshot['volume_id']:
                found_count += 1
                volid = v['volumeID']

        if found_count == 0:
            raise exception.VolumeNotFound(volume_id=snapshot['volume_id'])
        if found_count != 1:
            raise exception.DuplicateSfVolumeNames(vol_name='OS-VOLID-%s' %
                                                   snapshot['volume_id'])

        params = {
            'volumeID': int(volid),
            'name': snapshot_name,
            'attributes': {
                'OriginatingVolume': volid
            }
        }

        data = self._issue_api_request('CloneVolume', params)
        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)

        return (data, sfaccount)
Beispiel #17
0
    def manage_existing_get_size(self, volume, external_ref):
        """Return size of an existing LV for manage_existing.

        existing_ref is a dictionary of the form:
        {'name': <name of existing volume on SF Cluster>}
        """

        sfid = external_ref.get('source-id', None)
        if sfid is None:
            raise exception.SolidFireAPIException("Manage existing get size "
                                                  "requires 'id'.")

        params = {'startVolumeID': int(sfid), 'limit': 1}
        data = self._issue_api_request('ListActiveVolumes', params)
        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)
        sf_ref = data['result']['volumes'][0]
        return int(sf_ref['totalSize']) / int(units.Gi)
Beispiel #18
0
    def accept_transfer(self, context, volume, new_user, new_project):

        sfaccount = self._get_sfaccount(volume['project_id'])
        params = {'accountID': sfaccount['accountID']}
        sf_vol = self._get_sf_volume(volume['id'], params)

        if new_project != volume['project_id']:
            # do a create_sfaccount here as this tenant
            # may not exist on the cluster yet
            sfaccount = self._create_sfaccount(new_project)

        params = {
            'volumeID': sf_vol['volumeID'],
            'accountID': sfaccount['accountID']
        }
        data = self._issue_api_request('ModifyVolume', params, version='5.0')

        if 'result' not in data:
            raise exception.SolidFireAPIDataException(data=data)

        LOG.debug(_("Leaving SolidFire transfer volume"))
    def _do_clone_volume(self, src_uuid, src_project_id, v_ref):
        """Create a clone of an existing volume.

        Currently snapshots are the same as clones on the SF cluster.
        Due to the way the SF cluster works there's no loss in efficiency
        or space usage between the two.  The only thing different right
        now is the restore snapshot functionality which has not been
        implemented in the pre-release version of the SolidFire Cluster.

        """
        attributes = {}
        qos = {}

        sfaccount = self._get_sfaccount(src_project_id)
        params = {'accountID': sfaccount['accountID']}

        sf_vol = self._get_sf_volume(src_uuid, params)
        if sf_vol is None:
            raise exception.VolumeNotFound(volume_id=src_uuid)

        if src_project_id != v_ref['project_id']:
            sfaccount = self._create_sfaccount(v_ref['project_id'])

        params = {
            'volumeID': int(sf_vol['volumeID']),
            'name': 'UUID-%s' % v_ref['id'],
            'newAccountID': sfaccount['accountID']
        }
        data = self._issue_api_request('CloneVolume', params)

        if (('result' not in data) or ('volumeID' not in data['result'])):
            raise exception.SolidFireAPIDataException(data=data)
        sf_volume_id = data['result']['volumeID']

        if (self.configuration.sf_allow_tenant_qos
                and v_ref.get('volume_metadata') is not None):
            qos = self._set_qos_presets(v_ref)

        ctxt = context.get_admin_context()
        type_id = v_ref['volume_type_id']
        if type_id is not None:
            qos = self._set_qos_by_volume_type(ctxt, type_id)

        # NOTE(jdg): all attributes are copied via clone, need to do an update
        # to set any that were provided
        params = {'volumeID': sf_volume_id}

        attributes = {
            'uuid': v_ref['id'],
            'is_clone': 'True',
            'src_uuid': src_uuid
        }
        if qos:
            params['qos'] = qos
            for k, v in qos.items():
                attributes[k] = str(v)

        params['attributes'] = attributes
        data = self._issue_api_request('ModifyVolume', params)

        model_update = self._get_model_info(sfaccount, sf_volume_id)
        if model_update is None:
            mesg = _('Failed to get model update from clone')
            raise exception.SolidFireAPIDataException(mesg)

        return (data, sfaccount, model_update)