]> git.proxmox.com Git - mirror_frr.git/commitdiff
yang: embed models into binaries
authorDavid Lamparter <equinox@opensourcerouting.org>
Mon, 19 Nov 2018 18:18:37 +0000 (19:18 +0100)
committerDavid Lamparter <equinox@opensourcerouting.org>
Mon, 19 Nov 2018 22:45:24 +0000 (23:45 +0100)
This bakes our YANG models straight into the library/daemons, so they
don't need to be loaded from /usr/share/yang.  This makes the
installation quite a bit more robust, as well as gets us halfway to
running uninstalled.  (The other half is baking in the extension type
module.)

The /usr/share/yang directory is still searched as a fallback, as well
as for the experimental YANG model translator.  This is likely to stay
as is for the time being.

Signed-off-by: David Lamparter <equinox@diac24.net>
lib/subdir.am
lib/yang.c
lib/yang.h
ripd/subdir.am
yang/embedmodel.py [new file with mode: 0644]
yang/subdir.am

index c144c2c2e1d87205ca6050b7e44734418fa9eb17..43b39100cb3a701675bf540109f9d08db75fee8f 100644 (file)
@@ -92,6 +92,12 @@ lib_libfrr_la_SOURCES = \
        lib/lua.c \
        # end
 
+nodist_lib_libfrr_la_SOURCES = \
+       yang/frr-interface.yang.c \
+       yang/frr-route-types.yang.c \
+       yang/frr-module-translator.yang.c \
+       # end
+
 vtysh_scan += \
        $(top_srcdir)/lib/distribute.c \
        $(top_srcdir)/lib/filter.c \
index bb5d38e2e664197295306efed1c7c0161f7fc4ef..a7a50a46b0fcb225aff52c24217e96cda2083ad1 100644 (file)
@@ -32,6 +32,45 @@ DEFINE_MTYPE(LIB, YANG_DATA, "YANG data structure")
 /* libyang container. */
 struct ly_ctx *ly_native_ctx;
 
+static struct yang_module_embed *embeds, **embedupd = &embeds;
+
+void yang_module_embed(struct yang_module_embed *embed)
+{
+       embed->next = NULL;
+       *embedupd = embed;
+       embedupd = &embed->next;
+}
+
+static const char *yang_module_imp_clb(const char *mod_name,
+                                      const char *mod_rev,
+                                      const char *submod_name,
+                                      const char *submod_rev,
+                                      void *user_data,
+                                      LYS_INFORMAT *format,
+                                      void (**free_module_data)
+                                               (void *, void*))
+{
+       struct yang_module_embed *e;
+
+       if (submod_name || submod_rev)
+               return NULL;
+
+       for (e = embeds; e; e = e->next) {
+               if (strcmp(e->mod_name, mod_name))
+                       continue;
+               if (mod_rev && strcmp(e->mod_rev, mod_rev))
+                       continue;
+
+               *format = e->format;
+               return e->data;
+       }
+
+       flog_warn(EC_LIB_YANG_MODULE_LOAD,
+                 "YANG model \"%s@%s\" not embedded, trying external file",
+                 mod_name, mod_rev ? mod_rev : "*");
+       return NULL;
+}
+
 /* Generate the yang_modules tree. */
 static inline int yang_module_compare(const struct yang_module *a,
                                      const struct yang_module *b)
@@ -575,6 +614,7 @@ void yang_init(void)
                flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
                exit(1);
        }
+       ly_ctx_set_module_imp_clb(ly_native_ctx, yang_module_imp_clb, NULL);
        ly_ctx_set_searchdir(ly_native_ctx, YANG_MODELS_PATH);
        ly_ctx_set_priv_dup_clb(ly_native_ctx, ly_dup_cb);
 
index cd5597ff8c1f5cb0e90733b2ac48798a200a3b7c..b0348e320b3b6ab5b7073e2357c8f53acfe4b150 100644 (file)
@@ -44,6 +44,13 @@ DECLARE_MTYPE(YANG_DATA)
 /* Maximum string length of an YANG value. */
 #define YANG_VALUE_MAXLEN 1024
 
