Beispiel #1
0
    def DeleteVolume(self, request, context):
        Utils.validate_param_exists(request, 'volume_id')

        volume_id = request.volume_id
        nvmesh_vol_name = volume_id
        #secrets = request.secrets

        err, out = VolumeAPI().delete([NVMeshVolume(_id=nvmesh_vol_name)])
        if err:
            self.logger.error(err)
            raise DriverError(StatusCode.INTERNAL, err)

        self.logger.debug(out)

        if not out[0]['success']:
            err = out[0]['error']

            if err == "Couldn't find the specified volume":
                # Idempotency - Trying to remove a Volume that doesn't exists, perhaps already deleted
                # should return success
                pass
            else:
                raise DriverError(StatusCode.FAILED_PRECONDITION, err)

        return DeleteVolumeResponse()
Beispiel #2
0
    def ControllerExpandVolume(self, request, context):
        capacity_in_bytes = request.capacity_range.required_bytes
        nvmesh_vol_name = request.volume_id

        volume = self.get_nvmesh_volume(nvmesh_vol_name)

        # Call Node Expansion Method to Expand a FileSystem
        # For a Block Device there is no need to do anything on the node
        node_expansion_required = True if 'fsType' in volume.csi_metadata else False

        # Extend Volume
        volume.capacity = capacity_in_bytes

        self.logger.debug("ControllerExpandVolume volume={}".format(
            str(volume)))
        err, out = VolumeAPI().update([volume])

        if err:
            raise DriverError(StatusCode.NOT_FOUND, err)

        self.logger.debug(
            "ControllerExpandVolumeResponse: capacity_in_bytes={}, node_expansion_required={}"
            .format(capacity_in_bytes, node_expansion_required))
        return ControllerExpandVolumeResponse(
            capacity_bytes=capacity_in_bytes,
            node_expansion_required=node_expansion_required)
 def get_nvmesh_volume_by_name(nvmesh_vol_name):
     filter_obj = [MongoObj(field='_id', value=nvmesh_vol_name)]
     err, out = VolumeAPI(NVMESH_MGMT_ADDRESS).get(filter=filter_obj)
     if len(out) == 0:
         return None
     else:
         return out[0]
    def delete_all_nvmesh_volumes():
        projection = [MongoObj(field='_id', value=1)]
        err, volume_list = VolumeAPI(NVMESH_MGMT_ADDRESS).get(
            projection=projection)
        if len(volume_list) != 0:
            err, out = VolumeAPI(NVMESH_MGMT_ADDRESS).delete(
                [vol._id for vol in volume_list])
            if err:
                raise TestError(
                    'Failed to delete NVMesh volumes. Error: {}'.format(err))

            for vol_res in out:
                if not vol_res['success']:
                    if 'Couldn\'t find the specified volume' not in vol_res[
                            'error']:
                        raise TestError(
                            'Failed to delete NVMesh volume {}. Error: {}'.
                            format(vol_res['_id'], vol_res['error']))
Beispiel #5
0
    def _validate_volume_exists(self, nvmesh_vol_name):
        filterObj = [MongoObj(field='_id', value=nvmesh_vol_name)]
        projection = [MongoObj(field='_id', value=1)]

        err, out = VolumeAPI().get(filter=filterObj, projection=projection)
        if err or not len(out):
            raise DriverError(
                StatusCode.NOT_FOUND,
                'Could not find Volume with id {}'.format(nvmesh_vol_name))
Beispiel #6
0
 def _get_volume_by_name(self, volume_id):
     filterObj = [MongoObj(field='_id', value=volume_id)]
     err, data = VolumeAPI().get(filter=filterObj)
     if err:
         return err, data
     else:
         if not len(data):
             return "Could Not Find Volume {}".format(volume_id), None
         else:
             return None, data[0]
Beispiel #7
0
 def _create_new_volume_api(api_params):
     sdk_logger = logging.getLogger('NVMeshSDK')
     try:
         volume_api = VolumeAPI(logger=sdk_logger, **api_params)
         return volume_api
     except Exception as ex:
         sdk_logger.error(
             'Failed to create VolumeAPI with params: {}. \nError {}'.
             format(api_params, ex))
         raise
