Merge pull request #3361 from opensourcerouting/yang-embed-models

yang: embed models into binaries
This commit is contained in:
Russ White 2018-11-26 08:04:03 -05:00 committed by GitHub
commit 0c9503eb4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 167 additions and 0 deletions

View 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 \

View 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);

View 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.
*

View 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

2
yang/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.yang.c
*.yin

78
yang/embedmodel.py Normal file
View File

@ -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))

View 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