def disconnect_volume(self, connection_properties, device_info): client_id = connection_properties.get('volume', None) if client_id is None: raise exception.BrickException(_LE( 'Invalid StorPool connection data, no client ID specified')) volume_id = connection_properties.get('volume', None) if volume_id is None: raise exception.BrickException(_LE( 'Invalid StorPool connection data, no volume ID specified')) volume = self._attach.volumeName(volume_id) req_id = 'brick-%s-%s' % (client_id, volume_id) self._attach.sync(req_id, volume) self._attach.remove(req_id)
def _get_volume_id(self): """ Return the current volume contexts ScaleIO unique volume identifier :return: ScaleIO volume ID """ volname_encoded = urllib.parse.quote(self.volume_name, '') volname_double_encoded = urllib.parse.quote(volname_encoded, '') LOG.debug(_("Volume name after double encoding is %(volume_name)s."), {'volume_name': volname_double_encoded}) request = ( "https://%(server_ip)s:%(server_port)s/api/types/Volume/instances" "/getByName::%(encoded_volume_name)s" % { 'server_ip': self.server_ip, 'server_port': self.server_port, 'encoded_volume_name': volname_double_encoded }) LOG.info(_LE("ScaleIO get volume id by name request: %(request)s"), {'request': request}) r = requests.get(request, auth=(self.server_username, self.server_token), verify=False) r = self._check_response(r, request) volume_id = r.json() if not volume_id: msg = (_("Volume with name %(volume_name)s wasn't found.") % { 'volume_name': self.volume_name }) LOG.error(msg) raise exception.BrickException(message=msg) if r.status_code != self.OK_STATUS_CODE and "errorCode" in volume_id: msg = (_("Error getting volume id from name %(volume_name)s: " "%(err)s") % { 'volume_name': self.volume_name, 'err': volume_id['message'] }) LOG.error(msg) raise exception.BrickException(message=msg) LOG.info(_LE("ScaleIO volume id is %(volume_id)s."), {'volume_id': volume_id}) return volume_id
def _wait_for_volume_path(self, path): """ Wait for mapped volume to appear in path. There is a delay between the time the ScaleIO volume is mapped and it appearing in the disk device path :param path: Path to search for volume in :return: Full mapped disk path """ disk_filename = None tries = 0 if not os.path.isdir(path): msg = (_("ScaleIO volume %(volume_id)s not found at " "expected path.") % { 'volume_id': self.volume_id }) LOG.debug(msg) raise exception.BrickException(message=msg) while not disk_filename: if tries > self.device_scan_attempts: msg = (_("ScaleIO volume %(volume_id)s not found.") % { 'volume_id': self.volume_id }) LOG.debug(msg) raise exception.BrickException(message=msg) filenames = os.listdir(path) LOG.debug(_LE("Files found in %(path)s path: %(files)s "), { 'path': path, 'files': filenames }) for filename in filenames: if (filename.startswith("emc-vol") and filename.endswith(self.volume_id)): disk_filename = filename if not disk_filename: LOG.warn("ScaleIO volume {0} not yet " "found. Try number: {1} ".format( self.volume_id, tries)) tries = tries + 1 time.sleep(1) else: LOG.info(_LE("ScaleIO volume %(volume_id)s " "found!"), {'volume_id': self.volume_id}) return disk_filename
def __init__(self, root_helper, driver=None, execute=putils.execute, *args, **kwargs): self._execute = execute self._root_helper = root_helper try: from storpool import spopenstack except ImportError: raise exception.BrickException(_LE( 'Could not import the StorPool API bindings')) try: self._attach = spopenstack.AttachDB(log=LOG) self._attach.api().attachmentsList() except Exception as e: raise exception.BrickException(_LE( 'Could not initialize the StorPool API bindings: %s') % (e))
def _mount_nfs(self, nfs_share, mount_path, flags=None): """Mount nfs share using present mount types.""" mnt_errors = {} # This loop allows us to first try to mount with NFS 4.1 for pNFS # support but falls back to mount NFS 4 or NFS 3 if either the client # or server do not support it. for mnt_type in sorted(self._nfs_mount_type_opts.keys(), reverse=True): options = self._nfs_mount_type_opts[mnt_type] try: self._do_mount('nfs', nfs_share, mount_path, options, flags) LOG.debug('Mounted %(sh)s using %(mnt_type)s.' % { 'sh': nfs_share, 'mnt_type': mnt_type }) return except Exception as e: mnt_errors[mnt_type] = six.text_type(e) LOG.debug('Failed to do %s mount.', mnt_type) raise exception.BrickException( _("NFS mount failed for share %(sh)s. " "Error - %(error)s") % { 'sh': nfs_share, 'error': mnt_errors })
def connect_volume(self, connection_properties): """Connect to a volume.""" LOG.debug("Connect_volume connection properties: %s." % connection_properties) out = self._attach_volume(connection_properties['volume_id']) if not out or int(out['ret_code']) not in (self.attached_success_code, self.has_been_attached_code, self.attach_mnid_done_code): msg = (_("Attach volume failed, " "error code is %s") % out['ret_code']) raise exception.BrickException(msg=msg) out = self._query_attached_volume(connection_properties['volume_id']) if not out or int(out['ret_code']) != 0: msg = _("query attached volume failed or volume not attached.") raise exception.BrickException(msg=msg) device_info = {'type': 'block', 'path': out['dev_addr']} return device_info
def disconnect_volume(self, connection_properties, device_info): """Disconnect a volume from the local host.""" LOG.debug("Disconnect_volume: %s." % connection_properties) out = self._detach_volume(connection_properties['volume_id']) if not out or int(out['ret_code']) not in (self.attached_success_code, self.vbs_unnormal_code, self.not_mount_node_code): msg = (_("Disconnect_volume failed, " "error code is %s") % out['ret_code']) raise exception.BrickException(msg=msg)
def _get_client_id(self): """ Return the local SDC GUID :return: Unique ScaleIO SDC ID """ request = ("https://%(server_ip)s:%(server_port)s/" "api/types/Client/instances/getByIp::%(sdc_ip)s/" % { 'server_ip': self.server_ip, 'server_port': self.server_port, 'sdc_ip': self.local_sdc_ip }) LOG.info(_LE("ScaleIO get client id by ip request: %(request)s"), {'request': request}) r = requests.get(request, auth=(self.server_username, self.server_token), verify=False) r = self._check_response(r, request) sdc_id = r.json() if not sdc_id: msg = (_("Client with ip %(sdc_ip)s was not found.") % { 'sdc_ip': self.local_sdc_ip }) raise exception.BrickException(message=msg) if r.status_code != 200 and "errorCode" in sdc_id: msg = (_("Error getting sdc id from ip %(sdc_ip)s: %(err)s") % { 'sdc_ip': self.local_sdc_ip, 'err': sdc_id['message'] }) LOG.error(msg) raise exception.BrickException(message=msg) LOG.info(_LE("ScaleIO sdc id is %(sdc_id)s."), {'sdc_id': sdc_id}) return sdc_id
def connect_volume(self, connection_properties): client_id = connection_properties.get('volume', None) if client_id is None: raise exception.BrickException(_LE( 'Invalid StorPool connection data, no client ID specified')) volume_id = connection_properties.get('volume', None) if volume_id is None: raise exception.BrickException(_LE( 'Invalid StorPool connection data, no volume ID specified')) volume = self._attach.volumeName(volume_id) mode = connection_properties.get('access_mode', None) if mode is None or mode not in ('rw', 'ro'): raise exception.BrickException(_LE( 'Invalid access_mode specified in the connection data')) req_id = 'brick-%s-%s' % (client_id, volume_id) self._attach.add(req_id, { 'volume': volume, 'type': 'brick', 'id': req_id, 'rights': 1 if mode == 'ro' else 2, 'volsnap': False }) self._attach.sync(req_id, None) return {'type': 'block', 'path': '/dev/storpool/' + volume}
def _cli_cmd(self, method, volume_name): LOG.debug("Enter into _cli_cmd.") if not self.iscliexist: msg = _("SDS command line doesn't exist, " "can't execute SDS command.") raise exception.BrickException(msg=msg) if not method or volume_name is None: return cmd = [self.cli_path, '-c', method, '-v', volume_name] out, clilog = self._execute(*cmd, run_as_root=False, root_helper=self._root_helper) analyse_result = self._analyze_output(out) LOG.debug('%(method)s volume returns %(analyse_result)s.' % { 'method': method, 'analyse_result': analyse_result }) if clilog: LOG.error(_LE("SDS CLI output some log: %s.") % clilog) return analyse_result
def test_error_msg(self): self.assertEqual(six.text_type(exception.BrickException('test')), 'test')
def disconnect_volume(self, connection_properties, device_info): """Disconnect the ScaleIO volume. :param connection_properties: The dictionary that describes all of the target volume attributes. :type connection_properties: dict :param device_info: historical difference, but same as connection_props :type device_info: dict """ self.get_config(connection_properties) self.volume_id = self._get_volume_id() LOG.info( _LE("ScaleIO disconnect volume in ScaleIO brick volume driver.")) LOG.debug( _("ScaleIO Volume name: %(volume_name)s, SDC IP: %(sdc_ip)s, " "REST Server IP: %(server_ip)s"), { 'volume_name': self.volume_name, 'sdc_ip': self.local_sdc_ip, 'server_ip': self.server_ip }) LOG.info(_LE("ScaleIO sdc query guid command: %(cmd)s"), {'cmd': self.GET_GUID_CMD}) try: (out, err) = self._execute(*self.GET_GUID_CMD, run_as_root=True, root_helper=self._root_helper) LOG.info( _LE("Unmap volume %(cmd)s: stdout=%(out)s stderr=%(err)s"), { 'cmd': self.GET_GUID_CMD, 'out': out, 'err': err }) except putils.ProcessExecutionError as e: msg = _("Error querying sdc guid: %(err)s") % {'err': e.stderr} LOG.error(msg) raise exception.BrickException(message=msg) guid = out LOG.info(_LE("Current sdc guid: %(guid)s"), {'guid': guid}) params = {'guid': guid} headers = {'content-type': 'application/json'} request = ("https://%(server_ip)s:%(server_port)s/api/instances/" "Volume::%(volume_id)s/action/removeMappedSdc" % { 'server_ip': self.server_ip, 'server_port': self.server_port, 'volume_id': self.volume_id }) LOG.info(_LE("Unmap volume request: %(request)s"), {'request': request}) r = requests.post(request, data=json.dumps(params), headers=headers, auth=(self.server_username, self.server_token), verify=False) r = self._check_response(r, request, False, params) if r.status_code != self.OK_STATUS_CODE: response = r.json() error_code = response['errorCode'] if error_code == self.VOLUME_NOT_MAPPED_ERROR: LOG.warning( _LW("Ignoring error unmapping volume %(volume_id)s: " "volume not mapped."), {'volume_id': self.volume_name}) else: msg = (_("Error unmapping volume %(volume_id)s: %(err)s") % { 'volume_id': self.volume_name, 'err': response['message'] }) LOG.error(msg) raise exception.BrickException(message=msg)
def connect_volume(self, connection_properties): """Connect the volume. :param connection_properties: The dictionary that describes all of the target volume attributes. :type connection_properties: dict :returns: dict """ device_info = self.get_config(connection_properties) LOG.debug( _LE("scaleIO Volume name: %(volume_name)s, SDC IP: %(sdc_ip)s, " "REST Server IP: %(server_ip)s, " "REST Server username: %(username)s, " "iops limit:%(iops_limit)s, " "bandwidth limit: %(bandwidth_limit)s."), { 'volume_name': self.volume_name, 'sdc_ip': self.local_sdc_ip, 'server_ip': self.server_ip, 'username': self.server_username, 'iops_limit': self.iops_limit, 'bandwidth_limit': self.bandwidth_limit }) LOG.info(_LE("ScaleIO sdc query guid command: %(cmd)s"), {'cmd': self.GET_GUID_CMD}) try: (out, err) = self._execute(*self.GET_GUID_CMD, run_as_root=True, root_helper=self._root_helper) LOG.info( _LE("Map volume %(cmd)s: stdout=%(out)s " "stderr=%(err)s"), { 'cmd': self.GET_GUID_CMD, 'out': out, 'err': err }) except putils.ProcessExecutionError as e: msg = (_("Error querying sdc guid: %(err)s") % {'err': e.stderr}) LOG.error(msg) raise exception.BrickException(message=msg) guid = out LOG.info(_LE("Current sdc guid: %(guid)s"), {'guid': guid}) params = {'guid': guid, 'allowMultipleMappings': 'TRUE'} self.volume_id = self._get_volume_id() headers = {'content-type': 'application/json'} request = ("https://%(server_ip)s:%(server_port)s/api/instances/" "Volume::%(volume_id)s/action/addMappedSdc" % { 'server_ip': self.server_ip, 'server_port': self.server_port, 'volume_id': self.volume_id }) LOG.info(_LE("map volume request: %(request)s"), {'request': request}) r = requests.post(request, data=json.dumps(params), headers=headers, auth=(self.server_username, self.server_token), verify=False) r = self._check_response(r, request, False, params) if r.status_code != self.OK_STATUS_CODE: response = r.json() error_code = response['errorCode'] if error_code == self.VOLUME_ALREADY_MAPPED_ERROR: LOG.warning( _LW("Ignoring error mapping volume %(volume_name)s: " "volume already mapped."), {'volume_name': self.volume_name}) else: msg = (_("Error mapping volume %(volume_name)s: %(err)s") % { 'volume_name': self.volume_name, 'err': response['message'] }) LOG.error(msg) raise exception.BrickException(message=msg) self.volume_path = self._find_volume_path() device_info['path'] = self.volume_path # Set QoS settings after map was performed if self.iops_limit is not None or self.bandwidth_limit is not None: params = {'guid': guid} if self.bandwidth_limit is not None: params['bandwidthLimitInKbps'] = self.bandwidth_limit if self.iops_limit is not None: params['iopsLimit'] = self.iops_limit request = ("https://%(server_ip)s:%(server_port)s/api/instances/" "Volume::%(volume_id)s/action/setMappedSdcLimits" % { 'server_ip': self.server_ip, 'server_port': self.server_port, 'volume_id': self.volume_id }) LOG.info(_LE("Set client limit request: %(request)s"), {'request': request}) r = requests.post(request, data=json.dumps(params), headers=headers, auth=(self.server_username, self.server_token), verify=False) r = self._check_response(r, request, False, params) if r.status_code != self.OK_STATUS_CODE: response = r.json() LOG.info(_LE("Set client limit response: %(response)s"), {'response': response}) msg = (_("Error setting client limits for volume " "%(volume_name)s: %(err)s") % { 'volume_name': self.volume_name, 'err': response['message'] }) LOG.error(msg) return device_info
def test_error_msg(self): self.assertEqual(unicode(exception.BrickException('test')), 'test')