def update_nfs_access_rule(self, host_list, share_id=None, snapshot_id=None): if share_id is not None: name = os.path.join('/shares', share_id) elif snapshot_id is not None: name = os.path.join('/snapshots', snapshot_id) else: msg = _("No share/snapshot provided to update NFS rules.") raise exception.HNASBackendException(msg=msg) command = ['nfs-export', 'mod', '-c'] if len(host_list) == 0: command.append('127.0.0.1') else: string_command = '"' + six.text_type(host_list[0]) for i in range(1, len(host_list)): string_command += ',' + (six.text_type(host_list[i])) string_command += '"' command.append(string_command) command.append(name) try: self._execute(command) except processutils.ProcessExecutionError: msg = _("Could not update access rules for NFS export %s.") % name LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def _locked_selectfs(self, op, path): if op == 'create': command = [ 'selectfs', self.fs_name, '\n', 'ssc', '127.0.0.1', 'console-context', '--evs', self.evs_id, 'mkdir', '-p', path ] try: self._execute(command) except processutils.ProcessExecutionError: msg = _("Failed to create directory %s.") % path LOG.exception(msg) raise exception.HNASBackendException(msg=msg) if op == 'delete': command = [ 'selectfs', self.fs_name, '\n', 'ssc', '127.0.0.1', 'console-context', '--evs', self.evs_id, 'rmdir', path ] try: self._execute(command) except processutils.ProcessExecutionError as e: if 'DirectoryNotEmpty' in e.stderr: LOG.debug("Share %(path)s has more snapshots.", {'path': path}) elif 'NotFound' in e.stderr: LOG.warning( _LW("Attempted to delete path %s but it does " "not exist."), path) else: msg = _("Failed to delete directory %s.") % path LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def test_create_share_from_snapshot_cleanup(self): dest_path = '/snapshots/' + share_nfs['id'] + '/' + snapshot_nfs['id'] src_path = '/shares/' + share_nfs['id'] self.mock_object(driver.HitachiHNASDriver, "_check_fs_mounted", mock.Mock()) self.mock_object(ssh.HNASSSHBackend, "vvol_create") self.mock_object(ssh.HNASSSHBackend, "quota_add") self.mock_object(ssh.HNASSSHBackend, "tree_clone") self.mock_object(ssh.HNASSSHBackend, "vvol_delete") self.mock_object( ssh.HNASSSHBackend, "nfs_export_add", mock.Mock(side_effect=exception.HNASBackendException( msg='Error adding nfs export.'))) self.assertRaises(exception.HNASBackendException, self._driver.create_share_from_snapshot, 'context', share_nfs, snapshot_nfs) ssh.HNASSSHBackend.vvol_create.assert_called_once_with(share_nfs['id']) ssh.HNASSSHBackend.quota_add.assert_called_once_with( share_nfs['id'], share_nfs['size']) ssh.HNASSSHBackend.tree_clone.assert_called_once_with( dest_path, src_path) ssh.HNASSSHBackend.nfs_export_add.assert_called_once_with( share_nfs['id']) ssh.HNASSSHBackend.vvol_delete.assert_called_once_with(share_nfs['id'])
def cifs_allow_access(self, name, user, permission, is_snapshot=False): command = [ 'cifs-saa', 'add', '--target-label', self.fs_name, name, user, permission ] try: self._execute(command) except processutils.ProcessExecutionError as e: if 'already listed as a user' in e.stderr: if is_snapshot: LOG.debug( 'User %(user)s already allowed to access ' 'snapshot %(snapshot)s.', { 'user': user, 'snapshot': name, }) else: self._update_cifs_rule(name, user, permission) else: entity_type = "share" if is_snapshot: entity_type = "snapshot" msg = _("Could not add access of user %(user)s to " "%(entity_type)s %(name)s.") % { 'user': user, 'name': name, 'entity_type': entity_type, } LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def _deny_access(self, share_id, host, permission): """Deny access to the share. :param share_id: ID of share that access will be denied. :param host: Host to which access will be denied. :param permission: permission (e.g. 'rw', 'ro') that will be denied. """ # check if the share exists self._ensure_share(share_id) # get the list that contains all the hosts allowed on the share host_list = self.hnas.get_host_list(share_id) if permission in ('ro', 'rw'): host_access = host + '(' + permission + ')' else: msg = (_("Permission should be 'ro' or 'rw' instead " "of %s") % permission) raise exception.HNASBackendException(msg=msg) # check if the host(s) is already not allowed if host_access not in host_list: LOG.debug("Host: %(host)s is already not allowed.", {'host': host}) else: # remove the host on host_list host_list.remove(host_access) self.hnas.update_access_rule(share_id, host_list)
def _allow_access(self, share_id, host, permission='rw'): """Allow access to the share. :param share_id: ID of share that access will be allowed. :param host: Host to which access will be allowed. :param permission: permission (e.g. 'rw', 'ro') that will be allowed. """ # check if the share exists self._ensure_share(share_id) # get the list that contains all the hosts allowed on the share host_list = self.hnas.get_host_list(share_id) if permission in ('ro', 'rw'): host_access = host + '(' + permission + ')' else: msg = (_("Permission should be 'ro' or 'rw' instead " "of %s") % permission) raise exception.HNASBackendException(msg=msg) # check if the host(s) is already allowed if any(host in x for x in host_list): if host_access in host_list: LOG.debug("Host: %(host)s is already allowed.", {'host': host}) else: # remove all the hosts with different permissions host_list = [x for x in host_list if not x.startswith(host)] # add the host with new permission host_list.append(host_access) self.hnas.update_access_rule(share_id, host_list) else: host_list.append(host_access) self.hnas.update_access_rule(share_id, host_list)
def cifs_deny_access(self, name, user, is_snapshot=False): command = [ 'cifs-saa', 'delete', '--target-label', self.fs_name, name, user ] entity_type = "share" if is_snapshot: entity_type = "snapshot" try: self._execute(command) except processutils.ProcessExecutionError as e: if ('not listed as a user' in e.stderr or 'Could not delete user/group' in e.stderr): LOG.warning( 'User %(user)s already not allowed to access ' '%(entity_type)s %(name)s.', { 'entity_type': entity_type, 'user': user, 'name': name }) else: msg = _("Could not delete access of user %(user)s to " "%(entity_type)s %(name)s.") % { 'user': user, 'name': name, 'entity_type': entity_type, } LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def deny_access(self, share_id, host, share_proto, permission): """Deny access to the share. :param share_id: ID of share that access will be denied. :param host: Host to which access will be denied. :param share_proto: Storage protocol of share. Currently, only NFS storage protocol is supported. :param permission: permission (e.g. 'rw', 'ro') that will be denied. """ # check if the share exists self.ensure_share(share_id, share_proto) export = self._nfs_export_list(share_id) # get the list that contains all the hosts allowed on the share host_list = export[0].export_configuration if permission in ('ro', 'rw'): host_access = host + '(' + permission + ')' else: msg = (_("Permission should be 'ro' or 'rw' instead " "of %s") % permission) raise exception.HNASBackendException(msg=msg) # check if the host(s) is already not allowed if host_access not in host_list: LOG.debug("Host: %(host)s is already not allowed.", {'host': host}) else: # remove the host on host_list host_list.remove(host_access) self._update_access_rule(share_id, host_list)
def _get_export(self, name, is_snapshot=False): if is_snapshot: name = '/snapshots/' + name else: name = '/shares/' + name command = ['nfs-export', 'list ', name] export_list = [] try: output, err = self._execute(command) except processutils.ProcessExecutionError as e: if 'does not exist' in e.stderr: msg = _("Export %(name)s was not found in EVS " "%(evs_id)s.") % { 'name': name, 'evs_id': self.evs_id, } LOG.exception(msg) raise exception.HNASItemNotFoundException(msg=msg) else: msg = _("Could not list NFS exports by name %s.") % name LOG.exception(msg) raise exception.HNASBackendException(msg=msg) items = output.split('Export name') if items[0][0] == '\n': items.pop(0) for i in range(0, len(items)): export_list.append(Export(items[i])) return export_list
def _check_export(self, vvol_name): export = self._nfs_export_list(vvol_name) if (vvol_name in export[0].export_name and self.fs_name in export[0].file_system_label): return True else: msg = (_("Export %s does not exist.") % export[0].export_name) raise exception.HNASBackendException(msg=msg)
def _check_quota(self, vvol_name): command = ['quota', 'list', '--verbose', self.fs_name, vvol_name] output, err = self._execute(command) if 'No quotas matching specified filter criteria' not in output: return True else: msg = (_("Virtual volume %s does not have any quota.") % vvol_name) raise exception.HNASBackendException(msg=msg)
def mount(self): command = ['mount', self.fs_name] try: self._execute(command) except processutils.ProcessExecutionError as e: if 'file system is already mounted' not in e.stderr: msg = _("Failed to mount filesystem %s.") % self.fs_name LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def _locked_selectfs(self, op, path): if op == 'create': command = [ 'selectfs', self.fs_name, '\n', 'ssc', '127.0.0.1', 'console-context', '--evs', self.evs_id, 'mkdir', '-p', path ] try: self._execute(command) except processutils.ProcessExecutionError as e: if "Current file system invalid: VolumeNotFound" in e.stderr: msg = _("Command to create directory %s failed due to " "context change.") % path LOG.debug(msg) raise exception.HNASSSCContextChange(msg=msg) else: msg = _("Failed to create directory %s.") % path LOG.exception(msg) raise exception.HNASBackendException(msg=msg) if op == 'delete': command = [ 'selectfs', self.fs_name, '\n', 'ssc', '127.0.0.1', 'console-context', '--evs', self.evs_id, 'rmdir', path ] try: self._execute(command) except processutils.ProcessExecutionError as e: if 'DirectoryNotEmpty' in e.stderr: msg = _("Share %s has more snapshots.") % path LOG.debug(msg) raise exception.HNASDirectoryNotEmpty(msg=msg) elif 'cannot remove' in e.stderr and 'NotFound' in e.stderr: LOG.warning( "Attempted to delete path %s but it does " "not exist.", path) elif 'Current file system invalid: VolumeNotFound' in e.stderr: msg = _("Command to delete empty directory %s failed due " "to context change.") % path LOG.debug(msg) raise exception.HNASSSCContextChange(msg=msg) else: msg = _("Failed to delete directory %s.") % path LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def nfs_export_del(self, share_id=None, snapshot_id=None): if share_id is not None: name = os.path.join('/shares', share_id) elif snapshot_id is not None: name = os.path.join('/snapshots', snapshot_id) else: msg = _("NFS export not specified to delete.") raise exception.HNASBackendException(msg=msg) command = ['nfs-export', 'del', name] try: self._execute(command) except processutils.ProcessExecutionError as e: if 'does not exist' in e.stderr: LOG.warning("Export %s does not exist on " "backend anymore.", name) else: msg = _("Could not delete NFS export %s.") % name LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def modify_quota(self, vvol_name, new_size): str_quota = six.text_type(new_size) + 'G' command = ['quota', 'mod', '--usage-limit', str_quota, self.fs_name, vvol_name] try: self._execute(command) except processutils.ProcessExecutionError: msg = _("Failed to update quota of vvol %(vvol)s to " "%(quota)s.") % {'quota': str_quota, 'vvol': vvol_name} LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def vvol_create(self, vvol_name): # create a virtual-volume inside directory path = '/shares/' + vvol_name command = ['virtual-volume', 'add', '--ensure', self.fs_name, vvol_name, path] try: self._execute(command) except processutils.ProcessExecutionError: msg = _("Failed to create vvol %s.") % vvol_name LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def nfs_export_add(self, share_id): path = '/shares/' + share_id command = [ 'nfs-export', 'add', '-S', 'disable', '-c', '127.0.0.1', path, self.fs_name, path ] try: self._execute(command) except processutils.ProcessExecutionError: msg = _("Could not create NFS export %s.") % share_id LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def cifs_share_add(self, share_id): path = r'\\shares\\' + share_id command = [ 'cifs-share', 'add', '-S', 'disable', '--enable-abe', '--nodefaultsaa', share_id, self.fs_name, path ] try: self._execute(command) except processutils.ProcessExecutionError: msg = _("Could not create CIFS share %s.") % share_id LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def _check_vvol(self, vvol_name): command = [ 'virtual-volume', 'list', '--verbose', self.fs_name, vvol_name ] try: output, err = self._execute(command) return True except processutils.ProcessExecutionError as e: msg = six.text_type(e) LOG.exception(msg) msg = (_("Virtual volume %s does not exist.") % vvol_name) raise exception.HNASBackendException(msg=msg)
def quota_add(self, vvol_name, vvol_quota): str_quota = six.text_type(vvol_quota) + 'G' command = ['quota', 'add', '--usage-limit', str_quota, '--usage-hard-limit', 'yes', self.fs_name, vvol_name] try: self._execute(command) except processutils.ProcessExecutionError: msg = _("Failed to add %(quota)s quota to vvol " "%(vvol)s.") % {'quota': str_quota, 'vvol': vvol_name} LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def cifs_share_del(self, name): command = ['cifs-share', 'del', '--target-label', self.fs_name, name] try: self._execute(command) except processutils.ProcessExecutionError as e: if e.exit_code == 1: LOG.warning( "CIFS share %s does not exist on " "backend anymore.", name) else: msg = _("Could not delete CIFS share %s.") % name LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def nfs_export_del(self, share_id): path = '/shares/' + share_id command = ['nfs-export', 'del', path] try: self._execute(command) except processutils.ProcessExecutionError as e: if 'does not exist' in e.stderr: LOG.warning(_LW("Export %s does not exist on " "backend anymore."), path) else: msg = six.text_type(e) LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def tree_delete(self, path): command = ['tree-delete-job-submit', '--confirm', '-f', self.fs_name, path] try: self._execute(command) except processutils.ProcessExecutionError as e: if 'Source path: Cannot access' in e.stderr: LOG.warning(_LW("Attempted to delete path %s " "but it does not exist."), path) else: msg = _("Could not submit tree delete job to delete path " "%s.") % path LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def vvol_delete(self, vvol_name): path = '/shares/' + vvol_name # Virtual-volume and quota are deleted together command = ['tree-delete-job-submit', '--confirm', '-f', self.fs_name, path] try: self._execute(command) except processutils.ProcessExecutionError as e: if 'Source path: Cannot access' in e.stderr: LOG.warning(_LW("Share %s does not exist."), vvol_name) else: msg = _("Failed to delete vvol %s.") % vvol_name LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def manage_existing(self, share, driver_options): """Manages a share that exists on backend. :param share: Share that will be managed. :param driver_options: Empty dict or dict with 'volume_id' option. :returns: Returns a dict with size of share managed and its location (your path in file-system). """ share_id = self._get_hnas_share_id(share['id']) if share_id != share['id']: msg = _("Share ID %s already exists, cannot manage.") % share_id raise exception.HNASBackendException(msg=msg) LOG.info( _LI("Share %(shr_path)s will be managed with ID %(shr_id)s."), { 'shr_path': share['export_locations'][0]['path'], 'shr_id': share['id'] }) old_path_info = share['export_locations'][0]['path'].split(':') old_path = old_path_info[1].split('/') if len(old_path) == 3: evs_ip = old_path_info[0] share_id = old_path[2] else: msg = _("Incorrect path. It should have the following format: " "IP:/shares/share_id.") raise exception.ShareBackendException(msg=msg) if evs_ip != self.hnas_evs_ip: msg = _("The EVS IP %(evs)s is not " "configured.") % { 'evs': evs_ip } raise exception.ShareBackendException(msg=msg) if self.backend_name not in share['host']: msg = _("The backend passed in the host parameter (%(shr)s) is " "not configured.") % { 'shr': share['host'] } raise exception.ShareBackendException(msg=msg) output = self._manage_existing(share_id) self.private_storage.update(share['id'], {'hnas_id': share_id}) return output
def cifs_share_del(self, share_id): command = [ 'cifs-share', 'del', '--target-label', self.fs_name, share_id ] try: self._execute(command) except processutils.ProcessExecutionError as e: if e.exit_code == 1: LOG.warning( _LW("CIFS share %s does not exist on " "backend anymore."), share_id) else: msg = six.text_type(e) LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def check_quota(self, vvol_name): command = ['quota', 'list', '--verbose', self.fs_name, vvol_name] try: output, err = self._execute(command) except processutils.ProcessExecutionError: msg = _("Could not check quota of vvol %s.") % vvol_name LOG.exception(msg) raise exception.HNASBackendException(msg=msg) if 'No quotas matching specified filter criteria' in output: msg = _("Virtual volume %s does not have any" " quota.") % vvol_name LOG.error(msg) raise exception.HNASItemNotFoundException(msg=msg)
def nfs_export_add(self, share_id, snapshot_id=None): if snapshot_id is not None: path = os.path.join('/snapshots', share_id, snapshot_id) name = os.path.join('/snapshots', snapshot_id) else: path = name = os.path.join('/shares', share_id) command = ['nfs-export', 'add', '-S', 'disable', '-c', '127.0.0.1', name, self.fs_name, path] try: self._execute(command) except processutils.ProcessExecutionError: msg = _("Could not create NFS export %s.") % name LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def cifs_share_add(self, share_id, snapshot_id=None): if snapshot_id is not None: path = r'\\snapshots\\' + share_id + r'\\' + snapshot_id name = snapshot_id else: path = r'\\shares\\' + share_id name = share_id command = ['cifs-share', 'add', '-S', 'disable', '--enable-abe', '--nodefaultsaa', name, self.fs_name, path] try: self._execute(command) except processutils.ProcessExecutionError: msg = _("Could not create CIFS share %s.") % name LOG.exception(msg) raise exception.HNASBackendException(msg=msg)
def get_share_quota(self, share_id): command = ['quota', 'list', self.fs_name, share_id] output, err = self._execute(command) quota = Quota(output) if quota.limit is None: return None if quota.limit_unit == 'TB': return quota.limit * units.Ki elif quota.limit_unit == 'GB': return quota.limit else: msg = (_("Share %s does not support quota values " "below 1G.") % share_id) raise exception.HNASBackendException(msg=msg)