]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr: Add command to create exports for nfs-ganesha
authorVarsha Rao <varao@redhat.com>
Fri, 22 Nov 2019 05:34:01 +0000 (11:04 +0530)
committerVarsha Rao <varao@redhat.com>
Wed, 8 Apr 2020 09:19:43 +0000 (14:49 +0530)
'ceph fs nfs create': This command creates export objects for nfs-ganesha.
vstart ganesha daemon fetches the export block from rados object.

Fixes: https://tracker.ceph.com/issues/44193
Signed-off-by: Varsha Rao <varao@redhat.com>
src/pybind/mgr/volumes/fs/nfs.py [new file with mode: 0644]
src/pybind/mgr/volumes/module.py
src/vstart.sh

diff --git a/src/pybind/mgr/volumes/fs/nfs.py b/src/pybind/mgr/volumes/fs/nfs.py
new file mode 100644 (file)
index 0000000..9ef45aa
--- /dev/null
@@ -0,0 +1,186 @@
+import json
+import logging
+import cephfs
+import orchestrator
+from dashboard.services.cephx import CephX
+from dashboard.services.ganesha import Ganesha, NFSException, Export, GaneshaConfParser
+
+log = logging.getLogger(__name__)
+
+class GaneshaConf(object):
+    # pylint: disable=R0902
+
+    def __init__(self, cluster_id, rados_pool, rados_namespace, mgr):
+        self.mgr = mgr
+        self.cephx_key = ""
+        self.cluster_id = cluster_id
+        self.rados_pool = rados_pool
+        self.rados_namespace = rados_namespace
+        self.export_conf_blocks = []
+        self.daemons_conf_blocks = {}
+        self._defaults = {}
+        self.exports = {}
+
+        self._read_raw_config()
+
+        # load defaults
+        def_block = [b for b in self.export_conf_blocks
+                     if b['block_name'] == "EXPORT_DEFAULTS"]
+        self.export_defaults = def_block[0] if def_block else {}
+        self._defaults = self.ganesha_defaults(self.export_defaults)
+
+        for export_block in [block for block in self.export_conf_blocks
+                             if block['block_name'] == "EXPORT"]:
+            export = Export.from_export_block(export_block, cluster_id,
+                                              self._defaults)
+            self.exports[export.export_id] = export
+
+        # link daemons to exports
+        for daemon_id, daemon_blocks in self.daemons_conf_blocks.items():
+            for block in daemon_blocks:
+                if block['block_name'] == "%url":
+                    rados_url = block['value']
+                    _, _, obj = Ganesha.parse_rados_url(rados_url)
+                    if obj.startswith("export-"):
+                        export_id = int(obj[obj.find('-')+1:])
+                        self.exports[export_id].daemons.add(daemon_id)
+
+    def _read_raw_config(self):
+        with self.mgr.rados.open_ioctx(self.rados_pool) as ioctx:
+            if self.rados_namespace:
+                ioctx.set_namespace(self.rados_namespace)
+            objs = ioctx.list_objects()
+            for obj in objs:
+                if obj.key.startswith("export-"):
+                    size, _ = obj.stat()
+                    raw_config = obj.read(size)
+                    raw_config = raw_config.decode("utf-8")
+                    log.debug("read export configuration from rados "
+                              "object %s/%s/%s:\n%s", self.rados_pool,
+                              self.rados_namespace, obj.key, raw_config)
+                    self.export_conf_blocks.extend(
+                        GaneshaConfParser(raw_config).parse())
+                elif obj.key.startswith("conf-"):
+                    size, _ = obj.stat()
+                    raw_config = obj.read(size)
+                    raw_config = raw_config.decode("utf-8")
+                    log.debug("read daemon configuration from rados "
+                              "object %s/%s/%s:\n%s", self.rados_pool,
+                              self.rados_namespace, obj.key, raw_config)
+
+                    idx = obj.key.find('-')
+                    self.daemons_conf_blocks[obj.key[idx+1:]] = \
+                        GaneshaConfParser(raw_config).parse()
+
+    def _write_raw_config(self, conf_block, obj):
+        raw_config = GaneshaConfParser.write_conf(conf_block)
+        with self.mgr.rados.open_ioctx(self.rados_pool) as ioctx:
+            if self.rados_namespace:
+                ioctx.set_namespace(self.rados_namespace)
+            ioctx.write_full(obj, raw_config.encode('utf-8'))
+            log.debug(
+                    "write configuration into rados object %s/%s/%s:\n%s",
+                    self.rados_pool, self.rados_namespace, obj, raw_config)
+
+
+    @classmethod
+    def ganesha_defaults(cls, export_defaults):
+        """
+        According to
+        https://github.com/nfs-ganesha/nfs-ganesha/blob/next/src/config_samples/export.txt
+        """
+        return {
+            'access_type': export_defaults.get('access_type', 'NONE'),
+            'protocols': export_defaults.get('protocols', [3, 4]),
+            'transports': export_defaults.get('transports', ['TCP', 'UDP']),
+            'squash': export_defaults.get('squash', 'root_squash')
+        }
+
+    def _gen_export_id(self):
+        exports = sorted(self.exports)
+        nid = 1
+        for e_id in exports:
+            if e_id == nid:
+                nid += 1
+            else:
+                break
+        return nid
+
+    def fill_keys(self, export):
+        r, auth_dump, outs = self.mgr.mon_command({'prefix':"auth list", "format":"json"})
+        auth_dump_ls = json.loads(auth_dump)
+        result = {}
+        entity_type = "client"
+        for auth_entry in auth_dump_ls['auth_dump']:
+            entity = auth_entry['entity']
+            if not entity_type or entity.startswith('{}.'.format(entity_type)):
+                entity_id = entity[entity.find('.')+1:]
+                result[entity_id] = auth_entry
+        self.cephx_key = result["admin"]["key"]
+        export.fsal.cephx_key = self.cephx_key
+
+    def _persist_daemon_configuration(self):
+        daemon_map = {}
+        """
+        for daemon_id in self.list_daemons():
+            daemon_map[daemon_id] = []
+        """
+        daemon_map["ganesha.a"] = []
+
+        for _, ex in self.exports.items():
+            for daemon in ex.daemons:
+                daemon_map[daemon].append({
+                    'block_name': "%url",
+                    'value': Ganesha.make_rados_url(
+                        self.rados_pool, self.rados_namespace,
+                        "export-{}".format(ex.export_id))
+                })
+        for daemon_id, conf_blocks in daemon_map.items():
+            self._write_raw_config(conf_blocks, "conf-{}".format(daemon_id))
+
+
+    def _save_export(self, export):
+        self.fill_keys(export)
+        self.exports[export.export_id] = export
+        conf_block = export.to_export_block(self.export_defaults)
+        self._write_raw_config(conf_block, "export-{}".format(export.export_id))
+        self._persist_daemon_configuration()
+
+    def create_export(self, ex_dict):
+        ex_id = self._gen_export_id()
+        export = Export.from_dict(ex_id, ex_dict)
+        self._save_export(export)
+        return ex_id
+
+    def list_daemons(self):
+        return [daemon_id for daemon_id in self.daemons_conf_blocks]
+
+
+def create_instance(orch):
+    return GaneshaConf("a", "nfs-ganesha", "ganesha", orch)
+
+def create_export(ganesha_conf):
+    ex_id = ganesha_conf.create_export({
+        'path': "/",
+        'pseudo': "/cephfs",
+        'cluster_id': "a",
+        'daemons': ["ganesha.a"],
+        'tag': "",
+        'access_type': "RW",
+        'squash': "no_root_squash",
+        'security_label': True,
+        'protocols': [4],
+        'transports': ["TCP"],
+        'fsal': {"name": "CEPH", "user_id":"admin", "fs_name": "a", "sec_label_xattr": ""},
+        'clients': []
+        })
+
+    log.info("Export ID is {}".format(ex_id))
+    return 0, "", ""
+
+def check_fsal_valid(fs_map):
+    fsmap_res = [{'id': fs['id'], 'name': fs['mdsmap']['fs_name']}
+            for fs in fs_map['filesystems']]
+
+    #return 0, json.dumps(fsmap_res, indent=2), ""
+    return fsmap_res
index 0d8015bf48f4d1b09deaebd628ba64711e6ed197..45a86884ede5ce778a9a54d126f7fabf06a27f6f 100644 (file)
@@ -5,6 +5,7 @@ from mgr_module import MgrModule
 import orchestrator
 
 from .fs.volume import VolumeClient
