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()
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']))
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))
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]
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
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
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']
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']))
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
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]
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)
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')
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)
def getVolumeAPI(mgmt_address=TestConfig.ManagementServers[0]): return VolumeAPI(managementServers=mgmt_address)