Beispiel #8
0
    def get_nvmesh_volume_by_name(nvmesh_vol_name, mgmt_addresses=None):
        if not mgmt_addresses:
            mgmt_addresses = TestConfig.ManagementServers

        for mgmt_address in mgmt_addresses:
            filter_obj = [MongoObj(field='_id', value=nvmesh_vol_name)]
            err, out = VolumeAPI(managementServers=mgmt_address).get(
                filter=filter_obj)
            if len(out) > 0:
                return out[0], mgmt_address

        return None, None
Beispiel #9
0
    def _get_nvmesh_volume_capacity(self, nvmesh_vol_name):
        filterObj = [MongoObj(field='_id', value=nvmesh_vol_name)]
        projection = [
            MongoObj(field='_id', value=1),
            MongoObj(field='capacity', value=1)
        ]

        err, out = VolumeAPI().get(filter=filterObj, projection=projection)
        if err or not len(out):
            raise DriverError(StatusCode.INTERNAL, err)

        return out[0]['capacity']
Beispiel #10
0
    def delete_all_nvmesh_volumes(mgmt_addresses=None):
        if not mgmt_addresses:
            mgmt_addresses = TestConfig.ManagementServers

        for mgmt_address in mgmt_addresses:
            projection = [MongoObj(field='_id', value=1)]
            err, volume_list = VolumeAPI(managementServers=mgmt_address).get(
                projection=projection)
            if len(volume_list) != 0:
                err, out = VolumeAPI(managementServers=mgmt_address).delete(
                    [vol._id for vol in volume_list])
                if err:
                    raise TestError(
                        'Failed to delete NVMesh volumes. Error: {}'.format(
                            err))

                for vol_res in out:
                    if not vol_res['success']:
                        if 'Couldn\'t find the specified volume' not in vol_res[
                                'error']:
                            raise TestError(
                                'Failed to delete NVMesh volume {}. Error: {}'.
                                format(vol_res['_id'], vol_res['error']))
Beispiel #11
0
	def _try_to_connect_to_single_management(logger):
		protocol = Config.MANAGEMENT_PROTOCOL
		managementServers = Config.MANAGEMENT_SERVERS
		user = Config.MANAGEMENT_USERNAME
		password = Config.MANAGEMENT_PASSWORD

		# This call will try to connect to the Management Server Instance
		api = VolumeAPI(managementServers=managementServers, managementProtocol=protocol, user=user, password=password, logger=logger)
		err, connected = api.managementConnection.get('/isAlive')

		if err:
			raise ValueError(err)

		return api, connected
Beispiel #12
0
    def get_nvmesh_volume(self, nvmesh_vol_name, minimalFields=False):
        filterObj = [MongoObj(field='_id', value=nvmesh_vol_name)]

        projection = None
        if minimalFields:
            projection = [
                MongoObj(field='_id', value=1),
                MongoObj(field='capacity', value=1),
                MongoObj(field='status', value=1),
                MongoObj(field='csi_metadata', value=1)
            ]

        err, out = VolumeAPI().get(filter=filterObj, projection=projection)
        if err:
            raise DriverError(StatusCode.INTERNAL, err)
        if not isinstance(out, list):
            raise DriverError(StatusCode.INTERNAL, out)
        if not len(out):
            raise DriverError(
                StatusCode.NOT_FOUND,
                'Volume {} Could not be found'.format(nvmesh_vol_name))

        return out[0]
