def _wait_for_volume_path(self, path): 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) disk_filename = None filenames = os.listdir(path) LOG.info(_LI( "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 break if not disk_filename: msg = (_("ScaleIO volume %(volume_id)s not found.") % {'volume_id': self.volume_id}) LOG.debug(msg) raise exception.BrickException(message=msg) return disk_filename
def _find_vgc_host(self): """Finds vgc-cluster hostname for this box.""" params = [self.VGCCLUSTER, "domain-list", "-1"] try: out, unused = self._execute(*params, run_as_root=True, root_helper=self._root_helper) except putils.ProcessExecutionError as err: self._log_cli_err(err) msg = _("Unable to get list of domain members, check that " "the cluster is running.") raise exception.BrickException(message=msg) domain = out.splitlines() params = ["ip", "addr", "list"] try: out, unused = self._execute(*params, run_as_root=False) except putils.ProcessExecutionError as err: self._log_cli_err(err) msg = _("Unable to get list of IP addresses on this host, " "check permissions and networking.") raise exception.BrickException(message=msg) nets = out.splitlines() for host in domain: try: ip = socket.gethostbyname(host) for l in nets: x = l.strip() if x.startswith("inet %s/" % ip): return host except socket.error: pass msg = _("Current host isn't part of HGST domain.") raise exception.BrickException(message=msg)
def disconnect_volume(self, connection_properties, device_info, force=False, ignore_errors=False): """Detach and flush the volume. :param connection_properties: The dictionary that describes all of the target volume attributes. For HGST must include: name - Name of space to detach noremovehost - Host which should never be removed :type connection_properties: dict :param device_info: historical difference, but same as connection_props :type device_info: dict """ if connection_properties is None: msg = _("Connection properties passed in as None.") raise exception.BrickException(message=msg) if 'name' not in connection_properties: msg = _("Connection properties missing 'name' field.") raise exception.BrickException(message=msg) if 'noremovehost' not in connection_properties: msg = _("Connection properties missing 'noremovehost' field.") raise exception.BrickException(message=msg) if connection_properties['noremovehost'] != self._hostname(): params = [self.VGCCLUSTER, 'space-set-apphosts'] params += ['-n', connection_properties['name']] params += ['-A', self._hostname()] params += ['--action', 'DELETE'] try: self._execute(*params, run_as_root=True, root_helper=self._root_helper) except putils.ProcessExecutionError as err: self._log_cli_err(err) msg = (_("Unable to set apphost for space %s") % connection_properties['name']) raise exception.BrickException(message=msg)
def disconnect_volume(self, connection_properties, device_info, force=False, ignore_errors=False): """Disconnect a volume from an instance.""" volume_name = None if 'name' in connection_properties.keys(): volume_name = connection_properties['name'] if volume_name is None: msg = _("Failed to disconnect volume: invalid volume name") raise exception.BrickException(message=msg) cmd_arg = {'operation': 'disconnect_volume'} cmd_arg['volume_guid'] = volume_name cmdarg_json = json.dumps(cmd_arg) LOG.debug("HyperScale command hscli: %(cmd_arg)s", {'cmd_arg': cmdarg_json}) try: (out, err) = self._execute('hscli', cmdarg_json, run_as_root=True, root_helper=self._root_helper) except putils.ProcessExecutionError as e: msg = (_("Error executing hscli: %(err)s") % {'err': e.stderr}) raise exception.BrickException(message=msg) if err: msg = (_("Failed to connect volume: stdout=%(out)s " "stderr=%(err)s") % {'out': out, 'err': err}) raise exception.BrickException(message=msg)
def __init__(self, mount_type, root_helper, execute=putils.execute, *args, **kwargs): self._mount_type = mount_type if mount_type == "nfs": self._mount_base = kwargs.get('nfs_mount_point_base', None) if not self._mount_base: raise exception.InvalidParameterValue( err=_('nfs_mount_point_base required')) self._mount_options = kwargs.get('nfs_mount_options', None) self._check_nfs_options() elif mount_type == "cifs": self._mount_base = kwargs.get('smbfs_mount_point_base', None) if not self._mount_base: raise exception.InvalidParameterValue( err=_('smbfs_mount_point_base required')) self._mount_options = kwargs.get('smbfs_mount_options', None) elif mount_type == "glusterfs": self._mount_base = kwargs.get('glusterfs_mount_point_base', None) if not self._mount_base: raise exception.InvalidParameterValue( err=_('glusterfs_mount_point_base required')) self._mount_options = None else: raise exception.ProtocolNotSupported(protocol=mount_type) self.root_helper = root_helper self.set_execute(execute)
def connect_volume(self, connection_properties): """Attach a Space volume to running host. :param connection_properties: The dictionary that describes all of the target volume attributes. connection_properties for HGST must include: name - Name of space to attach :type connection_properties: dict :returns: dict """ if connection_properties is None: msg = _("Connection properties passed in as None.") raise exception.BrickException(message=msg) if 'name' not in connection_properties: msg = _("Connection properties missing 'name' field.") raise exception.BrickException(message=msg) device_info = { 'type': 'block', 'device': connection_properties['name'], 'path': '/dev/' + connection_properties['name'] } volname = device_info['device'] params = [self.VGCCLUSTER, 'space-set-apphosts'] params += ['-n', volname] params += ['-A', self._hostname()] params += ['--action', 'ADD'] try: self._execute(*params, run_as_root=True, root_helper=self._root_helper) except putils.ProcessExecutionError as err: self._log_cli_err(err) msg = (_("Unable to set apphost for space %s") % volname) raise exception.BrickException(message=msg) return device_info
def disconnect_volume(self, connection_properties, device_info, force=False, ignore_errors=False): tmp_file_path = device_info['path'] if not os.path.exists(tmp_file_path): msg = _("Vmdk: %s not found.") % tmp_file_path raise exception.NotFound(message=msg) session = None try: # We upload the temporary file to vCenter server only if it is # modified after connect_volume. if os.path.getmtime(tmp_file_path) > device_info['last_modified']: self._load_config(connection_properties) session = self._create_session() backing = vim_util.get_moref(connection_properties['volume'], "VirtualMachine") # Currently there is no way we can restore the volume if it # contains redo-log based snapshots (bug 1599026). if self._snapshot_exists(session, backing): msg = (_("Backing of volume: %s contains one or more " "snapshots; cannot disconnect.") % connection_properties['volume_id']) raise exception.BrickException(message=msg) ds_ref = vim_util.get_moref( connection_properties['datastore'], "Datastore") dc_ref = vim_util.get_moref( connection_properties['datacenter'], "Datacenter") vmdk_path = connection_properties['vmdk_path'] self._disconnect( tmp_file_path, session, ds_ref, dc_ref, vmdk_path) finally: os.remove(tmp_file_path) if session: session.logout()
def connect_volume(self, connection_properties): """Connect to a volume. :param connection_properties: The dictionary that describes all of the target volume attributes. :type connection_properties: dict :returns: dict """ 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(message=msg) try: volume_path = self._get_volume_path(connection_properties) except Exception: msg = _("query attached volume failed or volume not attached.") LOG.error(msg) raise exception.BrickException(message=msg) device_info = {'type': 'block', 'path': volume_path} return device_info
def __init__(self, user, pool, *args, **kwargs): self.rbd_user = user self.rbd_pool = pool for attr in ['rbd_user', 'rbd_pool']: val = getattr(self, attr) if val is not None: setattr(self, attr, utils.convert_str(val)) # allow these to be overridden for testing self.rados = kwargs.get('rados', rados) self.rbd = kwargs.get('rbd', rbd) if self.rados is None: raise exception.InvalidParameterValue( err=_('rados module required')) if self.rbd is None: raise exception.InvalidParameterValue( err=_('rbd module required')) self.rbd_conf = kwargs.get('conffile', '/etc/ceph/ceph.conf') self.rbd_cluster_name = kwargs.get('rbd_cluster_name', 'ceph') self.rados_connect_timeout = kwargs.get('rados_connect_timeout', -1) self.client, self.ioctx = self.connect()
def _get_volume_id(self): 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( _LI("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(_LI("ScaleIO volume id is %(volume_id)s."), {'volume_id': volume_id}) return volume_id
def _mount_vzstorage(self, vz_share, mount_path, flags=None): m = re.search("(?:(\S+):\/)?([a-zA-Z0-9_-]+)(?::(\S+))?", vz_share) if not m: msg = (_("Invalid Virtuozzo Storage share specification: %r." "Must be: [MDS1[,MDS2],...:/]<CLUSTER NAME>[:PASSWORD].") % vz_share) raise exception.BrickException(msg) mdss = m.group(1) cluster_name = m.group(2) passwd = m.group(3) if mdss: mdss = mdss.split(',') self._vzstorage_write_mds_list(cluster_name, mdss) if passwd: self._execute('pstorage', '-c', cluster_name, 'auth-node', '-P', process_input=passwd, root_helper=self.root_helper, run_as_root=True) mnt_cmd = ['pstorage-mount', '-c', cluster_name] if flags: mnt_cmd.extend(flags) mnt_cmd.extend([mount_path]) self._execute(*mnt_cmd, root_helper=self.root_helper, run_as_root=True, check_exit_code=0)
def retry(exceptions, interval=1, retries=3, backoff_rate=2): def _retry_on_exception(e): return isinstance(e, exceptions) def _backoff_sleep(previous_attempt_number, delay_since_first_attempt_ms): exp = backoff_rate ** previous_attempt_number wait_for = max(0, interval * exp) LOG.debug("Sleeping for %s seconds", wait_for) return wait_for * 1000.0 def _print_stop(previous_attempt_number, delay_since_first_attempt_ms): delay_since_first_attempt = delay_since_first_attempt_ms / 1000.0 LOG.debug("Failed attempt %s", previous_attempt_number) LOG.debug("Have been at this for %s seconds", delay_since_first_attempt) return previous_attempt_number == retries if retries < 1: raise ValueError(_('Retries must be greater than or ' 'equal to 1 (received: %s). ') % retries) def _decorator(f): @six.wraps(f) def _wrapper(*args, **kwargs): r = retrying.Retrying(retry_on_exception=_retry_on_exception, wait_func=_backoff_sleep, stop_func=_print_stop) return r.call(f, *args, **kwargs) return _wrapper return _decorator
def connect_volume(self, connection_properties): """Connect to a volume. :param connection_properties: The dictionary that describes all of the target volume attributes. :type connection_properties: dict :returns: dict """ do_local_attach = connection_properties.get('do_local_attach', self.do_local_attach) if do_local_attach: # NOTE(e0ne): sanity check if ceph-common is installed. cmd = ['which', 'rbd'] try: self._execute(*cmd) except putils.ProcessExecutionError: msg = _("ceph-common package is not installed.") LOG.error(msg) raise exception.BrickException(message=msg) # NOTE(e0ne): map volume to a block device # via the rbd kernel module. pool, volume = connection_properties['name'].split('/') cmd = ['rbd', 'map', volume, '--pool', pool] self._execute(*cmd, root_helper=self._root_helper, run_as_root=True) return {'path': RBDConnector.get_rbd_device_name(pool, volume), 'type': 'block'} rbd_handle = self._get_rbd_handle(connection_properties) return {'path': rbd_handle}
def _get_rbd_handle(self, connection_properties): try: user = connection_properties['auth_username'] pool, volume = connection_properties['name'].split('/') cluster_name = connection_properties.get('cluster_name') monitor_ips = connection_properties.get('hosts') monitor_ports = connection_properties.get('ports') keyring = connection_properties.get('keyring') except IndexError: msg = _("Connect volume failed, malformed connection properties") raise exception.BrickException(msg=msg) conf = self._create_ceph_conf(monitor_ips, monitor_ports, str(cluster_name), user, keyring) try: rbd_client = linuxrbd.RBDClient(user, pool, conffile=conf, rbd_cluster_name=str(cluster_name)) rbd_volume = linuxrbd.RBDVolume(rbd_client, volume) rbd_handle = linuxrbd.RBDVolumeIOWrapper( linuxrbd.RBDImageMetadata(rbd_volume, pool, user, conf)) except Exception: fileutils.delete_if_exists(conf) raise return rbd_handle
def fileno(self): """Sheepdog does not have support for fileno so we raise IOError. Raising IOError is recommended way to notify caller that interface is not supported - see http://docs.python.org/2/library/io.html#io.IOBase """ raise IOError(_("fileno is not supported by SheepdogVolumeIOWrapper"))
def fileno(self): """RBD does not have support for fileno() so we raise IOError. Raising IOError is recommended way to notify caller that interface is not supported - see http://docs.python.org/2/library/io.html#io.IOBase """ raise IOError(_("fileno() not supported by RBD()"))
def seek(self, offset, whence=0): if whence == 0: new_offset = offset elif whence == 1: new_offset = self._offset + offset elif whence == 2: new_offset = self._rbd_volume.image.size() new_offset += offset else: raise IOError(_("Invalid argument - whence=%s not supported") % (whence)) if (new_offset < 0): raise IOError(_("Invalid argument")) self._offset = new_offset
def __init__(self, mount_type, root_helper, execute=putils.execute, *args, **kwargs): mount_type_to_option_prefix = { 'nfs': 'nfs', 'cifs': 'smbfs', 'glusterfs': 'glusterfs', 'vzstorage': 'vzstorage', 'quobyte': 'quobyte' } if mount_type not in mount_type_to_option_prefix: raise exception.ProtocolNotSupported(protocol=mount_type) self._mount_type = mount_type option_prefix = mount_type_to_option_prefix[mount_type] self._mount_base = kwargs.get(option_prefix + '_mount_point_base') if not self._mount_base: raise exception.InvalidParameterValue( err=_('%s_mount_point_base required') % option_prefix) self._mount_options = kwargs.get(option_prefix + '_mount_options') if mount_type == "nfs": self._check_nfs_options() self.root_helper = root_helper self.set_execute(execute)
def _get_volume_path(self, connection_properties): out = self._query_attached_volume( connection_properties['volume_id']) if not out or int(out['ret_code']) != 0: msg = _("Couldn't find attached volume.") LOG.error(msg) raise exception.BrickException(message=msg) return out['dev_addr']
def get_local_share_path(self, share, expect_existing=True): local_share_path = self._smbutils.get_smb_share_path(share) if not local_share_path and expect_existing: err_msg = _("Could not find the local " "share path for %(share)s.") raise exception.VolumePathsNotFound(err_msg % dict(share=share)) return local_share_path
def _check_device_paths(self, device_paths): if len(device_paths) > 1: err_msg = _("Multiple volume paths were found: %s. This can " "occur if multipath is used and MPIO is not " "properly configured, thus not claiming the device " "paths. This issue must be addressed urgently as " "it can lead to data corruption.") raise exception.BrickException(err_msg % device_paths)
def get_volume_paths(self, connection_properties): volume_path = None try: volume_path = self._get_volume_path(connection_properties) except Exception: msg = _("Couldn't find a volume.") LOG.warning(msg) raise exception.BrickException(message=msg) return [volume_path]
def extend_volume(self, connection_properties): volume_paths = self.get_volume_paths(connection_properties) if not volume_paths: err_msg = _("Could not find the disk. Extend failed.") raise exception.NotFound(err_msg) device_path = volume_paths[0] device_number = self._diskutils.get_device_number_from_device_name( device_path) self._diskutils.refresh_disk(device_number)
def _check_or_get_keyring_contents(self, keyring, cluster_name, user): try: if keyring is None: keyring_path = ("/etc/ceph/%s.client.%s.keyring" % (cluster_name, user)) with open(keyring_path, 'r') as keyring_file: keyring = keyring_file.read() return keyring except IOError: msg = (_("Keyring path %s is not readable.") % (keyring_path)) raise exception.BrickException(msg=msg)
def __init__(self, mount_type, root_helper, execute=None, *args, **kwargs): self._mount_type = mount_type self._mount_base = kwargs.get( 'scality_mount_point_base', "").rstrip('/') if not self._mount_base: raise exception.InvalidParameterValue( err=_('scality_mount_point_base required')) self.root_helper = root_helper self.set_execute(execute or putils.execute) self._mount_options = None
def __init__(self, mount_type, root_helper, execute=None, *args, **kwargs): super(ScalityRemoteFsClient, self).__init__(mount_type, root_helper, execute=execute, *args, **kwargs) self._mount_type = mount_type self._mount_base = kwargs.get( 'scality_mount_point_base', "").rstrip('/') if not self._mount_base: raise exception.InvalidParameterValue( err=_('scality_mount_point_base required')) self._mount_options = None
def _create_mount_point(self, share): mnt_point = self.get_mount_point(share) if not os.path.isdir(self._mount_base): os.makedirs(self._mount_base) if os.path.exists(mnt_point): if not self._pathutils.is_symlink(mnt_point): raise exception.BrickException(_("Link path already exists " "and it's not a symlink")) else: self._pathutils.create_sym_link(mnt_point, share)
def connect_volume(self, connection_properties): volume_connected = False for (initiator_name, target_portal, target_iqn, target_lun) in self._get_all_paths(connection_properties): try: LOG.info("Attempting to establish an iSCSI session to " "target %(target_iqn)s on portal %(target_portal)s " "accessing LUN %(target_lun)s using initiator " "%(initiator_name)s.", dict(target_portal=target_portal, target_iqn=target_iqn, target_lun=target_lun, initiator_name=initiator_name)) self._iscsi_utils.login_storage_target( target_lun=target_lun, target_iqn=target_iqn, target_portal=target_portal, auth_username=connection_properties.get('auth_username'), auth_password=connection_properties.get('auth_password'), mpio_enabled=self.use_multipath, initiator_name=initiator_name, ensure_lun_available=False) self._iscsi_utils.ensure_lun_available( target_iqn=target_iqn, target_lun=target_lun, rescan_attempts=self.device_scan_attempts, retry_interval=self.device_scan_interval) if not volume_connected: (device_number, device_path) = ( self._iscsi_utils.get_device_number_and_path( target_iqn, target_lun)) volume_connected = True if not self.use_multipath: break except os_win_exc.OSWinException: LOG.exception("Could not establish the iSCSI session.") if not volume_connected: raise exception.BrickException( _("Could not connect volume %s.") % connection_properties) scsi_wwn = self._get_scsi_wwn(device_number) device_info = {'type': 'block', 'path': device_path, 'number': device_number, 'scsi_wwn': scsi_wwn} return device_info
def _get_sheepdog_handle(self, connection_properties): try: host = connection_properties['hosts'][0] name = connection_properties['name'] port = connection_properties['ports'][0] except IndexError: msg = _("Connect volume failed, malformed connection properties") raise exception.BrickException(msg=msg) sheepdog_handle = linuxsheepdog.SheepdogVolumeIOWrapper( host, port, name) return sheepdog_handle
def seek(self, offset, whence=0): if not self._valid: raise exception.VolumeDriverException(name=self._vdiname) if whence == 0: # SEEK_SET or 0 - start of the stream (the default); # offset should be zero or positive new_offset = offset elif whence == 1: # SEEK_CUR or 1 - current stream position; offset may be negative new_offset = self._offset + offset else: # SEEK_END or 2 - end of the stream; offset is usually negative # TODO(yamada-h): Support SEEK_END raise IOError(_("Invalid argument - whence=%s not supported.") % whence) if new_offset < 0: raise IOError(_("Invalid argument - negative seek offset.")) self._offset = new_offset
def _create_mount_point(self, share, use_local_path): # The mount point will contain a hash of the share so we're # intentionally preserving the original share path as this is # what the caller will expect. mnt_point = self.get_mount_point(share) share_norm_path = self._get_share_norm_path(share) symlink_dest = (share_norm_path if not use_local_path else self.get_local_share_path(share)) if not os.path.isdir(self._mount_base): os.makedirs(self._mount_base) if os.path.exists(mnt_point): if not self._pathutils.is_symlink(mnt_point): raise exception.BrickException( _("Link path already exists " "and it's not a symlink")) else: self._pathutils.create_sym_link(mnt_point, symlink_dest)
def __init__(self, *args, **kwargs): # Check if oslo.vmware library is available. if vim_util is None: message = _("Missing oslo_vmware python module, ensure oslo.vmware" " library is installed and available.") raise exception.BrickException(message=message) super(VmdkConnector, self).__init__(*args, **kwargs) self._ip = None self._port = None self._username = None self._password = None self._api_retry_count = None self._task_poll_interval = None self._ca_file = None self._insecure = None self._tmp_dir = None self._timeout = None
def _create_ceph_conf(self, monitor_ips, monitor_ports, cluster_name, user, keyring): monitors = [ "%s:%s" % (ip, port) for ip, port in zip( self._sanitize_mon_hosts(monitor_ips), monitor_ports) ] mon_hosts = "mon_host = %s" % (','.join(monitors)) keyring = self._check_or_get_keyring_contents(keyring, cluster_name, user) try: fd, ceph_conf_path = tempfile.mkstemp(prefix="brickrbd_") with os.fdopen(fd, 'w') as conf_file: conf_file.writelines([mon_hosts, "\n", keyring, "\n"]) return ceph_conf_path except IOError: msg = (_("Failed to write data to %s.") % (ceph_conf_path)) raise exception.BrickException(msg=msg)
def connect_volume(self, connection_properties): """Connect to a volume. :param connection_properties: The dictionary that describes all of the target volume attributes. :type connection_properties: dict :returns: dict """ do_local_attach = connection_properties.get('do_local_attach', self.do_local_attach) if do_local_attach: # NOTE(e0ne): sanity check if ceph-common is installed. cmd = ['which', 'rbd'] try: self._execute(*cmd) except putils.ProcessExecutionError: msg = _("ceph-common package is not installed.") LOG.error(msg) raise exception.BrickException(message=msg) # NOTE(e0ne): map volume to a block device # via the rbd kernel module. pool, volume = connection_properties['name'].split('/') rbd_dev_path = RBDConnector.get_rbd_device_name(pool, volume) if (not os.path.islink(rbd_dev_path) or not os.path.exists(os.path.realpath(rbd_dev_path))): cmd = ['rbd', 'map', volume, '--pool', pool] cmd += self._get_rbd_args(connection_properties) self._execute(*cmd, root_helper=self._root_helper, run_as_root=True) else: LOG.debug('volume %(vol)s is already mapped to local' ' device %(dev)s', {'vol': volume, 'dev': os.path.realpath(rbd_dev_path)}) return {'path': rbd_dev_path, 'type': 'block'} rbd_handle = self._get_rbd_handle(connection_properties) return {'path': rbd_handle}
def _check_response(self, response, request, is_get_request=True, params=None): if response.status_code == 401 or response.status_code == 403: LOG.info( _LI("Token is invalid, " "going to re-login to get a new one")) login_request = ( "https://%(server_ip)s:%(server_port)s/api/login" % { 'server_ip': self.server_ip, 'server_port': self.server_port }) r = requests.get(login_request, auth=(self.server_username, self.server_password), verify=False) token = r.json() # repeat request with valid token LOG.debug( _("Going to perform request %(request)s again " "with valid token"), {'request': request}) if is_get_request: res = requests.get(request, auth=(self.server_username, token), verify=False) else: headers = {'content-type': 'application/json'} res = requests.post(request, data=json.dumps(params), headers=headers, auth=(self.server_username, token), verify=False) self.server_token = token return res return response
def connect_volume(self, connection_properties): """Connect to a volume. :param connection_properties: The dictionary that describes all of the target volume attributes. connection_properties must include: device_path - path to the volume to be connected :type connection_properties: dict :returns: dict """ if 'device_path' not in connection_properties: msg = (_("Invalid connection_properties specified " "no device_path attribute.")) raise ValueError(msg) device_info = { 'type': 'gpfs', 'path': connection_properties['device_path'] } return device_info
def connect(self): LOG.debug("opening connection to ceph cluster (timeout=%s).", self.rados_connect_timeout) client = self.rados.Rados(rados_id=self.rbd_user, clustername=self.rbd_cluster_name, conffile=self.rbd_conf) try: if self.rados_connect_timeout >= 0: client.connect(timeout=self.rados_connect_timeout) else: client.connect() ioctx = client.open_ioctx(self.rbd_pool) return client, ioctx except self.rados.Error: msg = _("Error connecting to ceph cluster.") LOG.exception(msg) # shutdown cannot raise an exception client.shutdown() raise exception.BrickException(message=msg)
def _create_ceph_conf(self, monitor_ips, monitor_ports, cluster_name, user): monitors = [ "%s:%s" % (ip, port) for ip, port in zip( self._sanitize_mon_hosts(monitor_ips), monitor_ports) ] mon_hosts = "mon_host = %s" % (','.join(monitors)) client_section = "[client.%s]" % user keyring = ("keyring = /etc/ceph/%s.client.%s.keyring" % (cluster_name, user)) try: fd, ceph_conf_path = tempfile.mkstemp(prefix="brickrbd_") with os.fdopen(fd, 'w') as conf_file: conf_file.writelines( [mon_hosts, "\n", client_section, "\n", keyring]) return ceph_conf_path except IOError: msg = (_("Failed to write data to %s.") % (ceph_conf_path)) raise exception.BrickException(msg=msg)
def _show_rbd_mapping(self, connection_properties): # TODO(lpetrut): consider using "rbd device show" if/when # it becomes available. cmd = [ 'rbd-wnbd', 'show', connection_properties['name'], '--format', 'json' ] try: out, err = self._execute(*cmd) return json.loads(out) except processutils.ProcessExecutionError as ex: if abs(ctypes.c_int32(ex.exit_code).value) == errno.ENOENT: LOG.debug("Couldn't find RBD mapping: %s", connection_properties['name']) return raise except json.decoder.JSONDecodeError: msg = _("Could not get rbd mappping.") LOG.exception(msg) raise exception.BrickException(msg)
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(message=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 factory(protocol, root_helper, driver=None, use_multipath=False, device_scan_attempts=initiator.DEVICE_SCAN_ATTEMPTS_DEFAULT, arch=None, *args, **kwargs): """Build a Connector object based upon protocol and architecture.""" _mapping = get_connector_mapping(arch) LOG.debug("Factory for %(protocol)s on %(arch)s", { 'protocol': protocol, 'arch': arch }) protocol = protocol.upper() # set any special kwargs needed by connectors if protocol in (initiator.NFS, initiator.GLUSTERFS, initiator.SCALITY, initiator.QUOBYTE, initiator.VZSTORAGE): kwargs.update({'mount_type': protocol.lower()}) elif protocol == initiator.ISER: kwargs.update({'transport': 'iser'}) # now set all the default kwargs kwargs.update({ 'root_helper': root_helper, 'driver': driver, 'use_multipath': use_multipath, 'device_scan_attempts': device_scan_attempts, }) connector = _mapping.get(protocol) if not connector: msg = (_("Invalid InitiatorConnector protocol " "specified %(protocol)s") % dict(protocol=protocol)) raise exception.InvalidConnectorProtocol(msg) conn_cls = importutils.import_class(connector) return conn_cls(*args, **kwargs)
def _do_mount(self, mount_type, vz_share, mount_path, mount_options=None, flags=None): m = re.search(r"(?:(\S+):\/)?([a-zA-Z0-9_-]+)(?::(\S+))?", vz_share) if not m: msg = ( _("Invalid Virtuozzo Storage share specification: %r." "Must be: [MDS1[,MDS2],...:/]<CLUSTER NAME>[:PASSWORD].") % vz_share) raise exception.BrickException(msg) mdss = m.group(1) cluster_name = m.group(2) passwd = m.group(3) if mdss: mdss = mdss.split(',') self._vzstorage_write_mds_list(cluster_name, mdss) if passwd: self._execute('pstorage', '-c', cluster_name, 'auth-node', '-P', process_input=passwd, root_helper=self._root_helper, run_as_root=True) mnt_cmd = ['pstorage-mount', '-c', cluster_name] if flags: mnt_cmd.extend(flags) mnt_cmd.extend([mount_path]) self._execute(*mnt_cmd, root_helper=self._root_helper, run_as_root=True, check_exit_code=0)
def get_local_share_path(self, share, expect_existing=True): share = self._get_share_norm_path(share) share_name = self.get_share_name(share) share_subdir = self.get_share_subdir(share) is_local_share = self._smbutils.is_local_share(share) if not is_local_share: LOG.debug("Share '%s' is not exposed by the current host.", share) local_share_path = None else: local_share_path = self._smbutils.get_smb_share_path(share_name) if not local_share_path and expect_existing: err_msg = _("Could not find the local " "share path for %(share)s.") raise exception.VolumePathsNotFound(err_msg % dict(share=share)) if local_share_path and share_subdir: local_share_path = os.path.join(local_share_path, share_subdir) return local_share_path
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 create_non_openstack_config(cls, connection_properties): """Get root owned Ceph's .conf file for non OpenStack usage.""" # If keyring info is missing then we are in OpenStack, nothing to do keyring = connection_properties.get('keyring') if not keyring: return None try: user = connection_properties['auth_username'] pool, volume = connection_properties['name'].split('/') cluster_name = connection_properties['cluster_name'] monitor_ips = connection_properties['hosts'] monitor_ports = connection_properties['ports'] keyring = connection_properties.get('keyring') except (KeyError, ValueError): msg = _("Connect volume failed, malformed connection properties.") raise exception.BrickException(msg=msg) conf = rbd_privsep.root_create_ceph_conf(monitor_ips, monitor_ports, str(cluster_name), user, keyring) return conf
def _get_rbd_args(cls, connection_properties, conf=None): try: user = connection_properties['auth_username'] monitor_ips = connection_properties.get('hosts') monitor_ports = connection_properties.get('ports') except KeyError: msg = _("Connect volume failed, malformed connection properties") raise exception.BrickException(msg=msg) args = ['--id', user] if monitor_ips and monitor_ports: monitors = ["%s:%s" % (ip, port) for ip, port in zip( cls._sanitize_mon_hosts(monitor_ips), monitor_ports)] for monitor in monitors: args += ['--mon_host', monitor] if conf: args += ['--conf', conf] return args
def _create_ceph_conf(cls, monitor_ips, monitor_ports, cluster_name, user, keyring): monitors = ["%s:%s" % (ip, port) for ip, port in zip(cls._sanitize_mon_hosts(monitor_ips), monitor_ports)] mon_hosts = "mon_host = %s" % (','.join(monitors)) keyring = cls._check_or_get_keyring_contents(keyring, cluster_name, user) try: fd, ceph_conf_path = tempfile.mkstemp(prefix="brickrbd_") with os.fdopen(fd, 'w') as conf_file: # Bug #1865754 - '[global]' has been the appropriate # place for this stuff since at least Hammer, but in # Octopus (15.2.0+), Ceph began enforcing this. conf_file.writelines(["[global]", "\n", mon_hosts, "\n", keyring, "\n"]) return ceph_conf_path except IOError: msg = (_("Failed to write data to %s.") % (ceph_conf_path)) raise exception.BrickException(msg=msg)
def __init__(self, mount_type, root_helper, execute=None, *args, **kwargs): # For backwards compatibility, `putils.execute` is interpreted # as a sentinel to mean "I want the os-brick default" :-/ # This can be burnt as soon as we update all the callsites (in # nova+cinder) to the new default - and then we shall never # speak of it again. # TODO(gus): RemoteFsClient should probably inherit from Executor if execute is None or execute == putils.execute: execute = priv_rootwrap.execute mount_type_to_option_prefix = { 'nfs': 'nfs', 'cifs': 'smbfs', 'glusterfs': 'glusterfs', 'vzstorage': 'vzstorage', 'quobyte': 'quobyte' } if mount_type not in mount_type_to_option_prefix: raise exception.ProtocolNotSupported(protocol=mount_type) self._mount_type = mount_type option_prefix = mount_type_to_option_prefix[mount_type] self._mount_base = kwargs.get(option_prefix + '_mount_point_base') if not self._mount_base: raise exception.InvalidParameterValue( err=_('%s_mount_point_base required') % option_prefix) self._mount_options = kwargs.get(option_prefix + '_mount_options') if mount_type == "nfs": self._check_nfs_options() self.root_helper = root_helper self.set_execute(execute)
def connect_volume(self, connection_properties): """Connect to a volume. :param connection_properties: The dictionary that describes all of the target volume attributes. :type connection_properties: dict :returns: dict """ do_local_attach = connection_properties.get('do_local_attach', self.do_local_attach) if do_local_attach: # NOTE(e0ne): sanity check if ceph-common is installed. cmd = ['which', 'rbd'] try: self._execute(*cmd) except putils.ProcessExecutionError: msg = _("ceph-common package is not installed.") LOG.error(msg) raise exception.BrickException(message=msg) # NOTE(e0ne): map volume to a block device # via the rbd kernel module. pool, volume = connection_properties['name'].split('/') cmd = ['rbd', 'map', volume, '--pool', pool] self._execute(*cmd, root_helper=self._root_helper, run_as_root=True) return { 'path': RBDConnector.get_rbd_device_name(pool, volume), 'type': 'block' } rbd_handle = self._get_rbd_handle(connection_properties) return {'path': rbd_handle}
def retry(exceptions, interval=1, retries=3, backoff_rate=2): if retries < 1: raise ValueError( _('Retries must be greater than or ' 'equal to 1 (received: %s). ') % retries) def _decorator(f): @functools.wraps(f) def _wrapper(*args, **kwargs): r = tenacity.Retrying( before_sleep=tenacity.before_sleep_log(LOG, logging.DEBUG), after=tenacity.after_log(LOG, logging.DEBUG), stop=tenacity.stop_after_attempt(retries), reraise=True, retry=tenacity.retry_if_exception_type(exceptions), wait=tenacity.wait_exponential(multiplier=interval, min=0, exp_base=backoff_rate)) return r.call(f, *args, **kwargs) return _wrapper return _decorator
class VolumeNotDeactivated(BrickException): message = _('Volume %(name)s was not deactivated in time.')
class NoFibreChannelVolumeDeviceFound(BrickException): message = _("Unable to find a Fibre Channel volume device.")
class NoFibreChannelHostsFound(BrickException): message = _("We are unable to locate any Fibre Channel devices.")
class InvalidParameterValue(Invalid): message = _("%(err)s")
class Invalid(BrickException): message = _("Unacceptable parameters.") code = 400
class NotFound(BrickException): message = _("Resource could not be found.") code = 404 safe = True
class VolumeEncryptionNotSupported(Invalid): message = _("Volume encryption is not supported for %(volume_type)s " "volume %(volume_id)s.")
class InvalidIOHandleObject(BrickException): message = _('IO handle of %(protocol)s has wrong object ' 'type %(actual_type)s.')
class VolumeDriverException(BrickException): message = _('An error occurred while IO to volume %(name)s.')
class CommandExecutionFailed(BrickException): message = _("Failed to execute command %(cmd)s")