Skip to content
Permalink
Browse files

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

yang: embed models into binaries
  • Loading branch information...
riw777 committed Nov 26, 2018
2 parents 32bdc40 + 9fd4742 commit 0c9503eb4e71edddd57ae86d2574fd37c46ad4d0
Showing with 167 additions and 0 deletions.
  1. +6 −0 lib/subdir.am
  2. +40 −0 lib/yang.c
  3. +17 −0 lib/yang.h
  4. +3 −0 ripd/subdir.am
  5. +2 −0 yang/.gitignore
  6. +78 −0 yang/embedmodel.py
  7. +21 −0 yang/subdir.am
@@ -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 \
@@ -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);

@@ -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.
*
@@ -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
@@ -0,0 +1,2 @@
*.yang.c
*.yin
@@ -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))
@@ -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

0 comments on commit 0c9503e

Please sign in to comment.
You can’t perform that action at this time.