Beispiel #13
0
    def ListVolumes(self, request, context):
        max_entries = request.max_entries
        starting_token = request.starting_token

        try:
            page = int(starting_token or 0)
        except ValueError:
            raise DriverError(StatusCode.ABORTED, "Invalid starting_token")

        if starting_token and not max_entries:
            raise DriverError(StatusCode.ABORTED, "Invalid starting_token")

        count = max_entries or 0

        projection = [
            MongoObj(field='_id', value=1),
            MongoObj(field='capacity', value=1)
        ]

        err, nvmeshVolumes = VolumeAPI().get(projection=projection,
                                             page=page,
                                             count=count)

        if err:
            raise DriverError(StatusCode.INTERNAL, err)

        def convertNVMeshVolumeToCSIVolume(volume):
            vol = Volume(volume_id=volume._id, capacity_bytes=volume.capacity)
            return ListVolumesResponse.Entry(volume=vol)

        entries = map(convertNVMeshVolumeToCSIVolume, nvmeshVolumes)
        next_token = str(page + 1)

        if not len(entries):
            DriverError(StatusCode.ABORTED, "No more Entries")

        return ListVolumesResponse(entries=entries, next_token=next_token)
 def getVolumeAPI():
     return VolumeAPI(NVMESH_MGMT_ADDRESS)
Beispiel #15
0
    def test_scale(self):

        os.environ['DEVELOPMENT'] = 'TRUE'

        self._delete_topology_config_map_if_exists()

        clusters = self._create_multiple_clusters(num_of_clusters=15,
                                                  num_of_client_per_cluster=5)

        config_topology = get_config_topology_from_cluster_list(clusters)

        config = {
            'TOPOLOGY_TYPE': Consts.TopologyType.MULTIPLE_NVMESH_CLUSTERS,
            'TOPOLOGY': config_topology,
            'LOG_LEVEL': 'DEBUG',
            'SDK_LOG_LEVEL': 'DEBUG'
        }
        import driver.config as Config
        Config.config_loader = ConfigLoaderMock(config)
        Config.config_loader.load()

        self._start_csi_controller(config)

        log.debug('Creating volumes')
        ctrl_client = ControllerClient()

        parameters = {'vpg': 'DEFAULT_CONCATENATED_VPG'}

        expected_volumes = {}

        def create_volume_for_specific_zone(zone_name):
            log.debug('create_volume_for_specific_zone %s' % zone_name)
            allowed_zones = Topology(
                segments={Consts.TopologyKey.ZONE: zone_name})
            allowed_topologies = TopologyRequirement(requisite=[allowed_zones],
                                                     preferred=[allowed_zones])

            response = ctrl_client.CreateVolume(
                name='vol_zone_%s' % zone_name,
                capacity_in_bytes=5 * GB,
                parameters=parameters,
                topology_requirements=allowed_topologies)

            volume_id = response.volume.volume_id
            self.assertTrue(volume_id)

            if not zone_name in expected_volumes:
                expected_volumes[zone_name] = []

            volume_id_without_zone = volume_id.split(':')[1]
            expected_volumes[zone_name].append(volume_id_without_zone)

        for cluster in clusters:
            create_volume_for_specific_zone(cluster.name)

        # Verify volumes created in the correct zones
        volumes_from_clusters = {}
        for cluster in clusters:
            # Fetch volumes from the mgmt server and compare
            mgmt_srv = cluster.get_mgmt_server_string()
            api = VolumeAPI(managementServers=mgmt_srv,
                            managementProtocol='https')
            err, volumes = api.get()
            volume_ids = [v._id for v in volumes]
            volumes_from_clusters[cluster.name] = volume_ids

        for cluster in clusters:
            expected_volume_ids = expected_volumes[cluster.name]
            actual_volumes = volumes_from_clusters[cluster.name]
            self.assertItemsEqual(
                expected_volume_ids, actual_volumes,
                'expected: {}\n\nfound: {}'.format(
                    pretty_json(expected_volumes),
                    pretty_json(volumes_from_clusters)))

        log.info('test finished successfully')
