def update_access(self, dataset_name, access_rules, add_rules, delete_rules, make_all_ro=False, executor=None): """Update access rules for given ZFS dataset exported as NFS share.""" rw_rules = [] ro_rules = [] for rule in access_rules: if rule['access_type'].lower() != 'ip': msg = _("Only IP access type allowed for NFS protocol.") raise exception.InvalidShareAccess(reason=msg) if (rule['access_level'] == constants.ACCESS_LEVEL_RW and not make_all_ro): rw_rules.append(self._get_parsed_access_to(rule['access_to'])) elif (rule['access_level'] in (constants.ACCESS_LEVEL_RW, constants.ACCESS_LEVEL_RO)): ro_rules.append(self._get_parsed_access_to(rule['access_to'])) else: msg = _("Unsupported access level provided - " "%s.") % rule['access_level'] raise exception.InvalidShareAccess(reason=msg) rules = [] if self.is_kernel_version: if rw_rules: rules.append( "rw=%s,no_root_squash" % ":".join(rw_rules)) if ro_rules: rules.append("ro=%s,no_root_squash" % ":".join(ro_rules)) rules_str = "sharenfs=" + (','.join(rules) or 'off') else: for rule in rw_rules: rules.append("%s:rw,no_root_squash" % rule) for rule in ro_rules: rules.append("%s:ro,no_root_squash" % rule) rules_str = "sharenfs=" + (' '.join(rules) or 'off') out, err = self.zfs( 'list', '-r', dataset_name.split('/')[0], executor=executor) data = self.parse_zfs_answer(out) for datum in data: if datum['NAME'] == dataset_name: self.zfs("set", rules_str, dataset_name) break else: LOG.warning( "Dataset with '%(name)s' NAME is absent on backend. " "Access rules were not applied.", {'name': dataset_name}) # NOTE(vponomaryov): Setting of ZFS share options does not remove rules # that were added and then removed. So, remove them explicitly. if delete_rules and access_rules: mountpoint = self.get_zfs_option(dataset_name, 'mountpoint') for rule in delete_rules: if rule['access_type'].lower() != 'ip': continue access_to = self._get_parsed_access_to(rule['access_to']) export_location = access_to + ':' + mountpoint self.execute( 'sudo', 'exportfs', '-u', export_location, executor=executor, )
def _check_share_access(self, share_proto, access_type): if share_proto == 'NFS' and access_type != 'ip': reason = _('Only "ip" access type is allowed for ' 'NFS shares.') LOG.warning(reason) raise exception.InvalidShareAccess(reason=reason) elif share_proto != 'NFS': reason = _('Invalid NAS protocol: %s') % share_proto raise exception.InvalidShareAccess(reason=reason)
def _check_share_access(self, share_proto, access_type): if share_proto == 'CIFS' and access_type != 'user': reason = ('Only USER access type is allowed for ' 'CIFS shares.') LOG.warning(reason) raise exception.InvalidShareAccess(reason=reason) elif share_proto == 'NFS' and access_type not in ('ip', 'user'): reason = ('Only IP or USER access types are allowed for ' 'NFS shares.') LOG.warning(reason) raise exception.InvalidShareAccess(reason=reason) elif share_proto not in ('NFS', 'CIFS'): reason = ('Unsupported protocol \"%s\" specified for ' 'access rule.') % share_proto raise exception.InvalidShareAccess(reason=reason)
def allow_access(self, context, share, access, share_server=None): """Allow access to a share. Currently only IP based access control is supported. """ if access['access_type'] != 'ip': raise exception.InvalidShareAccess( reason=_('only IP access type allowed')) httpclient = httplib2.Http(disable_ssl_certificate_validation=True, timeout=None) sop_share_id = self._get_share_id_by_name(httpclient, share['id']) if access['access_level'] == 'rw': access_level = True elif access['access_level'] == 'ro': access_level = False else: raise exception.InvalidShareAccess( reason=(_('Unsupported level of access was provided - %s') % access['access_level'])) payload = { 'action': 'add-access-rule', 'all-squash': True, 'anongid': 65534, 'anonuid': 65534, 'host-specification': access['access_to'], 'description': '', 'read-write': access_level, 'root-squash': False, 'tags': 'nfs', 'name': '%s-%s' % (share['id'], access['access_to']), } sopuri = '/shares/' headers = dict(Authorization=self.get_sop_auth_header()) uri = self.sop_target + '/sopapi' + sopuri + sop_share_id resp_headers, resp_content = httpclient.request( uri, 'POST', body=json.dumps(payload), headers=headers) resp_code = int(resp_headers['status']) if resp_code == 202: job_loc = resp_headers['location'] self._wait_for_job_completion(httpclient, job_loc) else: raise exception.SopAPIError(err=_('received error: %s') % resp_headers['status'])
def allow_access_nfs(self, pool, project, share, access): """Allow an IP access to a share through NFS.""" if access['access_type'] != 'ip': reason = _('Only ip access type allowed.') raise exception.InvalidShareAccess(reason) ip = access['access_to'] details = self.get_share(pool, project, share) sharenfs = details['sharenfs'] if sharenfs == 'on' or sharenfs == 'rw': LOG.debug('Share %s has read/write permission' 'open to all.', share) return if sharenfs == 'off': sharenfs = 'sec=sys' if ip in sharenfs: LOG.debug( 'Access to share %(share)s via NFS ' 'already granted to %(ip)s.', { 'share': share, 'ip': ip }) return entry = (',rw=@%s' % ip) if '/' not in ip: entry = "%s/32" % entry arg = {'sharenfs': sharenfs + entry} self.modify_share(pool, project, share, arg)
def update_access(self, context, share, access_rules, add_rules, delete_rules, share_server=None): """Update access rules for given share.""" for access in access_rules: if access['access_type'].lower() != 'user': msg = _("Only 'user' access type allowed!") LOG.error(msg) raise exception.InvalidShareAccess(reason=msg) volume_name = self._get_volume_name(context, share) try: # 'update_access' is called before share is removed, so this # method shouldn`t raise exception if share does # not exist actually if not self._maprfs_util.volume_exists(volume_name): LOG.warning('Can not get share %s.', share['name']) return # check update if add_rules or delete_rules: self._maprfs_util.remove_volume_ace_rules( volume_name, delete_rules) self._maprfs_util.add_volume_ace_rules(volume_name, add_rules) else: self._maprfs_util.set_volume_ace(volume_name, access_rules) except exception.ProcessExecutionError: msg = (_('Failed to update access for share %(name)s.') % { 'name': share['name'] }) LOG.exception(msg) raise exception.MapRFSException(msg=msg)
def _nfs_allow_access(self, share, access): """Allow access to nfs share.""" access_type = access['access_type'] if access_type != 'ip': message = _('Only "ip" access type allowed for the NFS ' 'protocol.') LOG.error(message) raise exception.InvalidShareAccess(reason=message) export_path = self._get_container_path(share) access_ip = access['access_to'] access_level = access['access_level'] share_id = self._isilon_api.lookup_nfs_export(export_path) share_access_group = 'clients' if access_level == const.ACCESS_LEVEL_RO: share_access_group = 'read_only_clients' # Get current allowed clients export = self._get_existing_nfs_export(share_id) current_clients = export[share_access_group] # Format of ips could be '10.0.0.2', or '10.0.0.2, 10.0.0.0/24' ips = list() ips.append(access_ip) ips.extend(current_clients) export_params = {share_access_group: ips} url = '{0}/platform/1/protocols/nfs/exports/{1}'.format( self._server_url, share_id) resp = self._isilon_api.request('PUT', url, data=export_params) resp.raise_for_status()
def deny_access(self, context, share, access, share_server=None): """Deny access to a share. :param context: The `context.RequestContext` object for the request :param share: Share to which access will be denied. :param access: Information about the access that will be denied, e.g. host and type of access denied. :param share_server: Data structure with share server information. Not used by this driver. """ if ('nfs', 'ip') != (share['share_proto'].lower(), access['access_type'].lower()): msg = _("Only NFS protocol and IP access type currently " "supported.") raise exception.InvalidShareAccess(reason=msg) LOG.debug( "Sending HNAS request to deny access to share:" " %(shr_id)s.", {'shr_id': six.text_type(share['id'])}) share_id = self._get_hnas_share_id(share['id']) self.hnas.deny_access(share_id, access['access_to'], share['share_proto'], access['access_level']) LOG.info(_LI("Access denied successfully to share: %(shr)s."), {'shr': six.text_type(share['id'])})
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 _cifs_allow_access(self, share, hnas_share_id, add_rules): for rule in add_rules: if rule['access_type'].lower() != 'user': msg = _("Only USER access type currently supported for CIFS. " "Share provided %(share)s with rule %(r_id)s type " "%(type)s allowing permission to %(to)s.") % { 'share': share['id'], 'type': rule['access_type'], 'r_id': rule['id'], 'to': rule['access_to'] } raise exception.InvalidShareAccess(reason=msg) if rule['access_level'] == constants.ACCESS_LEVEL_RW: # Adding permission acr = Allow Change&Read permission = 'acr' else: # Adding permission ar = Allow Read permission = 'ar' formatted_user = rule['access_to'].replace('\\', '\\\\') self.hnas.cifs_allow_access(hnas_share_id, formatted_user, permission) LOG.debug( "Added %(rule)s rule for user/group %(user)s to share " "%(share)s.", { 'rule': rule['access_level'], 'user': rule['access_to'], 'share': share['id'] })
def _nfs_update_access(self, share, hnas_share_id, access_rules): host_list = [] for rule in access_rules: if rule['access_type'].lower() != 'ip': msg = _("Only IP access type currently supported for NFS. " "Share provided %(share)s with rule type " "%(type)s.") % { 'share': share['id'], 'type': rule['access_type'] } raise exception.InvalidShareAccess(reason=msg) if rule['access_level'] == constants.ACCESS_LEVEL_RW: host_list.append(rule['access_to'] + '(' + rule['access_level'] + ',norootsquash)') else: host_list.append(rule['access_to'] + '(' + rule['access_level'] + ')') self.hnas.update_nfs_access_rule(hnas_share_id, host_list) if host_list: LOG.debug("Share %(share)s has the rules: %(rules)s", { 'share': share['id'], 'rules': ', '.join(host_list) }) else: LOG.debug("Share %(share)s has no rules.", {'share': share['id']})
def allow_access(self, local_path, share, access): """Allow access to the host.""" if access['access_type'] != 'ip': raise exception.InvalidShareAccess(reason='Only ip access type ' 'supported.') err_msg = 'Failed to check exports on the system.' out = self._execute_mmnfs_command(('list', '-n', local_path), err_msg) options_not_allowed = ['access_type=ro', 'access_type=rw'] export_opts = self.get_export_options(share, access, 'CES', options_not_allowed) out = re.search(re.escape(local_path), out) if out is None: cmd = [ 'add', local_path, '-c', access['access_to'] + '(' + export_opts + ')' ] else: cmd = [ 'change', local_path, '--nfsadd', access['access_to'] + '(' + export_opts + ')' ] err_msg = ('Failed to allow access for share %s.' % share['name']) self._execute_mmnfs_command(cmd, err_msg)
def _allow_access(self, base_path, share, access): """Allow access to the share.""" if access['access_type'] != 'ip': raise exception.InvalidShareAccess('Only IP access type allowed') access = ganesha_utils.fixup_access_rule(access) cf = {} accid = access['id'] name = share['name'] export_name = "%s--%s" % (name, accid) ganesha_utils.patch( cf, self.export_template, { 'EXPORT': { 'Export_Id': self.ganesha.get_export_id(), 'Path': os.path.join(base_path, name), 'Pseudo': os.path.join(base_path, export_name), 'Tag': accid, 'CLIENT': { 'Clients': access['access_to'] }, 'FSAL': self._fsal_hook(base_path, share, access) } }) self.ganesha.add_export(export_name, cf)
def _allow_access(self, context, share, access, share_server=None): if access['access_type'] != CEPHX_ACCESS_TYPE: raise exception.InvalidShareAccess( reason=_("Only 'cephx' access type allowed.")) if access['access_level'] == constants.ACCESS_LEVEL_RO: raise exception.InvalidShareAccessLevel( level=constants.ACCESS_LEVEL_RO) ceph_auth_id = access['access_to'] # We need to check here rather than the API or Manila Client to see # if the ceph_auth_id is the same as the one specified for Manila's # usage. This is due to the fact that the API and the Manila client # cannot read the contents of the Manila configuration file. If it # is the same, we need to error out. if ceph_auth_id == CONF.cephfs_auth_id: error_message = (_('Ceph authentication ID %s must be different ' 'than the one the Manila service uses.') % ceph_auth_id) raise exception.InvalidInput(message=error_message) auth_result = self.volume_client.authorize(self._share_path(share), ceph_auth_id) return auth_result['auth_key']
def deny_access(self, context, share, access, share_server=None): """Deny access to a share that's using cert based auth. Remove the SSL CN (Common Name) that's allowed to access the server. """ if access['access_type'] != ACCESS_TYPE_CERT: raise exception.InvalidShareAccess( _("Only 'cert' access type " "allowed for access " "removal.")) exp_locn = share.get('export_location', None) gluster_addr = self.gluster_used_vols_dict.get(exp_locn) gargs, gkw = gluster_addr.make_gluster_args('volume', 'reset', gluster_addr.volume, AUTH_SSL_ALLOW) try: self._execute(*gargs, **gkw) except exception.ProcessExecutionError as exc: msg = (_("Error in gluster volume reset during deny access. " "Volume: %(volname)s, Option: %(option)s, " "Error: %(error)s"), { 'volname': gluster_addr.volume, 'option': AUTH_SSL_ALLOW, 'error': exc.stderr }) LOG.error(msg) raise exception.GlusterfsException(msg) # TODO(deepakcs) Remove this once ssl options can be # set dynamically. self._restart_gluster_vol(gluster_addr)
def deny_access(self, context, share, access, share_server=None): """Deny access to a share that's using cert based auth. Remove the SSL CN (Common Name) that's allowed to access the server. """ if access['access_type'] != ACCESS_TYPE_CERT: raise exception.InvalidShareAccess( _("Only 'cert' access type " "allowed for access " "removal.")) gargs, gkw = self.gluster_address.make_gluster_args( 'volume', 'reset', self.gluster_address.volume, AUTH_SSL_ALLOW) try: self._execute(*gargs, **gkw) except exception.ProcessExecutionError as exc: LOG.error( _("Error in gluster volume reset during deny access." "Volume: %(volname)s, Option: %(option)s, " "Error: %(error)s"), { 'volname': self.gluster_address.volume, 'option': AUTH_SSL_ALLOW, 'error': exc.stderr }) raise
def allow_access(self, context, share, share_name, access): """Allows access to the CIFS share for a given user.""" if access['access_type'] != 'user': msg = _("Cluster Mode supports only 'user' type for share access" " rules with CIFS protocol.") raise exception.InvalidShareAccess(reason=msg) user_name = access['access_to'] if access['access_level'] == constants.ACCESS_LEVEL_RW: readonly = False elif access['access_level'] == constants.ACCESS_LEVEL_RO: readonly = True else: raise exception.InvalidShareAccessLevel( level=access['access_level']) target, share_name = self._get_export_location(share) try: self._client.add_cifs_share_access(share_name, user_name, readonly) except netapp_api.NaApiError as e: if e.code == netapp_api.EDUPLICATEENTRY: # Duplicate entry, so use specific exception. raise exception.ShareAccessExists( access_type=access['access_type'], access=access) raise e
def extend_share(self, share, new_size, share_server): share_proto = share['share_proto'] share_name = share['name'] # The unit is in sectors. size = int(new_size) * units.Mi * 2 share_url_type = self.helper._get_share_url_type(share_proto) share = self.helper._get_share_by_name(share_name, share_url_type) if not share: err_msg = (_("Can not get share ID by share %s.") % share_name) LOG.error(err_msg) raise exception.InvalidShareAccess(reason=err_msg) fsid = share['FSID'] fs_info = self.helper._get_fs_info_by_id(fsid) current_size = int(fs_info['CAPACITY']) / units.Mi / 2 if current_size > new_size: err_msg = (_("New size for extend must be equal or bigger than " "current size on array. (current: %(size)s, " "new: %(new_size)s).") % {'size': current_size, 'new_size': new_size}) LOG.error(err_msg) raise exception.InvalidInput(reason=err_msg) self.helper._change_share_size(fsid, size)
def allow_access(self, context, share, access, share_server=None): """Allow access to a share using certs. Add the SSL CN (Common Name) that's allowed to access the server. """ if access['access_type'] != ACCESS_TYPE_CERT: raise exception.InvalidShareAccess( _("Only 'cert' access type " "allowed")) gargs, gkw = self.gluster_address.make_gluster_args( 'volume', 'set', self.gluster_address.volume, AUTH_SSL_ALLOW, access['access_to']) try: self._execute(*gargs, **gkw) except exception.ProcessExecutionError as exc: LOG.error( _("Error in gluster volume set during allow access." "Volume: %(volname)s, Option: %(option)s, " "access_to: %(access_to)s, Error: %(error)s"), { 'volname': self.gluster_address.volume, 'option': AUTH_SSL_ALLOW, 'access_to': access['access_to'], 'error': exc.stderr }) raise
def _allow_access(self, context, share, access, share_server=None): if access['access_type'] != CEPHX_ACCESS_TYPE: raise exception.InvalidShareAccess( reason=_("Only 'cephx' access type allowed.")) ceph_auth_id = access['access_to'] # We need to check here rather than the API or Manila Client to see # if the ceph_auth_id is the same as the one specified for Manila's # usage. This is due to the fact that the API and the Manila client # cannot read the contents of the Manila configuration file. If it # is the same, we need to error out. if ceph_auth_id == CONF.cephfs_auth_id: error_message = (_('Ceph authentication ID %s must be different ' 'than the one the Manila service uses.') % ceph_auth_id) raise exception.InvalidInput(message=error_message) # TODO(rraja): Log the Ceph point release version, once available, in # which the volume client can enable read-only access. if not getattr(self.volume_client, 'version', None): if access['access_level'] == constants.ACCESS_LEVEL_RO: raise exception.InvalidShareAccessLevel( level=constants.ACCESS_LEVEL_RO) auth_result = self.volume_client.authorize( self._share_path(share), ceph_auth_id) else: readonly = access['access_level'] == constants.ACCESS_LEVEL_RO auth_result = self.volume_client.authorize( self._share_path(share), ceph_auth_id, readonly=readonly, tenant_id=share['project_id']) return auth_result['auth_key']
def allow_access(self, ctx, share, access_type, access_to, access_level=None): """Allow access to share.""" if not share['host']: msg = _("Share host is None") raise exception.InvalidShare(reason=msg) if share['status'] not in ["available"]: msg = _("Share status must be available") raise exception.InvalidShare(reason=msg) policy.check_policy(ctx, 'share', 'allow_access') values = { 'share_id': share['id'], 'access_type': access_type, 'access_to': access_to, 'access_level': access_level, } access = [ a for a in self.db.share_access_get_all_by_type_and_access( ctx, share['id'], access_type, access_to) if a['state'] != 'error' ] if access: raise exception.ShareAccessExists(access_type=access_type, access=access_to) if access_level not in constants.ACCESS_LEVELS + (None, ): msg = _("Invalid share access level: %s.") % access_level raise exception.InvalidShareAccess(reason=msg) access = self.db.share_access_create(ctx, values) self.share_rpcapi.allow_access(ctx, share, access) return access
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 _cifs_deny_access(self, share, access, share_server): """Deny access to CIFS share.""" vdm_name = self._get_share_server_name(share_server) share_name = share['id'] if access['access_type'] != 'user': reason = _('Only user access type allowed for CIFS share') raise exception.InvalidShareAccess(reason=reason) user_name = access['access_to'] access_level = access['access_level'] if access_level == const.ACCESS_LEVEL_RW: cifs_access = constants.CIFS_ACL_FULLCONTROL else: cifs_access = constants.CIFS_ACL_READ # Check if CIFS server exists. server_name = vdm_name status, server = self._get_context('CIFSServer').get( server_name, vdm_name) if status != constants.STATUS_OK: message = (_("CIFS server %s not found.") % server_name) LOG.error(message) raise exception.EMCVnxXMLAPIError(err=message) self._get_context('CIFSShare').deny_share_access(vdm_name, share_name, user_name, server['domain'], access=cifs_access)
def _manage_access(self, access_type, access_to, cbk): """Manage share access with cbk. Adjust the exports of the Gluster-NFS server using cbk. :param access_type: type of access allowed in Manila :type access_type: string :param access_to: ip of the guest whose share access is managed :type access_to: string :param cbk: callback to adjust the exports of NFS server Following is the description of cbk(explist, host). :param explist: list of hosts that have access to the share :type explist: list :param host: ip address derived from the access object :type host: string :returns: bool (cbk leaves ddict intact) or None (cbk modifies ddict) """ if access_type != 'ip': raise exception.InvalidShareAccess('only ip access type allowed') export_vol_list = self._get_vol_exports() if cbk(export_vol_list, access_to): return if export_vol_list: argseq = ((NFS_RPC_AUTH_ALLOW, ','.join(export_vol_list)), (NFS_RPC_AUTH_REJECT, None)) else: argseq = ((NFS_RPC_AUTH_ALLOW, None), (NFS_RPC_AUTH_REJECT, '*')) for args in argseq: self.gluster_manager.set_vol_option(*args)
def update_access(self, share_name, access_rules): """Update access to the share.""" rw_list = [] ro_list = [] for rule in access_rules: if rule['access_type'].lower() != 'ip': msg = _('Only IP access type is supported.') raise exception.InvalidShareAccess(reason=msg) else: if rule['access_level'] == common.ACCESS_LEVEL_RW: rw_list.append(rule['access_to']) else: ro_list.append(rule['access_to']) share_opts = { 'auth_type': 'none', 'read_write': ':'.join(rw_list), 'read_only': ':'.join(ro_list), 'recursive': 'true', 'anonymous_rw': 'true', 'anonymous': 'true', 'extra_options': 'anon=0', } self.nms.netstorsvc.share_folder( 'svc:/network/nfs/server:default', self._get_share_path(share_name), share_opts)
def _allow_access(self, context, share, access, share_server=None): """Allow access to the share.""" share_proto = share['share_proto'] access_type = access['access_type'] access_level = access['access_level'] access_to = access['access_to'] LOG.debug( 'share_proto: %(share_proto)s ' 'access_type: %(access_type)s ' 'access_level: %(access_level)s ' 'access_to: %(access_to)s', { 'share_proto': share_proto, 'access_type': access_type, 'access_level': access_level, 'access_to': access_to }) self._check_share_access(share_proto, access_type) vol_name = self.private_storage.get(share['id'], 'volName') vol_name_timestamp = self._get_timestamp_from_vol_name(vol_name) host_name = self._gen_host_name(vol_name_timestamp, access_level) host_list = self.api_executor.get_host_list() LOG.debug( 'vol_name: %(vol_name)s ' 'access_level: %(access_level)s ' 'host_name: %(host_name)s ' 'host_list: %(host_list)s ', { 'vol_name': vol_name, 'access_level': access_level, 'host_name': host_name, 'host_list': host_list }) filter_host_list = self._get_vol_host(host_list, vol_name_timestamp) if len(filter_host_list) == 0: # if host does not exist, create a host for the share self.api_executor.add_host(host_name, access_to) elif (len(filter_host_list) == 1 and filter_host_list[0]['name'] == host_name): # if the host exist, and this host is for the same access right, # add ip to the host. ipv4_list = filter_host_list[0]['ipv4'] if access_to not in ipv4_list: ipv4_list.append(access_to) LOG.debug('vol_host["ipv4"]: %s', filter_host_list[0]['ipv4']) LOG.debug('ipv4_list: %s', ipv4_list) self.api_executor.edit_host(host_name, ipv4_list) else: # Until now, share of QNAP NAS can only apply one access level for # all ips. "rw" for some ips and "ro" for else is not allowed. support_level = (constants.ACCESS_LEVEL_RW if access_level == constants.ACCESS_LEVEL_RO else constants.ACCESS_LEVEL_RO) reason = _('Share only supports one access ' 'level: %s') % support_level LOG.error(reason) raise exception.InvalidShareAccess(reason=reason) access = 1 if access_level == constants.ACCESS_LEVEL_RO else 0 self.api_executor.set_nfs_access(vol_name, access, host_name)
def update_access(self, context, share, access_rules, add_rules, delete_rules, share_server=None): if share['share_proto'] != 'NFS': return validate_access_rules(access_rules) share_id = share['id'] export = self._to_volume_path(share) LOG.info("Changing access on %s", share_server) levels = {rule['access_level'] for rule in access_rules if rule} if not levels: return if len(levels) > 1: raise exception.InvalidShareAccess( reason= "Mixing access levels on the same share is not supported: %s" % levels) access_type = _MANILA_TO_VAST_ACCESS_LEVEL[levels.pop()] def reverse_lookup(dns): if RE_IS_IP.match(dns): return [dns] try: hostname, aliaslist, ipaddrlist = socket.gethostbyname_ex(dns) except socket.gaierror as exc: LOG.error("Failed to resolve host '%s': %s (ignoring)", dns, exc) return [] LOG.info("resolved %s: %s", hostname, ", ".join(ipaddrlist)) return ipaddrlist allowed_hosts = ",".join( sorted(ip for rule in access_rules if rule for ip in reverse_lookup(rule['access_to']))) LOG.info("Changing access on %s -> %s (%s)", export, allowed_hosts, access_type) data = dict(name=share_id, squash="ROOT_SQUASH", access_type=access_type, allowed_hosts=allowed_hosts) policy = self._get_policy(share_id) if policy: self.vms_session.patch("viewpolicies/{}".format(policy.id), data=data) else: self.vms_session.post("viewpolicies", data=data)
def _allow_access(self, context, share, access, share_server=None): if access['access_type'] != CEPHX_ACCESS_TYPE: raise exception.InvalidShareAccessType(type=access['access_type']) ceph_auth_id = access['access_to'] # We need to check here rather than the API or Manila Client to see # if the ceph_auth_id is the same as the one specified for Manila's # usage. This is due to the fact that the API and the Manila client # cannot read the contents of the Manila configuration file. If it # is the same, we need to error out. if ceph_auth_id == CONF.cephfs_auth_id: error_message = (_('Ceph authentication ID %s must be different ' 'than the one the Manila service uses.') % ceph_auth_id) raise exception.InvalidShareAccess(reason=error_message) argdict = { "vol_name": self.volname, "sub_name": share["id"], "auth_id": ceph_auth_id, "tenant_id": share["project_id"], } if share["share_group_id"] is not None: argdict.update({"group_name": share["share_group_id"]}) readonly = access['access_level'] == constants.ACCESS_LEVEL_RO if readonly: argdict.update({"access_level": "r"}) else: argdict.update({"access_level": "rw"}) try: auth_result = rados_command(self.rados_client, "fs subvolume authorize", argdict) except exception.ShareBackendException as e: if 'not allowed' in str(e).lower(): msg = ("Access to client %(client)s is not allowed. " "Reason: %(reason)s") msg_payload = {'client': ceph_auth_id, 'reason': e} raise exception.InvalidShareAccess(reason=msg % msg_payload) raise return auth_result
def _validate_access_level(protocol, access_type, access_level, fshare): readonly = access_level == 'ro' snapshot = HPE3ParMediator._is_share_from_snapshot(fshare) if snapshot and not readonly: reason = _('3PAR shares from snapshots require read-only access') LOG.error(reason) raise exception.InvalidShareAccess(reason=reason) if protocol == 'smb' and access_type == 'ip' and snapshot != readonly: msg = (_("Invalid CIFS access rule. HPE 3PAR optionally supports " "IP access rules for CIFS shares, but they must be " "read-only for shares from snapshots and read-write for " "other shares. Use the required CIFS 'user' access rules " "to refine access.")) LOG.error(msg) raise exception.InvalidShareAccess(reason=msg)
def update_access(self, context, share, access_rules, add_rules, delete_rules, share_server=None): """Update access rules for given share. Using access_rules list for both adding and deleting rules. :param context: The `context.RequestContext` object for the request :param share: Share that will have its access rules updated. :param access_rules: All access rules for given share. This list is enough to update the access rules for given share. :param add_rules: Empty List or List of access rules which should be added. access_rules already contains these rules. Not used by this driver. :param delete_rules: Empty List or List of access rules which should be removed. access_rules doesn't contain these rules. Not used by this driver. :param share_server: Data structure with share server information. Not used by this driver. """ LOG.debug('Updating access to share %s.', share) rw_list = [] ro_list = [] security_contexts = [] for rule in access_rules: if rule['access_type'].lower() != 'ip': msg = _('Only IP access type is supported.') raise exception.InvalidShareAccess(reason=msg) else: if rule['access_level'] == common.ACCESS_LEVEL_RW: rw_list.append(rule['access_to']) else: ro_list.append(rule['access_to']) def append_sc(addr_list, sc_type): for addr in addr_list: address_mask = addr.strip().split('/', 1) address = address_mask[0] ls = [{"allow": True, "etype": "network", "entity": address}] if len(address_mask) == 2: try: mask = int(address_mask[1]) if mask != 32: ls[0]['mask'] = mask except Exception: raise exception.InvalidInput( reason=_( '<{}> is not a valid access parameter').format( addr)) new_sc = {"securityModes": ["sys"]} new_sc[sc_type] = ls security_contexts.append(new_sc) append_sc(rw_list, 'readWriteList') append_sc(ro_list, 'readOnlyList') data = {"securityContexts": security_contexts} url = 'nas/nfs/' + PATH_DELIMITER.join( (self.pool_name, self.fs_prefix, share['name'])) self.nef.put(url, data)