def _delete_share(self, shareobj): """Remove container by removing GPFS fileset.""" sharename = shareobj['name'] fsdev = self._get_gpfs_device() # unlink and delete the share's fileset try: self._gpfs_execute('mmunlinkfileset', fsdev, sharename, '-f') except exception.ProcessExecutionError as e: msg = (_('Failed unlink fileset for share %(sharename)s. ' 'Error: %(excmsg)s.'), { 'sharename': sharename, 'excmsg': six.text_type(e) }) LOG.error(msg) raise exception.GPFSException(msg) try: self._gpfs_execute('mmdelfileset', fsdev, sharename, '-f') except exception.ProcessExecutionError as e: msg = (_('Failed delete fileset for share %(sharename)s. ' 'Error: %(excmsg)s.'), { 'sharename': sharename, 'excmsg': six.text_type(e) }) LOG.error(msg) raise exception.GPFSException(msg)
def _delete_share(self, shareobj): """Remove container by removing GPFS fileset.""" sharename = shareobj['name'] fsdev = self._get_gpfs_device() # ignore error, when the fileset does not exist # it may happen, when the share creation failed, the share is in # 'error' state, and the fileset was never created # we want to ignore that error condition while deleting the fileset, # i.e. 'Fileset name share-xyz not found', with error code '2' # and mark the deletion successful ignore_exit_code = [ERR_FILE_NOT_FOUND] # unlink and delete the share's fileset try: self._gpfs_execute('mmunlinkfileset', fsdev, sharename, '-f', ignore_exit_code=ignore_exit_code) except exception.ProcessExecutionError as e: msg = (_('Failed unlink fileset for share %(sharename)s. ' 'Error: %(excmsg)s.') % {'sharename': sharename, 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg) try: self._gpfs_execute('mmdelfileset', fsdev, sharename, '-f', ignore_exit_code=ignore_exit_code) except exception.ProcessExecutionError as e: msg = (_('Failed delete fileset for share %(sharename)s. ' 'Error: %(excmsg)s.') % {'sharename': sharename, 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg)
def allow_access(self, local_path, share, access_type, access): """Allow access to one or more vm instances.""" if access_type != 'ip': raise exception.InvalidShareAccess('Only ip access type ' 'supported.') # check if present in export try: out, __ = self._execute('exportfs', run_as_root=True) except exception.ProcessExecutionError as e: msg = (_('Failed to check exports on the systems. ' ' Error: %s.') % e) LOG.error(msg) raise exception.GPFSException(msg) out = re.search(re.escape(local_path) + '[\s\n]*' + re.escape(access), out) if out is not None: raise exception.ShareAccessExists(access_type=access_type, access=access) export_opts = self._get_export_options(share) cmd = ['exportfs', '-o', export_opts, ':'.join([access, local_path])] try: self._publish_access(*cmd) except exception.ProcessExecutionError as e: msg = (_('Failed to allow access for share %(sharename)s. ' 'Error: %(excmsg)s.') % {'sharename': share['name'], 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg)
def _verify_denied_access(self, local_path, share, ip): try: cmd = ['exportfs'] outs = self._publish_access(*cmd) except exception.ProcessExecutionError: msg = _('Failed to verify denied access for ' 'share %s.') % share['name'] LOG.exception(msg) raise exception.GPFSException(msg) for stdout, stderr in outs: if stderr and stderr.strip(): msg = ('Log/ignore stderr during _validate_denied_access for ' 'share %(sharename)s. Return code OK. ' 'Stderr: %(stderr)s' % {'sharename': share['name'], 'stderr': stderr}) LOG.debug(msg) gpfs_ips = NFSHelper.get_host_list(stdout, local_path) if ip in gpfs_ips: msg = (_('Failed to deny access for share %(sharename)s. ' 'IP %(ip)s still has access.') % {'sharename': share['name'], 'ip': ip}) LOG.error(msg) raise exception.GPFSException(msg)
def _create_share(self, shareobj): """Create a linked fileset file in GPFS. Note: GPFS file system must have quotas enabled (mmchfs -Q yes). """ sharename = shareobj['name'] sizestr = '%sG' % shareobj['size'] sharepath = self._local_path(sharename) fsdev = self._get_gpfs_device() # create fileset for the share, link it to root path and set max size try: self._gpfs_execute('mmcrfileset', fsdev, sharename, '--inode-space', 'new') except exception.ProcessExecutionError as e: msg = (_('Failed to create fileset on %(fsdev)s for ' 'the share %(sharename)s. Error: %(excmsg)s.') % { 'fsdev': fsdev, 'sharename': sharename, 'excmsg': e }) LOG.error(msg) raise exception.GPFSException(msg) try: self._gpfs_execute('mmlinkfileset', fsdev, sharename, '-J', sharepath) except exception.ProcessExecutionError as e: msg = (_('Failed to link fileset for the share %(sharename)s. ' 'Error: %(excmsg)s.') % { 'sharename': sharename, 'excmsg': e }) LOG.error(msg) raise exception.GPFSException(msg) try: self._gpfs_execute('mmsetquota', '-j', sharename, '-h', sizestr, fsdev) except exception.ProcessExecutionError as e: msg = (_('Failed to set quota for the share %(sharename)s. ' 'Error: %(excmsg)s.') % { 'sharename': sharename, 'excmsg': e }) LOG.error(msg) raise exception.GPFSException(msg) try: self._gpfs_execute('chmod', '777', sharepath) except exception.ProcessExecutionError as e: msg = (_('Failed to set permissions for share %(sharename)s. ' 'Error: %(excmsg)s.') % { 'sharename': sharename, 'excmsg': e }) LOG.error(msg) raise exception.GPFSException(msg)
def allow_access(self, local_path, share, access, error_on_exists=True): """Allow access to one or more vm instances.""" if access['access_type'] != 'ip': raise exception.InvalidShareAccess(reason='Only ip access type ' 'supported.') if error_on_exists: # check if present in export out = re.search( re.escape(local_path) + '[\s\n]*' + re.escape(access['access_to']), self._get_exports()) if out is not None: access_type = access['access_type'] access_to = access['access_to'] raise exception.ShareAccessExists(access_type=access_type, access=access_to) export_opts = self.get_export_options(share, access, 'KNFS') cmd = ['exportfs', '-o', export_opts, ':'.join([access['access_to'], local_path])] try: self._publish_access(*cmd) except exception.ProcessExecutionError: msg = _('Failed to allow access for share %s.') % share['name'] LOG.exception(msg) raise exception.GPFSException(msg)
def _get_nfs_client_exports(self, local_path): """Get the current NFS client export details from GPFS.""" out = self._execute_mmnfs_command( ('list', '-n', local_path, '-Y'), 'Failed to get exports from the system.') # Remove the header line and use the headers to describe the data lines = out.splitlines() for line in lines: data = line.split(':') if "HEADER" in data: headers = data lines.remove(line) break else: msg = _('Failed to parse exports for path %s. ' 'No HEADER found.') % local_path LOG.error(msg) raise exception.GPFSException(msg) exports = [] for line in lines: data = line.split(':') if len(data) < 3: continue # Skip empty lines (and anything less than minimal). result_data = self._fix_export_data(data, headers) exports.append(dict(zip(headers, result_data))) return exports
def _run_ssh(self, host, cmd_list, check_exit_code=True): command = ' '.join(pipes.quote(cmd_arg) for cmd_arg in cmd_list) if not self.sshpool: gpfs_ssh_login = self.configuration.gpfs_ssh_login password = self.configuration.gpfs_ssh_password privatekey = self.configuration.gpfs_ssh_private_key gpfs_ssh_port = self.configuration.gpfs_ssh_port ssh_conn_timeout = self.configuration.ssh_conn_timeout min_size = self.configuration.ssh_min_pool_conn max_size = self.configuration.ssh_max_pool_conn self.sshpool = utils.SSHPool(host, gpfs_ssh_port, ssh_conn_timeout, gpfs_ssh_login, password=password, privatekey=privatekey, min_size=min_size, max_size=max_size) try: with self.sshpool.item() as ssh: return processutils.ssh_execute( ssh, command, check_exit_code=check_exit_code) except Exception as e: with excutils.save_and_reraise_exception(): msg = (_('Error running SSH command: %(cmd)s. ' 'Error: %(excmsg)s.'), { 'cmd': command, 'excmsg': six.text_type(e) }) LOG.error(msg) raise exception.GPFSException(msg)
def _execute_mmnfs_command(self, cmd, err_msg): try: out, __ = self._execute('mmnfs', 'export', *cmd) except exception.ProcessExecutionError as e: msg = (_('%(err_msg)s Error: %(e)s.') % {'err_msg': err_msg, 'e': e}) LOG.error(msg) raise exception.GPFSException(msg) return out
def __init__(self, execute, config_object): super(KNFSHelper, self).__init__(execute, config_object) self._execute = execute try: self._execute('exportfs', check_exit_code=True, run_as_root=True) except exception.ProcessExecutionError as e: msg = (_('NFS server not found. Error: %s.') % e) LOG.error(msg) raise exception.GPFSException(msg)
def _is_gpfs_path(self, directory): try: self._gpfs_execute('mmlsattr', directory) except exception.ProcessExecutionError as e: msg = (_('%(dir)s is not on GPFS filesystem. Error: %(excmsg)s.') % {'dir': directory, 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg) return True
def _get_exports(self): """Get exportfs output.""" try: out, __ = self._execute('exportfs', run_as_root=True) except exception.ProcessExecutionError as e: msg = (_('Failed to check exports on the systems. ' ' Error: %s.') % e) LOG.error(msg) raise exception.GPFSException(msg) return out
def _is_dir(self, path): try: output, __ = self._gpfs_execute('stat', '--format=%F', path, run_as_root=False) except exception.ProcessExecutionError as e: msg = (_('%(path)s is not a directory. Error: %(excmsg)s') % {'path': path, 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg) return output.strip() == 'directory'
def _check_gpfs_state(self): try: out, __ = self._gpfs_execute('mmgetstate', '-Y') except exception.ProcessExecutionError as e: msg = (_('Failed to check GPFS state. Error: %(excmsg)s.') % {'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg) lines = out.splitlines() try: state_token = lines[0].split(':').index('state') gpfs_state = lines[1].split(':')[state_token] except (IndexError, ValueError) as e: msg = (_('Failed to check GPFS state. Error: %(excmsg)s.') % {'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg) if gpfs_state != 'active': return False return True
def check_for_setup_error(self): """Returns an error if prerequisites aren't met.""" if not self._check_gpfs_state(): msg = (_('GPFS is not active.')) LOG.error(msg) raise exception.GPFSException(msg) if not self.configuration.gpfs_share_export_ip: msg = (_('gpfs_share_export_ip must be specified.')) LOG.error(msg) raise exception.InvalidParameterValue(err=msg) gpfs_base_dir = self.configuration.gpfs_mount_point_base if not gpfs_base_dir.startswith('/'): msg = (_('%s must be an absolute path.') % gpfs_base_dir) LOG.error(msg) raise exception.GPFSException(msg) if not self._is_dir(gpfs_base_dir): msg = (_('%s is not a directory.') % gpfs_base_dir) LOG.error(msg) raise exception.GPFSException(msg) if not self._is_gpfs_path(gpfs_base_dir): msg = (_('%s is not on GPFS. Perhaps GPFS not mounted.') % gpfs_base_dir) LOG.error(msg) raise exception.GPFSException(msg) if self.configuration.gpfs_nfs_server_type not in ("KNFS", "CES"): msg = (_('Invalid gpfs_nfs_server_type value: %s. ' 'Valid values are: "KNFS", "CES".') % self.configuration.gpfs_nfs_server_type) LOG.error(msg) raise exception.InvalidParameterValue(err=msg) if ((not self.configuration.gpfs_nfs_server_list) and (self.configuration.gpfs_nfs_server_type != 'CES')): msg = (_('Missing value for gpfs_nfs_server_list.')) LOG.error(msg) raise exception.InvalidParameterValue(err=msg)
def _extend_share(self, shareobj, new_size): sharename = shareobj['name'] sizestr = '%sG' % new_size fsdev = self._get_gpfs_device() try: self._gpfs_execute('mmsetquota', fsdev + ':' + sharename, '--block', '0:' + sizestr) except exception.ProcessExecutionError as e: msg = (_('Failed to set quota for the share %(sharename)s. ' 'Error: %(excmsg)s.') % {'sharename': sharename, 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg)
def deny_access(self, local_path, share, access_type, access, force=False): """Remove access for one or more vm instances.""" cmd = ['exportfs', '-u', ':'.join([access, local_path])] try: self._publish_access(*cmd) except exception.ProcessExecutionError as e: msg = (_('Failed to deny access for share %(sharename)s. ' 'Error: %(excmsg)s.') % {'sharename': share['name'], 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg)
def _create_share_from_snapshot(self, share, snapshot, share_path): """Create share from a share snapshot.""" self._create_share(share) snapshot_path = self._get_snapshot_path(snapshot) snapshot_path = snapshot_path + "/" try: self._gpfs_execute('rsync', '-rp', snapshot_path, share_path) except exception.ProcessExecutionError as e: msg = (_('Failed to create share %(share)s from ' 'snapshot %(snapshot)s. Error: %(excmsg)s.') % {'share': share['name'], 'snapshot': snapshot['name'], 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg)
def _delete_share_snapshot(self, snapshot): """Delete a snapshot of the share.""" sharename = snapshot['share_name'] fsdev = self._get_gpfs_device() try: self._gpfs_execute('mmdelsnapshot', fsdev, snapshot['name'], '-j', sharename) except exception.ProcessExecutionError as e: msg = (_('Failed to delete snapshot %(snapshot)s. ' 'Error: %(excmsg)s.') % {'snapshot': snapshot['name'], 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg)
def _get_gpfs_device(self): fspath = self.configuration.gpfs_mount_point_base try: (out, __) = self._gpfs_execute('df', fspath) except exception.ProcessExecutionError as e: msg = (_('Failed to get GPFS device for %(fspath)s.' 'Error: %(excmsg)s') % {'fspath': fspath, 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg) lines = out.splitlines() fs = lines[1].split()[0] return fs
def _get_available_capacity(self, path): """Calculate available space on path.""" try: out, __ = self._gpfs_execute('df', '-P', '-B', '1', path) except exception.ProcessExecutionError as e: msg = (_('Failed to check available capacity for %(path)s.' 'Error: %(excmsg)s.') % {'path': path, 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg) out = out.splitlines()[1] size = int(out.split()[1]) available = int(out.split()[3]) return available, size
def _has_client_access(self, local_path, access_to=None): try: out, __ = self._execute('exportfs', run_as_root=True) except exception.ProcessExecutionError: msg = _('Failed to check exports on the systems.') LOG.exception(msg) raise exception.GPFSException(msg) if access_to: if (re.search(re.escape(local_path) + '[\s\n]*' + re.escape(access_to), out)): return True else: if re.findall(local_path + '\\b', ''.join(out)): return True return False
def _deny_ip(self, local_path, share, ip): """Remove access for one or more vm instances.""" cmd = ['exportfs', '-u', ':'.join([ip, local_path])] try: # Can get exit code 0 for success or 1 for already gone (also # potentially get 1 due to exportfs bug). So allow # _publish_access to continue with [0, 1] and then verify after # it is done. self._publish_access(*cmd, check_exit_code=[0, 1]) except exception.ProcessExecutionError: msg = _('Failed to deny access for share %s.') % share['name'] LOG.exception(msg) raise exception.GPFSException(msg) # Error code (0 or 1) makes deny IP success indeterminate. # So, verify that the IP access was completely removed. self._verify_denied_access(local_path, share, ip)
def _create_share_snapshot(self, snapshot): """Create a snapshot of the share.""" sharename = snapshot['share_name'] snapshotname = snapshot['name'] fsdev = self._get_gpfs_device() LOG.debug("sharename = %s, snapshotname = %s, fsdev = %s", (sharename, snapshotname, fsdev)) try: self._gpfs_execute('mmcrsnapshot', fsdev, snapshot['name'], '-j', sharename) except exception.ProcessExecutionError as e: msg = (_('Failed to create snapshot %(snapshot)s. ' 'Error: %(excmsg)s.') % {'snapshot': snapshot['name'], 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg)
def _create_share_snapshot(self, snapshot): """Create a snapshot of the share.""" sharename = snapshot['share_name'] snapshotname = snapshot['name'] fsdev = self._get_gpfs_device() LOG.debug( 'Attempting to create a snapshot %(snap)s from share %(share)s ' 'on device %(dev)s.', {'share': sharename, 'snap': snapshotname, 'dev': fsdev} ) try: self._gpfs_execute('mmcrsnapshot', fsdev, snapshot['name'], '-j', sharename) except exception.ProcessExecutionError as e: msg = (_('Failed to create snapshot %(snapshot)s. ' 'Error: %(excmsg)s.') % {'snapshot': snapshot['name'], 'excmsg': e}) LOG.error(msg) raise exception.GPFSException(msg)
def _get_share_name(self, fsdev, location): try: out, __ = self._gpfs_execute('mmlsfileset', fsdev, '-J', location, '-L', '-Y') except exception.ProcessExecutionError: msg = (_('Given share path %(share_path)s does not exist at ' 'mount point %(mount_point)s.') % {'share_path': location, 'mount_point': fsdev}) LOG.exception(msg) raise exception.ManageInvalidShare(reason=msg) lines = out.splitlines() try: validation_token = lines[0].split(':').index('filesetName') share_name = lines[1].split(':')[validation_token] except (IndexError, ValueError): msg = (_('Failed to check share at %s.') % location) LOG.exception(msg) raise exception.GPFSException(msg) return share_name
def _manage_existing(self, fsdev, share, old_share_name): new_share_name = share['name'] new_export_location = self._local_path(new_share_name) try: self._gpfs_execute('mmunlinkfileset', fsdev, old_share_name, '-f') except exception.ProcessExecutionError: msg = _('Failed to unlink fileset for share %s.') % new_share_name LOG.exception(msg) raise exception.GPFSException(msg) LOG.debug('Unlinked the fileset of share %s.', old_share_name) try: self._gpfs_execute('mmchfileset', fsdev, old_share_name, '-j', new_share_name) except exception.ProcessExecutionError: msg = _('Failed to rename fileset for share %s.') % new_share_name LOG.exception(msg) raise exception.GPFSException(msg) LOG.debug('Renamed the fileset from %(old_share)s to %(new_share)s.', {'old_share': old_share_name, 'new_share': new_share_name}) try: self._gpfs_execute('mmlinkfileset', fsdev, new_share_name, '-J', new_export_location) except exception.ProcessExecutionError: msg = _('Failed to link fileset for the share %s.' ) % new_share_name LOG.exception(msg) raise exception.GPFSException(msg) LOG.debug('Linked the fileset of share %(share_name)s at location ' '%(export_location)s.', {'share_name': new_share_name, 'export_location': new_export_location}) try: self._gpfs_execute('chmod', '777', new_export_location) except exception.ProcessExecutionError: msg = _('Failed to set permissions for share %s.') % new_share_name LOG.exception(msg) raise exception.GPFSException(msg) LOG.debug('Changed the permission of share %s.', new_share_name) try: out, __ = self._gpfs_execute('mmlsquota', '-j', new_share_name, '-Y', fsdev) except exception.ProcessExecutionError: msg = _('Failed to check size for share %s.') % new_share_name LOG.exception(msg) raise exception.GPFSException(msg) lines = out.splitlines() try: quota_limit = lines[0].split(':').index('blockLimit') quota_status = lines[1].split(':')[quota_limit] except (IndexError, ValueError): msg = _('Failed to check quota for share %s.') % new_share_name LOG.exception(msg) raise exception.GPFSException(msg) share_size = int(quota_status) # Note: since share_size returns integer value in KB, # we are checking whether share is less than 1GiB. # (units.Mi * KB = 1GB) if share_size < units.Mi: try: self._gpfs_execute('mmsetquota', fsdev + ':' + new_share_name, '--block', '0:1G') except exception.ProcessExecutionError: msg = _('Failed to set quota for share %s.') % new_share_name LOG.exception(msg) raise exception.GPFSException(msg) LOG.info(_LI('Existing share %(shr)s has size %(size)s KB ' 'which is below 1GiB, so extended it to 1GiB.') % {'shr': new_share_name, 'size': share_size}) share_size = 1 else: orig_share_size = share_size share_size = int(math.ceil(float(share_size) / units.Mi)) if orig_share_size != share_size * units.Mi: try: self._gpfs_execute('mmsetquota', fsdev + ':' + new_share_name, '--block', '0:' + str(share_size) + 'G') except exception.ProcessExecutionError: msg = _('Failed to set quota for share %s.' ) % new_share_name LOG.exception(msg) raise exception.GPFSException(msg) new_export_location = self._get_helper(share).create_export( new_export_location) return share_size, new_export_location