+struct yang_module_embed {
+       struct yang_module_embed *next;
+       const char *mod_name, *mod_rev;
+       const char *data;
+       LYS_INFORMAT format;
+};
+
 struct yang_module {
        RB_ENTRY(yang_module) entry;
        const char *name;
@@ -132,6 +139,16 @@ extern struct yang_module *yang_module_load(const char *module_name);
  */
 extern struct yang_module *yang_module_find(const char *module_name);
 
+/*
+ * Register a YANG module embedded in the binary file.  Should be called
+ * from a constructor function.
+ *
+ * embed
+ *    YANG module embedding structure to register.  (static global provided
+ *    by caller.)
+ */
+extern void yang_module_embed(struct yang_module_embed *embed);
+
 /*
  * Iterate over all libyang schema nodes from the given YANG module.
  *
index 480fa1e47f559114dd6504619222bd85475f4e3d..ed74047ccef933f11f7b61fc8ab09caec9d2badf 100644 (file)
@@ -51,6 +51,9 @@ ripd_ripd_LDADD = ripd/librip.a lib/libfrr.la @LIBCAP@
 ripd_ripd_SOURCES = \
        ripd/rip_main.c \
        # end
+nodist_ripd_ripd_SOURCES = \
+       yang/frr-ripd.yang.c \
+       # end
 
 ripd_ripd_snmp_la_SOURCES = ripd/rip_snmp.c
 ripd_ripd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99
diff --git a/yang/embedmodel.py b/yang/embedmodel.py
new file mode 100644 (file)
index 0000000..52671f9
--- /dev/null
@@ -0,0 +1,78 @@
+#!/usr/bin/python3
+#
+# YANG module to C wrapper
+# written 2018 by David Lamparter, placed in Public Domain.
+
+import sys, string, re
+
+inname = sys.argv[1]
+outname = sys.argv[2]
+
+# these are regexes to avoid a compile-time/host dependency on yang-tools
+# or python-yang.  Cross-compiling FRR is already somewhat involved, no need
+# to make it even harder.
+
+re_name = re.compile(r'\bmodule\s+([^\s]+)\s+\{')
+re_rev = re.compile(r'\brevision\s+([\d-]+)\s+\{')
+
+
+template = '''/* autogenerated by embedmodel.py.  DO NOT EDIT */
+
+#include <zebra.h>
+#include "yang.h"
+
+static const char model[] =
+\t"%s";
+
+static struct yang_module_embed embed = {
+\t.mod_name = "%s",
+\t.mod_rev = "%s",
+\t.data = model,
+\t.format = %s,
+};
+
+static void embed_register(void) __attribute__((_CONSTRUCTOR(2000)));
+static void embed_register(void)
+{
+\tyang_module_embed(&embed);
+}
+'''
+
+passchars = set(string.printable) - set('\\\'"%\r\n\t\x0b\x0c')
+def escapech(char):
+    if char in passchars:
+        return char
+    if char == '\n':
+        return '\\n'
+    if char == '\t':
+        return '\\t'
+    if char in '"\\\'':
+        return '\\' + char
+    return '\\x%02x' % (ord(char))
+def escape(line):
+    return ''.join([escapech(i) for i in line])
+
+with open(inname, 'r') as fd:
+    data = fd.read()
+
+# XML support isn't actively used currently, but it's here in case the need
+# arises.  It does avoid the regex'ing.
+if '<?xml' in data:
+    from xml.etree import ElementTree
+    xml = ElementTree.fromstring(data)
+    name = xml.get('name')
+    rev = xml.find('{urn:ietf:params:xml:ns:yang:yin:1}revision').get('date')
+    fmt = 'LYS_YIN'
+else:
+    name = re_name.search(data).group(1)
+    rev = re_rev.search(data).group(1)
+    fmt = 'LYS_YANG'
+
+if name is None or rev is None:
+    raise ValueError('cannot determine YANG module name and revision')
+
+lines = [escape(row) for row in data.split('\n')]
+text = '\\n"\n\t"'.join(lines)
+
+with open(outname, 'w') as fd:
+    fd.write(template % (text, escape(name), escape(rev), fmt))
index d68a341a9faeac0a508cdf1922b56a0c0e04d98d..ee6fbc181d1cd4e1596703a0745c7b5741117174 100644 (file)
@@ -1,3 +1,24 @@
+SUFFIXES += .yang .yang.c .yin .yin.c
+EXTRA_DIST += yang/embedmodel.py
+
+.yang.yang.c:
+       $(AM_V_GEN)$(PYTHON) $(top_srcdir)/yang/embedmodel.py $^ $@
+.yin.yin.c:
+       $(AM_V_GEN)$(PYTHON) $(top_srcdir)/yang/embedmodel.py $^ $@
+
+# use .yang.c files like this:
+#
+# ripd_ripd_SOURCES = \
+#      ...
+# nodist_ripd_ripd_SOURCES = \
+#      yang/frr-ripd.yang.c \
+#      # end
+#
+# Note that putting the .yang.c file into a static library.a will NOT work
+# because the entire file is "optimized out" since it does not contain any
+# global symbols :(.  Just put it in the daemon.  Dynamic libraries.so work
+# without problems, as seen in libfrr.
+
 dist_yangmodels_DATA += yang/frr-module-translator.yang
 dist_yangmodels_DATA += yang/frr-interface.yang
 dist_yangmodels_DATA += yang/frr-route-types.yang