From: Varsha Rao Date: Mon, 27 Jul 2020 12:56:45 +0000 (+0530) Subject: mgr/volumes/nfs: Add interface for adding user defined configuration X-Git-Tag: wip-pdonnell-testing-20200918.022351~385^2~2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=9535154794c1d6d116b81c1317c48ba1aecd7581;p=ceph-ci.git mgr/volumes/nfs: Add interface for adding user defined configuration Code related to creating and removing rados object has been rearranged abd modified. This patch introduces interface to set and reset user config. nfs cluster config set -i nfs cluster config reset Fixes: https://tracker.ceph.com/issues/45747 Signed-off-by: Varsha Rao --- diff --git a/src/pybind/mgr/volumes/fs/nfs.py b/src/pybind/mgr/volumes/fs/nfs.py index a08646d3365..11bcc5fd9d3 100644 --- a/src/pybind/mgr/volumes/fs/nfs.py +++ b/src/pybind/mgr/volumes/fs/nfs.py @@ -13,6 +13,7 @@ import orchestrator from .fs_util import create_pool log = logging.getLogger(__name__) +POOL_NAME = 'nfs-ganesha' def available_clusters(mgr): @@ -290,6 +291,63 @@ class Client(object): } +class NFSRados: + def __init__(self, mgr, namespace): + self.mgr = mgr + self.pool = POOL_NAME + self.namespace = namespace + + def _make_rados_url(self, obj): + return "rados://{}/{}/{}".format(self.pool, self.namespace, obj) + + def _create_url_block(self, obj_name): + return {'block_name': '%url', 'value': self._make_rados_url(obj_name)} + + def write_obj(self, conf_block, obj, config_obj=''): + if 'export-' in obj: + conf_block = GaneshaConfParser.write_block(conf_block) + + with self.mgr.rados.open_ioctx(self.pool) as ioctx: + ioctx.set_namespace(self.namespace) + ioctx.write_full(obj, conf_block.encode('utf-8')) + if not config_obj: + # Return after creating empty common config object + return + log.debug("write configuration into rados object " + f"{self.pool}/{self.namespace}/{obj}:\n{conf_block}") + + # Add created obj url to common config obj + ioctx.append(config_obj, GaneshaConfParser.write_block( + self._create_url_block(obj)).encode('utf-8')) + FSExport._check_rados_notify(ioctx, config_obj) + log.debug(f"Added {obj} url to {config_obj}") + + def remove_obj(self, obj, config_obj): + with self.mgr.rados.open_ioctx(self.pool) as ioctx: + ioctx.set_namespace(self.namespace) + export_urls = ioctx.read(config_obj) + url = '%url "{}"\n\n'.format(self._make_rados_url(obj)) + export_urls = export_urls.replace(url.encode('utf-8'), b'') + ioctx.remove_object(obj) + ioctx.write_full(config_obj, export_urls) + FSExport._check_rados_notify(ioctx, config_obj) + log.debug("Object deleted: {}".format(url)) + + def remove_all_obj(self): + with self.mgr.rados.open_ioctx(self.pool) as ioctx: + ioctx.set_namespace(self.namespace) + for obj in ioctx.list_objects(): + obj.remove() + + def check_user_config(self): + with self.mgr.rados.open_ioctx(self.pool) as ioctx: + ioctx.set_namespace(self.namespace) + for obj in ioctx.list_objects(): + if obj.key.startswith("userconf-nfs"): + return True + return False + + class Export(object): # pylint: disable=R0902 def __init__(self, export_id, path, fsal, cluster_id, pseudo, @@ -374,7 +432,7 @@ class Export(object): class FSExport(object): def __init__(self, mgr, namespace=None): self.mgr = mgr - self.rados_pool = 'nfs-ganesha' + self.rados_pool = POOL_NAME self.rados_namespace = namespace self._exports = None @@ -451,47 +509,10 @@ class FSExport(object): self.export_conf_objs.append(Export.from_export_block( GaneshaConfParser(raw_config).parse()[0], rados_namespace)) - def _write_raw_config(self, conf_block, obj, append=False): - raw_config = GaneshaConfParser.write_block(conf_block) - with self.mgr.rados.open_ioctx(self.rados_pool) as ioctx: - if self.rados_namespace: - ioctx.set_namespace(self.rados_namespace) - if append: - ioctx.append(obj, raw_config.encode('utf-8')) - FSExport._check_rados_notify(ioctx, obj) - else: - ioctx.write_full(obj, raw_config.encode('utf-8')) - log.debug("write configuration into rados object " - f"{self.rados_pool}/{self.rados_namespace}/{obj}:\n{raw_config}") - - def _make_rados_url(self, obj): - return "rados://{}/{}/{}".format(self.rados_pool, self.rados_namespace, obj) - - def _delete_export_url(self, obj, ex_id): - export_name = 'export-{}'.format(ex_id) - with self.mgr.rados.open_ioctx(self.rados_pool) as ioctx: - ioctx.set_namespace(self.rados_namespace) - export_urls = ioctx.read(obj) - url = '%url "{}"\n\n'.format(self._make_rados_url(export_name)) - export_urls = export_urls.replace(url.encode('utf-8'), b'') - ioctx.remove_object(export_name) - ioctx.write_full(obj, export_urls) - FSExport._check_rados_notify(ioctx, obj) - log.debug("Export deleted: {}".format(url)) - - def _update_common_conf(self, cluster_id, ex_id): - common_conf = 'conf-nfs.ganesha-{}'.format(cluster_id) - conf_blocks = { - 'block_name': '%url', - 'value': self._make_rados_url('export-{}'.format(ex_id)) - } - self._write_raw_config(conf_blocks, common_conf, True) - def _save_export(self, export): self.exports[self.rados_namespace].append(export) - conf_block = export.to_export_block() - self._write_raw_config(conf_block, "export-{}".format(export.export_id)) - self._update_common_conf(export.cluster_id, export.export_id) + NFSRados(self.mgr, self.rados_namespace).write_obj(export.to_export_block(), + f'export-{export.export_id}', f'conf-nfs.ganesha-{export.cluster_id}') def _delete_export(self, cluster_id, pseudo_path, export_obj=None): try: @@ -501,8 +522,9 @@ class FSExport(object): export = self._fetch_export(pseudo_path) if export: - common_conf = 'conf-nfs.ganesha-{}'.format(cluster_id) - self._delete_export_url(common_conf, export.export_id) + if pseudo_path: + NFSRados(self.mgr, self.rados_namespace).remove_obj( + f'export-{export.export_id}', f'conf-nfs.ganesha-{cluster_id}') self.exports[cluster_id].remove(export) self._delete_user(export.fsal.user_id) if not self.exports[cluster_id]: @@ -618,7 +640,7 @@ class FSExport(object): class NFSCluster: def __init__(self, mgr): - self.pool_name = 'nfs-ganesha' + self.pool_name = POOL_NAME self.pool_ns = '' self.mgr = mgr @@ -629,7 +651,10 @@ class NFSCluster: self.pool_ns = cluster_id def _get_common_conf_obj_name(self): - return 'conf-nfs.{}'.format(self.cluster_id) + return f'conf-nfs.{self.cluster_id}' + + def _get_user_conf_obj_name(self): + return f'userconf-nfs.{self.cluster_id}' def _call_orch_apply_nfs(self, placement): spec = NFSServiceSpec(service_type='nfs', service_id=self.cluster_id, @@ -641,19 +666,19 @@ class NFSCluster: def create_empty_rados_obj(self): common_conf = self._get_common_conf_obj_name() - result = '' - with self.mgr.rados.open_ioctx(self.pool_name) as ioctx: - ioctx.set_namespace(self.pool_ns) - ioctx.write_full(common_conf, result.encode('utf-8')) - log.debug("write configuration into rados object " - f"{self.pool_name}/{self.pool_ns}/{common_conf}\n") + NFSRados(self.mgr, self.pool_ns).write_obj('', self._get_common_conf_obj_name()) + log.info(f"Created empty object:{common_conf}") - def delete_common_config_obj(self): - common_conf = self._get_common_conf_obj_name() - with self.mgr.rados.open_ioctx(self.pool_name) as ioctx: - ioctx.set_namespace(self.pool_ns) - ioctx.remove_object(common_conf) - log.info(f"Deleted object:{common_conf}") + def delete_config_obj(self): + NFSRados(self.mgr, self.pool_ns).remove_all_obj() + log.info(f"Deleted {self._get_common_conf_obj_name()} object and all objects in " + f"{self.pool_ns}") + + def _restart_nfs_service(self): + completion = self.mgr.service_action(action='restart', + service_name='nfs.'+self.cluster_id) + self.mgr._orchestrator_wait([completion]) + orchestrator.raise_if_exception(completion) @cluster_setter def create_nfs_cluster(self, export_type, cluster_id, placement): @@ -701,8 +726,7 @@ class NFSCluster: completion = self.mgr.remove_service('nfs.' + self.cluster_id) self.mgr._orchestrator_wait([completion]) orchestrator.raise_if_exception(completion) - if len(cluster_list) == 1: - self.delete_common_config_obj() + self.delete_config_obj() return 0, "NFS Cluster Deleted Successfully", "" return 0, "", "Cluster does not exist" except Exception as e: @@ -760,3 +784,37 @@ class NFSCluster: except Exception as e: log.exception(f"Failed to show info for cluster") return getattr(e, 'errno', -1), "", str(e) + + @cluster_setter + def set_nfs_cluster_config(self, cluster_id, nfs_config): + try: + if not nfs_config: + return -errno.EINVAL, "", "Empty Config!!" + if cluster_id in available_clusters(self.mgr): + rados_obj = NFSRados(self.mgr, self.pool_ns) + if rados_obj.check_user_config(): + return 0, "", "NFS-Ganesha User Config already exists" + rados_obj.write_obj(nfs_config, self._get_user_conf_obj_name(), + self._get_common_conf_obj_name()) + self._restart_nfs_service() + return 0, "NFS-Ganesha Config Set Successfully", "" + return -errno.ENOENT, "", "Cluster does not exist" + except Exception as e: + log.exception(f"Setting NFS-Ganesha Config failed for {cluster_id}") + return getattr(e, 'errno', -1), "", str(e) + + @cluster_setter + def reset_nfs_cluster_config(self, cluster_id): + try: + if cluster_id in available_clusters(self.mgr): + rados_obj = NFSRados(self.mgr, self.pool_ns) + if not rados_obj.check_user_config(): + return 0, "", "NFS-Ganesha User Config does not exist" + rados_obj.remove_obj(self._get_user_conf_obj_name(), + self._get_common_conf_obj_name()) + self._restart_nfs_service() + return 0, "NFS-Ganesha Config Reset Successfully", "" + return -errno.ENOENT, "", "Cluster does not exist" + except Exception as e: + log.exception(f"Resetting NFS-Ganesha Config failed for {cluster_id}") + return getattr(e, 'errno', -1), "", str(e) diff --git a/src/pybind/mgr/volumes/module.py b/src/pybind/mgr/volumes/module.py index 60bd597a9f1..c611a5df782 100644 --- a/src/pybind/mgr/volumes/module.py +++ b/src/pybind/mgr/volumes/module.py @@ -344,6 +344,18 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): 'desc': "Displays NFS Cluster info", 'perm': 'r' }, + { + 'cmd': 'nfs cluster config set ' + 'name=clusterid,type=CephString ', + 'desc': "Set NFS-Ganesha config by `-i `", + 'perm': 'rw' + }, + { + 'cmd': 'nfs cluster config reset ' + 'name=clusterid,type=CephString ', + 'desc': "Reset NFS-Ganesha Config to default", + 'perm': 'rw' + }, # volume ls [recursive] # subvolume ls # volume authorize/deauthorize @@ -599,3 +611,9 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): @mgr_cmd_wrap def _cmd_nfs_cluster_info(self, inbuf, cmd): return self.nfs.show_nfs_cluster_info(cluster_id=cmd.get('clusterid', None)) + + def _cmd_nfs_cluster_config_set(self, inbuf, cmd): + return self.nfs.set_nfs_cluster_config(cluster_id=cmd['clusterid'], nfs_config=inbuf) + + def _cmd_nfs_cluster_config_reset(self, inbuf, cmd): + return self.nfs.reset_nfs_cluster_config(cluster_id=cmd['clusterid'])