Beispiel #16
0
    def CreateVolume(self, request, context):
        Utils.validate_param_exists(request, 'name')
        name = request.name
        capacity = self._parse_required_capacity(request.capacity_range)
        parameters = request.parameters

        #UNUSED - secrets = request.secrets
        #UNUSED - volume_content_source = request.volume_content_source
        #UNUSED - accessibility_requirements = request.accessibility_requirements

        reqJson = MessageToJson(request)
        self.logger.debug('create volume request: {}'.format(reqJson))
        reqDict = MessageToDict(request)
        capabilities = reqDict['volumeCapabilities']

        is_file_system = False
        is_block_device = False

        csi_metadata = {'csi_name': name, 'capabilities': capabilities}

        for capability in capabilities:
            if 'mount' in capability:
                is_file_system = True
                csi_metadata['fsType'] = capability['mount']['fsType']
            else:
                csi_metadata['block'] = True

            access_mode = capability['accessMode']['mode']
            if Consts.AccessMode.fromCsiString(
                    access_mode) not in Consts.AccessMode.allowed_access_modes(
                    ):
                self.logger.warning(
                    'Requested mode {} is not enforced by NVMesh Storage backend'
                    .format(access_mode))

        if is_file_system and is_block_device:
            raise DriverError(
                StatusCode.INVALID_ARGUMENT,
                'Error: Contradicting capabilities both Block Volume and FileSystem Volume were requested for volume {}. request: {}'
                .format(name, reqJson))

        nvmesh_vol_name = Utils.volume_id_to_nvmesh_name(name)
        nvmesh_params = {}

        self.logger.debug('create volume parameters: {}'.format(parameters))

        if 'vpg' in parameters:
            self.logger.debug('Creating Volume from VPG {}'.format(
                parameters['vpg']))
            nvmesh_params['VPG'] = parameters['vpg']

            # This is a workaround since the nvmesh create volume api expects a 'RAIDLevel'
            # but if 'VPG' is present 'RAIDLevel' field will be ignored
            # and the RAIDLevel will be fetched from the VPG.
            nvmesh_params['RAIDLevel'] = RAIDLevels.CONCATENATED
        else:
            self.logger.debug('Creating without VPG')
            for param in parameters:
                nvmesh_params[param] = parameters[param]

            self._handle_non_vpg_params(nvmesh_params)

        self.logger.debug('nvmesh_params = {}'.format(nvmesh_params))

        volume = NVMeshVolume(name=nvmesh_vol_name,
                              capacity=capacity,
                              csi_metadata=csi_metadata,
                              **nvmesh_params)

        self.logger.debug('Creating volume: {}'.format(str(volume)))
        err, data = VolumeAPI().save([volume])

        if err:
            raise DriverError(
                StatusCode.RESOURCE_EXHAUSTED,
                'Error: {} Details: {} Volume Requested: {}'.format(
                    err, data, str(volume)))
        elif not type(data) == list or not data[0]['success']:
            if 'Name already Exists' in data[0]['error']:
                existing_capacity = self._get_nvmesh_volume_capacity(
                    nvmesh_vol_name)
                if capacity == existing_capacity:
                    # Idempotency - same Name same Capacity - return success
                    pass
                else:
                    raise DriverError(
                        StatusCode.ALREADY_EXISTS,
                        'Error: {} Details: {}'.format(err, data))
            else:
                raise DriverError(StatusCode.RESOURCE_EXHAUSTED,
                                  'Error: {} Details: {}'.format(err, data))

        err, details = self._wait_for_volume_status(
            volume._id, NVMeshConsts.VolumeStatuses.ONLINE)

        if err:
            if err == 'Timed out Waiting for Volume to be Online':
                raise DriverError(StatusCode.FAILED_PRECONDITION,
                                  'Error: {} Details: {}'.format(err, details))
            else:
                raise DriverError(StatusCode.INVALID_ARGUMENT, err)
        else:
            self.logger.debug(details)

        # we return the nvmesh_vol_name that we created to the CO
        # all subsequent requests for this volume will have volume_id of the nvmesh volume name
        csiVolume = Volume(volume_id=nvmesh_vol_name, capacity_bytes=capacity)
        return CreateVolumeResponse(volume=csiVolume)
Beispiel #17
0
 def getVolumeAPI(mgmt_address=TestConfig.ManagementServers[0]):
     return VolumeAPI(managementServers=mgmt_address)