+from .fs.nfs import check_fsal_valid, create_instance, create_export
 
 class Module(orchestrator.OrchestratorClientMixin, MgrModule):
     COMMANDS = [
@@ -212,6 +213,11 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
             'desc': "Cancel an pending or ongoing clone operation.",
             'perm': 'r'
         },
+        {
+            'cmd': 'fs nfs create',
+            'desc': "Create dummy exports",
+            'perm': 'rw'
+        },
 
         # volume ls [recursive]
         # subvolume ls <volume>
@@ -376,3 +382,8 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
     def _cmd_fs_clone_cancel(self, inbuf, cmd):
         return self.vc.clone_cancel(
             vol_name=cmd['vol_name'], clone_name=cmd['clone_name'],  group_name=cmd.get('group_name', None))
+
+    def _cmd_fs_nfs_create(self, inbuf, cmd):
+        if check_fsal_valid(self.vc.mgr.get('fs_map')):
+            instance = create_instance(self)
+            return create_export(instance)
index 31c67ee45dea3e090030254daaa4245541a50edc..017b7f3e5a89a01829f4975e2250b72030e26fc3 100755 (executable)
@@ -1108,24 +1108,17 @@ NFSv4 {
         Minor_Versions = 1, 2;
 }
 
-EXPORT {
-       Export_Id = 100;
-       Transports = TCP;
-       Path = /;
-       Pseudo = /ceph/;
-       Protocols = 4;
-       Access_Type = RW;
-       Attr_Expiration_Time = 0;
-       Squash = None;
-       FSAL {
-           Name = CEPH;
-       }
+CEPH {
+       Ceph_Conf = $conf_fn;
 }
 
-CEPH {
-       Ceph_Conf = $conf_fn;
+RADOS_URLS {
+          ceph_conf = $conf_fn;
+          userid = "admin";
 }
 
+%url rados://nfs-ganesha/ganesha/export-1
+
 RADOS_KV {
        Ceph_Conf = $conf_fn;
        pool = 'nfs-ganesha';
@@ -1149,6 +1142,7 @@ EOF
             prun ceph_adm osd pool application enable nfs-ganesha nfs
         fi
 
+        prun ceph_adm fs nfs create
         prun ganesha-rados-grace -p nfs-ganesha -n ganesha add $name
         prun ganesha-rados-grace -p nfs-ganesha -n ganesha