summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_routemap.c47
-rw-r--r--bgpd/bgp_script.c15
-rw-r--r--bgpd/bgp_script.h11
-rw-r--r--configure.ac4
-rw-r--r--doc/developer/scripting.rst206
-rw-r--r--docker/ubuntu20-ci/Dockerfile2
-rw-r--r--lib/command.c6
-rw-r--r--lib/compiler.h23
-rw-r--r--lib/frrlua.c100
-rw-r--r--lib/frrlua.h21
-rw-r--r--lib/frrscript.c18
-rw-r--r--lib/frrscript.h76
-rw-r--r--lib/sockunion.c3
-rw-r--r--lib/sockunion.h1
-rw-r--r--tests/.gitignore2
-rw-r--r--tests/lib/script1.lua1
-rw-r--r--tests/lib/test_frrlua.c111
-rw-r--r--tests/lib/test_frrlua.py14
-rw-r--r--tests/lib/test_frrscript.c37
-rw-r--r--tests/lib/test_frrscript.py14
-rw-r--r--tests/subdir.am28
21 files changed, 554 insertions, 186 deletions
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 9a7b7b3cf..529abcbea 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -29,6 +29,7 @@
#include "memory.h"
#include "log.h"
#include "frrlua.h"
+#include "frrscript.h"
#ifdef HAVE_LIBPCREPOSIX
#include <pcreposix.h>
#else
@@ -373,42 +374,29 @@ route_match_script(void *rule, const struct prefix *prefix, void *object)
return RMAP_NOMATCH;
}
- enum frrlua_rm_status status_failure = LUA_RM_FAILURE,
+ enum frrlua_rm_status lrm_status = LUA_RM_FAILURE,
status_nomatch = LUA_RM_NOMATCH,
status_match = LUA_RM_MATCH,
status_match_and_change = LUA_RM_MATCH_AND_CHANGE;
- /* Make result values available */
- struct frrscript_env env[] = {
- {"integer", "RM_FAILURE", &status_failure},
- {"integer", "RM_NOMATCH", &status_nomatch},
- {"integer", "RM_MATCH", &status_match},
- {"integer", "RM_MATCH_AND_CHANGE", &status_match_and_change},
- {"integer", "action", &status_failure},
- {"prefix", "prefix", prefix},
- {"attr", "attributes", path->attr},
- {"peer", "peer", path->peer},
- {}};
-
- struct frrscript_env results[] = {
- {"integer", "action"},
- {"attr", "attributes"},
- {},
- };
-
- int result = frrscript_call(fs, env);
+ struct attr newattr = *path->attr;
+
+ int result = frrscript_call(
+ fs, ("RM_FAILURE", (long long *)&lrm_status),
+ ("RM_NOMATCH", (long long *)&status_nomatch),
+ ("RM_MATCH", (long long *)&status_match),
+ ("RM_MATCH_AND_CHANGE", (long long *)&status_match_and_change),
+ ("action", (long long *)&lrm_status), ("prefix", prefix),
+ ("attributes", &newattr), ("peer", path->peer));
if (result) {
zlog_err("Issue running script rule; defaulting to no match");
return RMAP_NOMATCH;
}
- enum frrlua_rm_status *lrm_status =
- frrscript_get_result(fs, &results[0]);
-
int status = RMAP_NOMATCH;
- switch (*lrm_status) {
+ switch (lrm_status) {
case LUA_RM_FAILURE:
zlog_err(
"Executing route-map match script '%s' failed; defaulting to no match",
@@ -423,27 +411,22 @@ route_match_script(void *rule, const struct prefix *prefix, void *object)
zlog_debug("Updating attribute based on script's values");
uint32_t locpref = 0;
- struct attr *newattr = frrscript_get_result(fs, &results[1]);
- path->attr->med = newattr->med;
+ path->attr->med = newattr.med;
if (path->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
locpref = path->attr->local_pref;
- if (locpref != newattr->local_pref) {
+ if (locpref != newattr.local_pref) {
SET_FLAG(path->attr->flag,
ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF));
- path->attr->local_pref = newattr->local_pref;
+ path->attr->local_pref = newattr.local_pref;
}
-
- aspath_free(newattr->aspath);
- XFREE(MTYPE_TMP, newattr);
break;
case LUA_RM_MATCH:
status = RMAP_MATCH;
break;
}
- XFREE(MTYPE_TMP, lrm_status);
frrscript_unload(fs);
return status;
diff --git a/bgpd/bgp_script.c b/bgpd/bgp_script.c
index 0cda1927f..9446a25a0 100644
--- a/bgpd/bgp_script.c
+++ b/bgpd/bgp_script.c
@@ -28,9 +28,8 @@
#include "bgp_aspath.h"
#include "frratomic.h"
#include "frrscript.h"
-#include "frrlua.h"
-static void lua_pushpeer(lua_State *L, const struct peer *peer)
+void lua_pushpeer(lua_State *L, const struct peer *peer)
{
lua_newtable(L);
lua_pushinteger(L, peer->as);
@@ -142,7 +141,7 @@ static void lua_pushpeer(lua_State *L, const struct peer *peer)
lua_setfield(L, -2, "stats");
}
-static void lua_pushattr(lua_State *L, const struct attr *attr)
+void lua_pushattr(lua_State *L, const struct attr *attr)
{
lua_newtable(L);
lua_pushinteger(L, attr->med);
@@ -155,10 +154,8 @@ static void lua_pushattr(lua_State *L, const struct attr *attr)
lua_setfield(L, -2, "localpref");
}
-static void *lua_toattr(lua_State *L, int idx)
+void lua_decode_attr(lua_State *L, int idx, struct attr *attr)
{
- struct attr *attr = XCALLOC(MTYPE_TMP, sizeof(struct attr));
-
lua_getfield(L, -1, "metric");
attr->med = lua_tointeger(L, -1);
lua_pop(L, 1);
@@ -171,7 +168,13 @@ static void *lua_toattr(lua_State *L, int idx)
lua_getfield(L, -1, "localpref");
attr->local_pref = lua_tointeger(L, -1);
lua_pop(L, 1);
+}
+
+void *lua_toattr(lua_State *L, int idx)
+{
+ struct attr *attr = XCALLOC(MTYPE_TMP, sizeof(struct attr));
+ lua_decode_attr(L, idx, attr);
return attr;
}
diff --git a/bgpd/bgp_script.h b/bgpd/bgp_script.h
index 6682c2eeb..f8178aa98 100644
--- a/bgpd/bgp_script.h
+++ b/bgpd/bgp_script.h
@@ -21,14 +21,25 @@
#define __BGP_SCRIPT__
#include <zebra.h>
+#include "bgpd.h"
#ifdef HAVE_SCRIPTING
+#include "frrlua.h"
+
/*
* Initialize scripting stuff.
*/
void bgp_script_init(void);
+void lua_pushpeer(lua_State *L, const struct peer *peer);
+
+void lua_pushattr(lua_State *L, const struct attr *attr);
+
+void lua_decode_attr(lua_State *L, int idx, struct attr *attr);
+
+void *lua_toattr(lua_State *L, int idx);
+
#endif /* HAVE_SCRIPTING */
#endif /* __BGP_SCRIPT__ */
diff --git a/configure.ac b/configure.ac
index c04956928..6108a3752 100644
--- a/configure.ac
+++ b/configure.ac
@@ -280,6 +280,8 @@ if test "$enable_clang_coverage" = "yes"; then
])
fi
+AM_CONDITIONAL([SCRIPTING], [test "$enable_scripting" = "yes"])
+
if test "$enable_scripting" = "yes"; then
AX_PROG_LUA([5.3], [5.4], [], [
AC_MSG_ERROR([Lua 5.3 is required to build with Lua support. No other version is supported.])
@@ -290,7 +292,9 @@ if test "$enable_scripting" = "yes"; then
AX_LUA_LIBS([
AC_DEFINE([HAVE_SCRIPTING], [1], [Have support for scripting])
LIBS="$LIBS $LUA_LIB"
+ SCRIPTING=true
], [
+ SCRIPTING=false
AC_MSG_ERROR([Lua 5.3 libraries are required to build with Lua support. No other version is supported.])
])
fi
diff --git a/doc/developer/scripting.rst b/doc/developer/scripting.rst
index 708f65ff7..1757d41fe 100644
--- a/doc/developer/scripting.rst
+++ b/doc/developer/scripting.rst
@@ -135,24 +135,20 @@ A typical execution call looks something like this:
int status_ok = 0, status_fail = 1;
struct prefix p = ...;
- struct frrscript_env env[] = {
- {"integer", "STATUS_FAIL", &status_fail},
- {"integer", "STATUS_OK", &status_ok},
- {"prefix", "myprefix", &p},
- {}};
-
- int result = frrscript_call(fs, env);
+ int result = frrscript_call(fs,
+ ("STATUS_FAIL", &status_fail),
+ ("STATUS_OK", &status_ok),
+ ("prefix", &p));
To execute a loaded script, we need to define the inputs. These inputs are
-passed by binding values to variable names that will be accessible within the
+passed in by binding values to variable names that will be accessible within the
Lua environment. Basically, all communication with the script takes place via
global variables within the script, and to provide inputs we predefine globals
-before the script runs. This is done by passing ``frrscript_call()`` an array
-of ``struct frrscript_env``. Each struct has three fields. The first identifies
-the type of the value being passed; more on this later. The second defines the
-name of the global variable within the script environment to bind the third
-argument (the value) to.
+before the script runs. This is done by passing ``frrscript_call()`` a list of
+parenthesized pairs, where the first and second fields identify, respectively,
+the name of the global variable within the script environment and the value it
+is bound to.
The script is then executed and returns a general status code. In the success
case this will be 0, otherwise it will be nonzero. The script itself does not
@@ -162,32 +158,10 @@ determine this code, it is provided by the Lua interpreter.
Querying State
^^^^^^^^^^^^^^
-When a chunk is executed, its state at exit is preserved and can be inspected.
-
-After running a script, results may be retrieved by querying the script's
-state. Again this is done by retrieving the values of global variables, which
-are known to the script author to be "output" variables.
-
-A result is retrieved like so:
-
-.. code-block:: c
-
- struct frrscript_env myresult = {"string", "myresult"};
-
- char *myresult = frrscript_get_result(fs, &myresult);
-
- ... do something ...
-
- XFREE(MTYPE_TMP, myresult);
-
+.. todo::
-As with arguments, results are retrieved by providing a ``struct
-frrscript_env`` specifying a type and a global name. No value is necessary, nor
-is it modified by ``frrscript_get_result()``. That function simply extracts the
-requested value from the script state and returns it.
-
-In most cases the returned value will be allocated with ``MTYPE_TMP`` and will
-need to be freed after use.
+ This section will be updated once ``frrscript_get_result`` has been
+ updated to work with the new ``frrscript_call`` and the rest of the new API.
Unloading
@@ -199,21 +173,14 @@ To destroy a script and its associated state:
frrscript_unload(fs);
-Values returned by ``frrscript_get_result`` are still valid after the script
-they were retrieved from is unloaded.
-
-Note that you must unload and then load the script if you want to reset its
-state, for example to run it again with different inputs. Otherwise the state
-from the previous run carries over into subsequent runs.
-
.. _marshalling:
Marshalling
^^^^^^^^^^^
-Earlier sections glossed over the meaning of the type name field in ``struct
-frrscript_env`` and how data is passed between C and Lua. Lua, as a dynamically
+Earlier sections glossed over the types of values that can be passed into
+``frrscript_call`` and how data is passed between C and Lua. Lua, as a dynamically
typed, garbage collected language, cannot directly use C values without some
kind of marshalling / unmarshalling system to translate types between the two
runtimes.
@@ -222,31 +189,10 @@ Lua communicates with C code using a stack. C code wishing to provide data to
Lua scripts must provide a function that marshalls the C data into a Lua
representation and pushes it on the stack. C code wishing to retrieve data from
Lua must provide a corresponding unmarshalling function that retrieves a Lua
-value from the stack and converts it to the corresponding C type. These two
-functions, together with a chosen name of the type they operate on, are
-referred to as ``codecs`` in FRR.
-
-A codec is defined as:
-
-.. code-block:: c
-
- typedef void (*encoder_func)(lua_State *, const void *);
- typedef void *(*decoder_func)(lua_State *, int);
-
- struct frrscript_codec {
- const char *typename;
- encoder_func encoder;
- decoder_func decoder;
- };
+value from the stack and converts it to the corresponding C type. These
+functions are known as encoders and decoders in FRR.
-A typename string and two function pointers.
-
-``typename`` can be anything you want. For example, for the combined types of
-``struct prefix`` and its equivalent in Lua I have chosen the name ``prefix``.
-There is no restriction on naming here, it is just a human name used as a key
-and specified when passing and retrieving values.
-
-``encoder`` is a function that takes a ``lua_State *`` and a C type and pushes
+An encoder is a function that takes a ``lua_State *`` and a C type and pushes
onto the Lua stack a value representing the C type. For C structs, the usual
case, this will typically be a Lua table (tables are the only datastructure Lua
has). For example, here is the encoder function for ``struct prefix``:
@@ -254,7 +200,7 @@ has). For example, here is the encoder function for ``struct prefix``:
.. code-block:: c
- void lua_pushprefix(lua_State *L, const struct prefix *prefix)
+ void lua_pushprefix(lua_State *L, struct prefix *prefix)
{
char buffer[PREFIX_STRLEN];
@@ -269,17 +215,48 @@ has). For example, here is the encoder function for ``struct prefix``:
lua_setfield(L, -2, "family");
}
-This function pushes a single value onto the Lua stack. It is a table whose equivalent in Lua is:
+This function pushes a single value onto the Lua stack. It is a table whose
+equivalent in Lua is:
.. code-block:: c
{ ["network"] = "1.2.3.4/24", ["prefixlen"] = 24, ["family"] = 2 }
-``decoder`` does the reverse; it takes a ``lua_State *`` and an index into the
-stack, and unmarshalls a Lua value there into the corresponding C type. Again
-for ``struct prefix``:
+Decoders are a bit more involved. They do the reverse; a decoder function takes
+a ``lua_State *``, pops a value off the Lua stack and converts it back into its
+C type.
+However, since Lua programs have the ability to directly modify their inputs
+(i.e. values passed in via ``frrscript_call``), we need two separate decoder
+functions, called ``lua_decode_*`` and ``lua_to*``.
+A ``lua_decode_*`` function takes a ``lua_State*``, an index, and a C type, and
+unmarshalls a Lua value into that C type.
+Again, for ``struct prefix``:
+
+.. code-block:: c
+
+ void lua_decode_prefix(lua_State *L, int idx, struct prefix *prefix)
+ {
+ lua_getfield(L, idx, "network");
+ (void)str2prefix(lua_tostring(L, -1), prefix);
+ lua_pop(L, 1);
+ /* pop the table */
+ lua_pop(L, 1);
+ }
+
+.. warning::
+
+ ``lua_decode_prefix`` functions should leave the Lua stack completely empty
+ when they return.
+ For decoders that unmarshall fields from tables, remember to pop the table
+ at the end.
+
+
+A ``lua_to*`` function perform a similar role except that it first allocates
+memory for the new C type before decoding the value from the Lua stack, then
+returns a pointer to the newly allocated C type.
+This function can and should be implemented using ``lua_decode_*``:
.. code-block:: c
@@ -287,39 +264,70 @@ for ``struct prefix``:
{
struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
- lua_getfield(L, idx, "network");
- str2prefix(lua_tostring(L, -1), p);
- lua_pop(L, 1);
-
+ lua_decode_prefix(L, idx, p);
return p;
}
-By convention these functions should be called ``lua_to*``, as this is the
-naming convention used by the Lua C library for the basic types e.g.
-``lua_tointeger`` and ``lua_tostring``.
The returned data must always be copied off the stack and the copy must be
allocated with ``MTYPE_TMP``. This way it is possible to unload the script
(destroy the state) without invalidating any references to values stored in it.
+Note that it is the caller's responsibility to free the data.
-To register a new type with its corresponding encoding functions:
-
-.. code-block:: c
-
- struct frrscript_codec frrscript_codecs_lib[] = {
- {.typename = "prefix",
- .encoder = (encoder_func)lua_pushprefix,
- .decoder = lua_toprefix},
- {.typename = "sockunion",
- .encoder = (encoder_func)lua_pushsockunion,
- .decoder = lua_tosockunion},
- ...
- {}};
+For consistency, we should always name functions of the first type
+``lua_decode_*``.
+Functions of the second type should be named ``lua_to*``, as this is the
+naming convention used by the Lua C library for the basic types e.g.
+``lua_tointeger`` and ``lua_tostring``.
- frrscript_register_type_codecs(frrscript_codecs_lib);
+This two-function design allows the compiler to warn if a value passed into
+``frrscript_call`` does not have a encoder and decoder for that type.
+The ``lua_to*`` functions enable us to easily create decoders for nested
+structures.
+
+To register a new type with its corresponding encoding and decoding functions,
+add the mapping in the following macros in ``frrscript.h``:
+
+.. code-block:: diff
+
+ #define ENCODE_ARGS_WITH_STATE(L, value) \
+ _Generic((value), \
+ ...
+ - struct peer * : lua_pushpeer \
+ + struct peer * : lua_pushpeer, \
+ + struct prefix * : lua_pushprefix \
+ )(L, value)
+
+ #define DECODE_ARGS_WITH_STATE(L, value) \
+ _Generic((value), \
+ ...
+ - struct peer * : lua_decode_peer \
+ + struct peer * : lua_decode_peer, \
+ + struct prefix * : lua_decode_prefix \
+ )(L, -1, value)
+
+
+At compile time, the compiler will search for encoders/decoders for the type of
+each value passed in via ``frrscript_call``. If a encoder/decoder cannot be
+found, it will appear as a compile warning. Note that the types must
+match *exactly*.
+In the above example, we defined encoders/decoders for a value of
+``struct prefix *``, but not ``struct prefix`` or ``const struct prefix *``.
+
+``const`` values are a special case. We want to use them in our Lua scripts
+but not modify them, so creating a decoder for them would be meaningless.
+But we still need a decoder for the type of value so that the compiler will be
+satisfied.
+For that, use ``lua_decode_noop``:
+
+.. code-block:: diff
+
+ #define DECODE_ARGS_WITH_STATE(L, value) \
+ _Generic((value), \
+ ...
+ + const struct prefix * : lua_decode_noop \
+ )(L, -1, value)
-From this point on the type names are available to be used when calling any
-script and getting its results.
.. note::
diff --git a/docker/ubuntu20-ci/Dockerfile b/docker/ubuntu20-ci/Dockerfile
index 8b7557db1..47d5b81d3 100644
--- a/docker/ubuntu20-ci/Dockerfile
+++ b/docker/ubuntu20-ci/Dockerfile
@@ -12,6 +12,7 @@ RUN apt update && \
libcap-dev python2 libelf-dev \
sudo gdb curl iputils-ping time \
libgrpc++-dev libgrpc-dev protobuf-compiler-grpc \
+ lua5.3 liblua5.3-dev \
mininet iproute2 iperf && \
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output /tmp/get-pip.py && \
python2 /tmp/get-pip.py && \
@@ -66,6 +67,7 @@ RUN cd ~/frr && \
--enable-group=frr \
--enable-vty-group=frrvty \
--enable-snmp=agentx \
+ --enable-scripting \
--with-pkg-extra-version=-my-manual-build && \
make -j $(nproc) && \
sudo make install
diff --git a/lib/command.c b/lib/command.c
index 560d4a09f..9dac60599 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -2428,14 +2428,16 @@ DEFUN(script,
struct prefix p;
(void)str2prefix("1.2.3.4/24", &p);
-
struct frrscript *fs = frrscript_load(argv[1]->arg, NULL);
if (fs == NULL) {
vty_out(vty, "Script '/etc/frr/scripts/%s.lua' not found\n",
argv[1]->arg);
} else {
- int ret = frrscript_call(fs, NULL);
+ int ret = frrscript_call(fs, ("p", &p));
+ char buf[40];
+ prefix2str(&p, buf, sizeof(buf));
+ vty_out(vty, "p: %s\n", buf);
vty_out(vty, "Script result: %d\n", ret);
}
diff --git a/lib/compiler.h b/lib/compiler.h
index 970ed297f..bf443906e 100644
--- a/lib/compiler.h
+++ b/lib/compiler.h
@@ -173,6 +173,29 @@ extern "C" {
#define MACRO_REPEAT(NAME, ...) \
MACRO_VARIANT(_MACRO_REPEAT, ##__VA_ARGS__)(NAME, ##__VA_ARGS__)
+/* per-arglist repeat macro, use like this:
+ * #define foo(...) MAP_LISTS(F, ##__VA_ARGS__)
+ * where F is a n-ary function where n is the number of args in each arglist.
+ * e.g.: MAP_LISTS(f, (a, b), (c, d))
+ * expands to: f(a, b); f(c, d)
+ */
+
+#define ESC(...) __VA_ARGS__
+#define MAP_LISTS(M, ...) \
+ _CONCAT(_MAP_LISTS_, PP_NARG(__VA_ARGS__))(M, ##__VA_ARGS__)
+#define _MAP_LISTS_0(M)
+#define _MAP_LISTS_1(M, _1) ESC(M _1)
+#define _MAP_LISTS_2(M, _1, _2) ESC(M _1; M _2)
+#define _MAP_LISTS_3(M, _1, _2, _3) ESC(M _1; M _2; M _3)
+#define _MAP_LISTS_4(M, _1, _2, _3, _4) ESC(M _1; M _2; M _3; M _4)
+#define _MAP_LISTS_5(M, _1, _2, _3, _4, _5) ESC(M _1; M _2; M _3; M _4; M _5)
+#define _MAP_LISTS_6(M, _1, _2, _3, _4, _5, _6) \
+ ESC(M _1; M _2; M _3; M _4; M _5; M _6)
+#define _MAP_LISTS_7(M, _1, _2, _3, _4, _5, _6, _7) \
+ ESC(M _1; M _2; M _3; M _4; M _5; M _6; M _7)
+#define _MAP_LISTS_8(M, _1, _2, _3, _4, _5, _6, _7, _8) \
+ ESC(M _1; M _2; M _3; M _4; M _5; M _6; M _7; M _8)
+
/*
* for warnings on macros, put in the macro content like this:
* #define MACRO BLA CPP_WARN("MACRO has been deprecated")
diff --git a/lib/frrlua.c b/lib/frrlua.c
index d8aaa3aa3..e97e48121 100644
--- a/lib/frrlua.c
+++ b/lib/frrlua.c
@@ -52,10 +52,9 @@ int frrlua_table_get_integer(lua_State *L, const char *key)
}
/*
- * Encoders.
- *
* This section has functions that convert internal FRR datatypes into Lua
- * datatypes.
+ * datatypes: one encoder function and two decoder functions for each type.
+ *
*/
void lua_pushprefix(lua_State *L, const struct prefix *prefix)
@@ -71,14 +70,19 @@ void lua_pushprefix(lua_State *L, const struct prefix *prefix)
lua_setfield(L, -2, "family");
}
-void *lua_toprefix(lua_State *L, int idx)
+void lua_decode_prefix(lua_State *L, int idx, struct prefix *prefix)
{
- struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
-
lua_getfield(L, idx, "network");
- (void)str2prefix(lua_tostring(L, -1), p);
+ (void)str2prefix(lua_tostring(L, -1), prefix);
lua_pop(L, 1);
+ /* pop the table */
+ lua_pop(L, 1);
+}
+void *lua_toprefix(lua_State *L, int idx)
+{
+ struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
+ lua_decode_prefix(L, idx, p);
return p;
}
@@ -109,10 +113,8 @@ void lua_pushinterface(lua_State *L, const struct interface *ifp)
lua_setfield(L, -2, "linklayer_type");
}
-void *lua_tointerface(lua_State *L, int idx)
+void lua_decode_interface(lua_State *L, int idx, struct interface *ifp)
{
- struct interface *ifp = XCALLOC(MTYPE_TMP, sizeof(struct interface));
-
lua_getfield(L, idx, "name");
strlcpy(ifp->name, lua_tostring(L, -1), sizeof(ifp->name));
lua_pop(L, 1);
@@ -146,13 +148,21 @@ void *lua_tointerface(lua_State *L, int idx)
lua_getfield(L, idx, "linklayer_type");
ifp->ll_type = lua_tointeger(L, -1);
lua_pop(L, 1);
+ /* pop the table */
+ lua_pop(L, 1);
+}
+void *lua_tointerface(lua_State *L, int idx)
+{
+ struct interface *ifp = XCALLOC(MTYPE_TMP, sizeof(struct interface));
+ lua_decode_interface(L, idx, ifp);
return ifp;
}
void lua_pushinaddr(lua_State *L, const struct in_addr *addr)
{
char buf[INET_ADDRSTRLEN];
+
inet_ntop(AF_INET, addr, buf, sizeof(buf));
lua_newtable(L);
@@ -162,14 +172,19 @@ void lua_pushinaddr(lua_State *L, const struct in_addr *addr)
lua_setfield(L, -2, "string");
}
-void *lua_toinaddr(lua_State *L, int idx)
+void lua_decode_inaddr(lua_State *L, int idx, struct in_addr *inaddr)
{
- struct in_addr *inaddr = XCALLOC(MTYPE_TMP, sizeof(struct in_addr));
-
lua_getfield(L, idx, "value");
inaddr->s_addr = lua_tointeger(L, -1);
lua_pop(L, 1);
+ /* pop the table */
+ lua_pop(L, 1);
+}
+void *lua_toinaddr(lua_State *L, int idx)
+{
+ struct in_addr *inaddr = XCALLOC(MTYPE_TMP, sizeof(struct in_addr));
+ lua_decode_inaddr(L, idx, inaddr);
return inaddr;
}
@@ -177,6 +192,7 @@ void *lua_toinaddr(lua_State *L, int idx)
void lua_pushin6addr(lua_State *L, const struct in6_addr *addr)
{
char buf[INET6_ADDRSTRLEN];
+
inet_ntop(AF_INET6, addr, buf, sizeof(buf));
lua_newtable(L);
@@ -186,20 +202,26 @@ void lua_pushin6addr(lua_State *L, const struct in6_addr *addr)
lua_setfield(L, -2, "string");
}
-void *lua_toin6addr(lua_State *L, int idx)
+void lua_decode_in6addr(lua_State *L, int idx, struct in6_addr *in6addr)
{
- struct in6_addr *in6addr = XCALLOC(MTYPE_TMP, sizeof(struct in6_addr));
-
lua_getfield(L, idx, "string");
inet_pton(AF_INET6, lua_tostring(L, -1), in6addr);
lua_pop(L, 1);
+ /* pop the table */
+ lua_pop(L, 1);
+}
+void *lua_toin6addr(lua_State *L, int idx)
+{
+ struct in6_addr *in6addr = XCALLOC(MTYPE_TMP, sizeof(struct in6_addr));
+ lua_decode_in6addr(L, idx, in6addr);
return in6addr;
}
void lua_pushsockunion(lua_State *L, const union sockunion *su)
{
char buf[SU_ADDRSTRLEN];
+
sockunion2str(su, buf, sizeof(buf));
lua_newtable(L);
@@ -210,13 +232,20 @@ void lua_pushsockunion(lua_State *L, const union sockunion *su)
lua_setfield(L, -2, "string");
}
-void *lua_tosockunion(lua_State *L, int idx)
+void lua_decode_sockunion(lua_State *L, int idx, union sockunion *su)
{
- union sockunion *su = XCALLOC(MTYPE_TMP, sizeof(union sockunion));
-
lua_getfield(L, idx, "string");
str2sockunion(lua_tostring(L, -1), su);
+ lua_pop(L, 1);
+ /* pop the table */
+ lua_pop(L, 1);
+}
+
+void *lua_tosockunion(lua_State *L, int idx)
+{
+ union sockunion *su = XCALLOC(MTYPE_TMP, sizeof(union sockunion));
+ lua_decode_sockunion(L, idx, su);
return su;
}
@@ -225,12 +254,17 @@ void lua_pushtimet(lua_State *L, const time_t *time)
lua_pushinteger(L, *time);
}
+void lua_decode_timet(lua_State *L, int idx, time_t *t)
+{
+ *t = lua_tointeger(L, idx);
+ lua_pop(L, 1);
+}
+
void *lua_totimet(lua_State *L, int idx)
{
time_t *t = XCALLOC(MTYPE_TMP, sizeof(time_t));
- *t = lua_tointeger(L, idx);
-
+ lua_decode_timet(L, idx, t);
return t;
}
@@ -239,17 +273,28 @@ void lua_pushintegerp(lua_State *L, const long long *num)
lua_pushinteger(L, *num);
}
-void *lua_tointegerp(lua_State *L, int idx)
+void lua_decode_integerp(lua_State *L, int idx, long long *num)
{
int isnum;
- long long *num = XCALLOC(MTYPE_TMP, sizeof(long long));
-
*num = lua_tonumberx(L, idx, &isnum);
+ lua_pop(L, 1);
assert(isnum);
+}
+
+void *lua_tointegerp(lua_State *L, int idx)
+{
+ long long *num = XCALLOC(MTYPE_TMP, sizeof(long long));
+ lua_decode_integerp(L, idx, num);
return num;
}
+void lua_decode_stringp(lua_State *L, int idx, char *str)
+{
+ strlcpy(str, lua_tostring(L, idx), strlen(str) + 1);
+ lua_pop(L, 1);
+}
+
void *lua_tostringp(lua_State *L, int idx)
{
char *string = XSTRDUP(MTYPE_TMP, lua_tostring(L, idx));
@@ -258,6 +303,13 @@ void *lua_tostringp(lua_State *L, int idx)
}
/*
+ * Decoder for const values, since we cannot modify them.
+ */
+void lua_decode_noop(lua_State *L, int idx, const void *ptr)
+{
+}
+
+/*
* Logging.
*
* Lua-compatible wrappers for FRR logging functions.
diff --git a/lib/frrlua.h b/lib/frrlua.h
index 6fb30938b..c4de82740 100644
--- a/lib/frrlua.h
+++ b/lib/frrlua.h
@@ -50,6 +50,8 @@ static inline void lua_pushstring_wrapper(lua_State *L, const char *str)
*/
void lua_pushprefix(lua_State *L, const struct prefix *prefix);
+void lua_decode_prefix(lua_State *L, int idx, struct prefix *prefix);
+
/*
* Converts the Lua value at idx to a prefix.
*
@@ -63,6 +65,8 @@ void *lua_toprefix(lua_State *L, int idx);
*/
void lua_pushinterface(lua_State *L, const struct interface *ifp);
+void lua_decode_interface(lua_State *L, int idx, struct interface *ifp);
+
/*
* Converts the Lua value at idx to an interface.
*
@@ -77,6 +81,8 @@ void *lua_tointerface(lua_State *L, int idx);
*/
void lua_pushinaddr(lua_State *L, const struct in_addr *addr);
+void lua_decode_inaddr(lua_State *L, int idx, struct in_addr *addr);
+
/*
* Converts the Lua value at idx to an in_addr.
*
@@ -90,6 +96,8 @@ void *lua_toinaddr(lua_State *L, int idx);
*/
void lua_pushin6addr(lua_State *L, const struct in6_addr *addr);
+void lua_decode_in6addr(lua_State *L, int idx, struct in6_addr *addr);
+
/*
* Converts the Lua value at idx to an in6_addr.
*
@@ -103,6 +111,8 @@ void *lua_toin6addr(lua_State *L, int idx);
*/
void lua_pushtimet(lua_State *L, const time_t *time);
+void lua_decode_timet(lua_State *L, int idx, time_t *time);
+
/*
* Converts the Lua value at idx to a time_t.
*
@@ -116,6 +126,8 @@ void *lua_totimet(lua_State *L, int idx);
*/
void lua_pushsockunion(lua_State *L, const union sockunion *su);
+void lua_decode_sockunion(lua_State *L, int idx, union sockunion *su);
+
/*
* Converts the Lua value at idx to a sockunion.
*
@@ -129,6 +141,8 @@ void *lua_tosockunion(lua_State *L, int idx);
*/
void lua_pushintegerp(lua_State *L, const long long *num);
+void lua_decode_integerp(lua_State *L, int idx, long long *num);
+
/*
* Converts the Lua value at idx to an int.
*
@@ -137,6 +151,8 @@ void lua_pushintegerp(lua_State *L, const long long *num);
*/
void *lua_tointegerp(lua_State *L, int idx);
+void lua_decode_stringp(lua_State *L, int idx, char *str);
+
/*
* Pop string.
*
@@ -146,6 +162,11 @@ void *lua_tointegerp(lua_State *L, int idx);
void *lua_tostringp(lua_State *L, int idx);
/*
+ * No-op decocder
+ */
+void lua_decode_noop(lua_State *L, int idx, const void *ptr);
+
+/*
* Retrieve an integer from table on the top of the stack.
*
* key
diff --git a/lib/frrscript.c b/lib/frrscript.c
index 10d400886..1a9f3639d 100644
--- a/lib/frrscript.c
+++ b/lib/frrscript.c
@@ -104,24 +104,8 @@ static void codec_free(struct codec *c)
/* Generic script APIs */
-int frrscript_call(struct frrscript *fs, struct frrscript_env *env)
+int _frrscript_call(struct frrscript *fs)
{
- struct frrscript_codec c = {};
- const void *arg;
- const char *bindname;
-
- /* Encode script arguments */
- for (int i = 0; env && env[i].val != NULL; i++) {
- bindname = env[i].name;
- c.typename = env[i].typename;
- arg = env[i].val;
-
- struct frrscript_codec *codec = hash_lookup(codec_hash, &c);
- assert(codec && "No encoder for type");
- codec->encoder(fs->L, arg);
-
- lua_setglobal(fs->L, bindname);
- }
int ret = lua_pcall(fs->L, 0, 0, 0);
diff --git a/lib/frrscript.h b/lib/frrscript.h
index f4057f531..8612c602f 100644
--- a/lib/frrscript.h
+++ b/lib/frrscript.h
@@ -25,6 +25,7 @@
#include <lua.h>
#include "frrlua.h"
+#include "../bgpd/bgp_script.h"
#ifdef __cplusplus
extern "C" {
@@ -96,6 +97,56 @@ void frrscript_register_type_codecs(struct frrscript_codec *codecs);
*/
void frrscript_init(const char *scriptdir);
+#define ENCODE_ARGS(name, value) \
+ do { \
+ ENCODE_ARGS_WITH_STATE(L, value); \
+ lua_setglobal(L, name); \
+ } while (0)
+
+#define DECODE_ARGS(name, value) \
+ do { \
+ lua_getglobal(L, name); \
+ DECODE_ARGS_WITH_STATE(L, value); \
+ } while (0)
+
+/*
+ * Maps the type of value to its encoder/decoder.
+ * Add new mappings here.
+ *
+ * L
+ * Lua state
+ * scriptdir
+ * Directory in which to look for scripts
+ */
+#define ENCODE_ARGS_WITH_STATE(L, value) \
+ _Generic((value), \
+long long * : lua_pushintegerp, \
+struct prefix * : lua_pushprefix, \
+struct interface * : lua_pushinterface, \
+struct in_addr * : lua_pushinaddr, \
+struct in6_addr * : lua_pushin6addr, \
+union sockunion * : lua_pushsockunion, \
+time_t * : lua_pushtimet, \
+char * : lua_pushstring_wrapper, \
+struct attr * : lua_pushattr, \
+struct peer * : lua_pushpeer, \
+const struct prefix * : lua_pushprefix \
+)(L, value)
+
+#define DECODE_ARGS_WITH_STATE(L, value) \
+ _Generic((value), \
+long long * : lua_decode_integerp, \
+struct prefix * : lua_decode_prefix, \
+struct interface * : lua_decode_interface, \
+struct in_addr * : lua_decode_inaddr, \
+struct in6_addr * : lua_decode_in6addr, \
+union sockunion * : lua_decode_sockunion, \
+time_t * : lua_decode_timet, \
+char * : lua_decode_stringp, \
+struct attr * : lua_decode_attr, \
+struct peer * : lua_decode_noop, \
+const struct prefix * : lua_decode_noop \
+)(L, -1, value)
/*
* Call script.
@@ -103,14 +154,31 @@ void frrscript_init(const char *scriptdir);
* fs
* The script to call; this is obtained from frrscript_load().
*
- * env
- * The script's environment. Specify this as an array of frrscript_env.
- *
* Returns:
* 0 if the script ran successfully, nonzero otherwise.
*/
-int frrscript_call(struct frrscript *fs, struct frrscript_env *env);
+int _frrscript_call(struct frrscript *fs);
+/*
+ * Wrapper for call script. Maps values passed in to their encoder
+ * and decoder types.
+ *
+ * fs
+ * The script to call; this is obtained from frrscript_load().
+ *
+ * Returns:
+ * 0 if the script ran successfully, nonzero otherwise.
+ */
+#define frrscript_call(fs, ...) \
+ ({ \
+ lua_State *L = fs->L; \
+ MAP_LISTS(ENCODE_ARGS, ##__VA_ARGS__); \
+ int ret = _frrscript_call(fs); \
+ if (ret == 0) { \
+ MAP_LISTS(DECODE_ARGS, ##__VA_ARGS__); \
+ } \
+ ret; \
+ })
/*
* Get result from finished script.
diff --git a/lib/sockunion.c b/lib/sockunion.c
index e6340a174..c7af458e9 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -606,8 +606,7 @@ static void __attribute__((unused)) sockunion_print(const union sockunion *su)
}
}
-static int in6addr_cmp(const struct in6_addr *addr1,
- const struct in6_addr *addr2)
+int in6addr_cmp(const struct in6_addr *addr1, const struct in6_addr *addr2)
{
unsigned int i;
const uint8_t *p1, *p2;
diff --git a/lib/sockunion.h b/lib/sockunion.h
index 2cc80bb70..9e6719ccf 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -73,6 +73,7 @@ enum connect_result { connect_error, connect_success, connect_in_progress };
/* Prototypes. */
extern int str2sockunion(const char *, union sockunion *);
extern const char *sockunion2str(const union sockunion *, char *, size_t);
+int in6addr_cmp(const struct in6_addr *addr1, const struct in6_addr *addr2);
extern int sockunion_cmp(const union sockunion *, const union sockunion *);
extern int sockunion_same(const union sockunion *, const union sockunion *);
extern unsigned int sockunion_hash(const union sockunion *);
diff --git a/tests/.gitignore b/tests/.gitignore
index fb2edc939..3fad1b081 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -25,6 +25,8 @@
/lib/test_atomlist
/lib/test_buffer
/lib/test_checksum
+/lib/test_frrscript
+/lib/test_frrlua
/lib/test_graph
/lib/test_heavy
/lib/test_heavy_thread
diff --git a/tests/lib/script1.lua b/tests/lib/script1.lua
new file mode 100644
index 000000000..e9ebc29bd
--- /dev/null
+++ b/tests/lib/script1.lua
@@ -0,0 +1 @@
+a = a + b
diff --git a/tests/lib/test_frrlua.c b/tests/lib/test_frrlua.c
new file mode 100644
index 000000000..a81446f9c
--- /dev/null
+++ b/tests/lib/test_frrlua.c
@@ -0,0 +1,111 @@
+/*
+ * frrlua unit tests
+ * Copyright (C) 2021 Donald Lee
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include "string.h"
+#include "stdio.h"
+#include "lib/frrlua.h"
+
+static void test_encode_decode(void)
+{
+ lua_State *L = luaL_newstate();
+
+ long long a = 123;
+ long long b = a;
+
+ lua_pushintegerp(L, &a);
+ lua_decode_integerp(L, -1, &a);
+ assert(a == b);
+ assert(lua_gettop(L) == 0);
+
+ time_t time_a = 100;
+ time_t time_b = time_a;
+
+ lua_pushtimet(L, &time_a);
+ lua_decode_timet(L, -1, &time_a);
+ assert(time_a == time_b);
+ assert(lua_gettop(L) == 0);
+
+ char str_b[] = "Hello", str_a[6];
+
+ strlcpy(str_a, str_b, sizeof(str_b));
+ lua_pushstring_wrapper(L, str_a);
+ lua_decode_stringp(L, -1, str_a);
+ assert(strncmp(str_a, str_b, sizeof(str_b)) == 0);
+ assert(lua_gettop(L) == 0);
+
+ char p_b_str[] = "10.0.0.0/24", p_a_str[12];
+ struct prefix p_a;
+
+ strlcpy(p_a_str, p_b_str, sizeof(p_b_str));
+ str2prefix(p_a_str, &p_a);
+ lua_pushprefix(L, &p_a);
+ lua_decode_prefix(L, -1, &p_a);
+ prefix2str(&p_a, p_a_str, sizeof(p_b_str));
+ assert(strncmp(p_a_str, p_b_str, sizeof(p_b_str)) == 0);
+ assert(lua_gettop(L) == 0);
+
+ struct interface ifp_a;
+ struct interface ifp_b = ifp_a;
+
+ lua_pushinterface(L, &ifp_a);
+ lua_decode_interface(L, -1, &ifp_a);
+ assert(strncmp(ifp_a.name, ifp_b.name, sizeof(ifp_b.name)) == 0);
+ assert(ifp_a.ifindex == ifp_b.ifindex);
+ assert(ifp_a.status == ifp_b.status);
+ assert(ifp_a.flags == ifp_b.flags);
+ assert(ifp_a.metric == ifp_b.metric);
+ assert(ifp_a.speed == ifp_b.speed);
+ assert(ifp_a.mtu == ifp_b.mtu);
+ assert(ifp_a.mtu6 == ifp_b.mtu6);
+ assert(ifp_a.bandwidth == ifp_b.bandwidth);
+ assert(ifp_a.link_ifindex == ifp_b.link_ifindex);
+ assert(ifp_a.ll_type == ifp_b.ll_type);
+ assert(lua_gettop(L) == 0);
+
+ struct in_addr addr_a;
+ struct in_addr addr_b = addr_a;
+
+ lua_pushinaddr(L, &addr_a);
+ lua_decode_inaddr(L, -1, &addr_a);
+ assert(addr_a.s_addr == addr_b.s_addr);
+ assert(lua_gettop(L) == 0);
+
+ struct in6_addr in6addr_a;
+ struct in6_addr in6addr_b = in6addr_a;
+
+ lua_pushin6addr(L, &in6addr_a);
+ lua_decode_in6addr(L, -1, &in6addr_a);
+ assert(in6addr_cmp(&in6addr_a, &in6addr_b) == 0);
+ assert(lua_gettop(L) == 0);
+
+ union sockunion su_a, su_b;
+
+ memset(&su_a, 0, sizeof(union sockunion));
+ memset(&su_b, 0, sizeof(union sockunion));
+ lua_pushsockunion(L, &su_a);
+ lua_decode_sockunion(L, -1, &su_a);
+ assert(sockunion_cmp(&su_a, &su_b) == 0);
+ assert(lua_gettop(L) == 0);
+}
+
+int main(int argc, char **argv)
+{
+ test_encode_decode();
+}
diff --git a/tests/lib/test_frrlua.py b/tests/lib/test_frrlua.py
new file mode 100644
index 000000000..2f6ddc1c0
--- /dev/null
+++ b/tests/lib/test_frrlua.py
@@ -0,0 +1,14 @@
+import frrtest
+import pytest
+
+if 'S["SCRIPTING_TRUE"]=""\n' not in open("../config.status").readlines():
+ class TestFrrlua:
+ @pytest.mark.skipif(True, reason="Test unsupported")
+ def test_exit_cleanly(self):
+ pass
+else:
+
+ class TestFrrlua(frrtest.TestMultiOut):
+ program = "./test_frrlua"
+
+ TestFrrlua.exit_cleanly()
diff --git a/tests/lib/test_frrscript.c b/tests/lib/test_frrscript.c
new file mode 100644
index 000000000..bd75cc555
--- /dev/null
+++ b/tests/lib/test_frrscript.c
@@ -0,0 +1,37 @@
+/*
+ * frrscript unit tests
+ * Copyright (C) 2021 Donald Lee
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "lib/frrscript.h"
+
+int main(int argc, char **argv)
+{
+ frrscript_init("./lib");
+
+ struct frrscript *fs = frrscript_load("script1", NULL);
+ long long a = 100, b = 200;
+ int result = frrscript_call(fs, ("a", &a), ("b", &b));
+
+ assert(result == 0);
+ assert(a == 300);
+ assert(b == 200);
+
+ return 0;
+}
diff --git a/tests/lib/test_frrscript.py b/tests/lib/test_frrscript.py
new file mode 100644
index 000000000..046d97b01
--- /dev/null
+++ b/tests/lib/test_frrscript.py
@@ -0,0 +1,14 @@
+import frrtest
+import pytest
+
+if 'S["SCRIPTING_TRUE"]=""\n' not in open("../config.status").readlines():
+ class TestFrrscript:
+ @pytest.mark.skipif(True, reason="Test unsupported")
+ def test_exit_cleanly(self):
+ pass
+else:
+
+ class TestFrrscript(frrtest.TestMultiOut):
+ program = "./test_frrscript"
+
+ TestFrrscript.exit_cleanly()
diff --git a/tests/subdir.am b/tests/subdir.am
index ca477851e..c2153140f 100644
--- a/tests/subdir.am
+++ b/tests/subdir.am
@@ -59,6 +59,15 @@ TESTS_ZEBRA =
IGNORE_ZEBRA = --ignore=zebra/
endif
+if SCRIPTING
+TESTS_SCRIPTING = \
+ tests/lib/test_frrlua \
+ tests/lib/test_frrscript \
+ #end
+else
+TESTS_SCRIPTING =
+endif
+
clippy_scan += \
tests/lib/cli/test_cli.c \
tests/ospf6d/test_lsdb.c \
@@ -104,6 +113,7 @@ check_PROGRAMS = \
$(TESTS_OSPFD) \
$(TESTS_OSPF6D) \
$(TESTS_ZEBRA) \
+ $(TESTS_SCRIPTING) \
# end
if GRPC
@@ -289,6 +299,16 @@ tests_lib_test_checksum_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_checksum_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_checksum_LDADD = $(ALL_TESTS_LDADD)
tests_lib_test_checksum_SOURCES = tests/lib/test_checksum.c
+if SCRIPTING
+tests_lib_test_frrlua_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_frrlua_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_frrlua_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_frrlua_SOURCES = tests/lib/test_frrlua.c
+tests_lib_test_frrscript_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_frrscript_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_frrscript_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_frrscript_SOURCES = tests/lib/test_frrscript.c
+endif
tests_lib_test_graph_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_graph_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_graph_LDADD = $(ALL_TESTS_LDADD)
@@ -464,6 +484,14 @@ EXTRA_DIST += \
tests/zebra/test_lm_plugin.refout \
# end
+
+if SCRIPTING
+EXTRA_DIST += \
+ tests/lib/test_frrscript.py \
+ tests/lib/test_frrlua.py \
+ #end
+endif
+
.PHONY: tests/tests.xml
tests/tests.xml: $(check_PROGRAMS)
( cd tests; $(PYTHON) ../$(srcdir)/tests/runtests.py --junitxml=tests.xml -v ../$(srcdir)/tests $(IGNORE_BGPD) $(IGNORE_ISISD) $(IGNORE_OSPF6D); )