]> git.puffer.fish Git - matthieu/frr.git/commitdiff
lib: start adding generic scripting stuff
authorQuentin Young <qlyoung@nvidia.com>
Sun, 29 Nov 2020 00:02:39 +0000 (19:02 -0500)
committerQuentin Young <qlyoung@nvidia.com>
Tue, 1 Dec 2020 23:37:14 +0000 (18:37 -0500)
Rather than let Luaisms propagate from the start, this is some generic
wrapper stuff that defines some semantics for interacting with scripts
that aren't specific to the underlying language.

The concept I have in mind for FRR's idea of a script is:

- has a name
- has some inputs, which have types
- has some outputs, which have types

I don't want to even say they have to be files; maybe we can embed
scripts in frr.conf, for example. Similarly the types of inputs and
outputs are probably going to end up being some language-specific setup.

For now, we will stick to this simple model, but the plan is to add full
object support (ie calling back into C).

This shouldn't be misconstrued as prepping for multilingual scripting
support, which is a bad idea for the following reasons:

- Each language would require different FFI methods, and specifically
  different object encoders; a lot of code
- Languages have different capabilities that would have to be brought to
  parity with each other; a lot of work
- Languages have *vastly* different performance characteristics; bad
  impressions, lots of issues we can't do anything about
- Each language would need a dedicated maintainer for the above reasons;
  pragmatically difficult
- Supporting multiple languages fractures the community and limits the
  audience with which a given script can be shared

The only pro for multilingual support would be ease of use for users not
familiar with Lua but familiar with one of the other supported
languages. This is not enough to outweigh the cons.

In order to get rich scripting capabilities, we need to be able to pass
representations of internal objects to the scripts. For example, a
script that performs some computation based on information about a peer
needs access to some equivalent of `struct peer` for the peer in
question. To transfer these objects from C-space into Lua-space we need
to encode them onto the Lua stack. This patch adds a mapping from
arbitrary type names to the functions that encode objects of that type.

For example, the function that encodes `struct peer` into a Lua table
could be registered with:

  bgp_peer_encoder_func(struct frrscript *fs, struct peer *peer)
  {
     // encode peer to Lua table, push to stack in fs->scriptinfo->L
  }

  frrscript_register_type_encoder("peer", bgp_peer_encoder_func);

Later on when calling a script that wants a peer, the plan is to be able
to specify the type name like so:

  frrscript_call(script, "peer", peer);

Using C-style types for the type names would have been nice, it might be
possible to do this with preprocessor magic or possibly python
preprocessing later on.

Signed-off-by: Quentin Young <qlyoung@nvidia.com>
mergeme no stdlib

Signed-off-by: Quentin Young <qlyoung@nvidia.com>
lib/command.c
lib/frrlua.c
lib/frrlua.h
lib/frrscript.c [new file with mode: 0644]
lib/frrscript.h [new file with mode: 0644]
lib/subdir.am

index 87110157f6eb8e851d1856c5bc606348dd35e9c1..99f86ec680f63106d0676845358d6927200f2a40 100644 (file)
@@ -2279,6 +2279,22 @@ done:
        return CMD_SUCCESS;
 }
 
+#ifdef DEV_BUILD
+DEFUN(script,
+      script_cmd,
+      "script SCRIPT",
+      "Test command - execute a script\n"
+      "Script name (same as filename in /etc/frr/scripts/\n")
+{
+       struct frrscript *fs = frrscript_load(argv[2]->arg, NULL);
+       int ret = frrscript_call(fs, 42);
+
+       vty_out(vty, "Script result: %d\n", ret);
+
+       return CMD_SUCCESS;
+}
+#endif
+
 /* Set config filename.  Called from vty.c */
 void host_config_set(const char *filename)
 {
index 15fda79201b3c280e82b71392af6f14be6358547..57288e66760de3b4a568e1ae086a95416da29c41 100644 (file)
@@ -26,6 +26,9 @@
 #include "frrlua.h"
 #include "log.h"
 #include "buffer.h"
+#include "frrscript.h"
+
+/* Lua stuff */
 
 /*
  * FRR convenience functions.
index bc017b644180789ab20ecffec86ae14fe743a386..37e8e8635b25920930b6c8cdc53f49be787d3a0d 100644 (file)
@@ -26,6 +26,7 @@
 #include "lauxlib.h"
 
 #include "prefix.h"
+#include "frrscript.h"
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/lib/frrscript.c b/lib/frrscript.c
new file mode 100644 (file)
index 0000000..9b27ca7
--- /dev/null
@@ -0,0 +1,133 @@
+/* Scripting foo
+ * Copyright (C) 2020  NVIDIA Corporation
+ * Quentin Young
+ *
+ * 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 "frrscript.h"
+#include "memory.h"
+#include "hash.h"
+#include "log.h"
+
+#include "frrlua.h"
+
+/* Type encoders */
+
+struct encoder {
+       char *typename;
+       int (*encoder)(struct lua_State *, const void *);
+};
+
+struct hash *encoder_hash;
+
+static unsigned int encoder_hash_key(const void *data)
+{
+       const struct encoder *e = data;
+
+       return string_hash_make(e->typename);
+}
+
+static bool encoder_hash_cmp(const void *d1, const void *d2)
+{
+       return !strcmp(d1, d2);
+}
+
+static void *encoder_alloc(void *arg)
+{
+       struct encoder *tmp = arg;
+
+       struct encoder *e = XCALLOC(MTYPE_TMP, sizeof(struct encoder));
+       e->typename = XSTRDUP(MTYPE_TMP, tmp->typename);
+
+       return e;
+}
+
+#if 0
+static void encoder_free(struct encoder *e)
+{
+       XFREE(MTYPE_TMP, e->typename);
+       XFREE(MTYPE_TMP, e);
+}
+#endif
+
+/* Generic script APIs */
+
+int frrscript_lua_call(struct frrscript *fs, ...)
+{
+       /* Process arguments according to argspec in fs */
+       /* ... */
+
+       int ret = lua_pcall(fs->L, 0, 0, 0);
+
+       /* Process stack result according to argspec in fs */
+       /* ... */
+
+       /* LUA_OK is 0, so we can just return lua_pcall's result directly */
+       return ret;
+}
+
+void frrscript_register_type_encoder(const char *typename,
+                                    int (*encoder)(lua_State *L, void *))
+{
+       struct encoder e = {
+               .typename = (char *) typename,
+               .encoder = NULL
+       };
+
+       if (hash_lookup(encoder_hash, &e)) {
+               zlog_backtrace(LOG_ERR);
+               assert(!"Type encoder double-registered.");
+       }
+
+       hash_get(encoder_hash, &e, encoder_alloc);
+}
+
+
+struct frrscript *frrscript_load(const char *name,
+                                int (*load_cb)(struct frrscript *))
+{
+       struct frrscript *fs = XCALLOC(MTYPE_TMP, sizeof(struct frrscript));
+
+       fs->name = XSTRDUP(MTYPE_TMP, name);
+       fs->L = luaL_newstate();
+
+       char fname[MAXPATHLEN];
+       snprintf(fname, sizeof(fname), FRRSCRIPT_PATH "/%s.lua", fs->name);
+
+       if (luaL_loadfile(fs->L, fname) != LUA_OK)
+               goto fail;
+
+       if (load_cb && (*load_cb)(fs) != 0)
+               goto fail;
+
+       return fs;
+fail:
+       frrscript_unload(fs);
+       return NULL;
+}
+
+void frrscript_unload(struct frrscript *fs)
+{
+       XFREE(MTYPE_TMP, fs->name);
+       XFREE(MTYPE_TMP, fs);
+}
+
+void frrscript_init()
+{
+       encoder_hash = hash_create(encoder_hash_key, encoder_hash_cmp, "Lua type encoders");
+}
diff --git a/lib/frrscript.h b/lib/frrscript.h
new file mode 100644 (file)
index 0000000..2988d90
--- /dev/null
@@ -0,0 +1,84 @@
+/* Scripting foo
+ * Copyright (C) 2020  NVIDIA Corporation
+ * Quentin Young
+ *
+ * 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
+ */
+#ifndef __FRRSCRIPT_H__
+#define __FRRSCRIPT_H__
+
+#include "frrlua.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FRRSCRIPT_PATH "/etc/frr/scripts"
+
+struct frrscript {
+       /* Script name */
+       char *name;
+
+       /* Lua state */
+       struct lua_State *L;
+};
+
+
+/*
+ * Create new FRR script.
+ */
+struct frrscript *frrscript_load(const char *name,
+                                int (*load_cb)(struct frrscript *));
+
+/*
+ * Destroy FRR script.
+ */
+void frrscript_unload(struct frrscript *fs);
+
+/*
+ * Register a Lua encoder for a type.
+ *
+ * tname
+ *    Name of type; e.g., "peer", "ospf_interface", etc. Chosen at will.
+ *
+ * encoder
+ *    Function pointer to encoder function. Encoder function should push a Lua
+ *    table representing the passed argument - which will have the C type
+ *    associated with the chosen 'tname' to the provided stack.
+ *
+ */
+void frrscript_register_type_encoder(const char *tname,
+                                    int (*encoder)(lua_State *, void *));
+
+/*
+ * Initialize scripting subsystem. Call this before anything else.
+ */
+void frrscript_init(void);
+
+/*
+ * Forward decl for frrscript_lua_call
+ */
+int frrscript_lua_call(struct frrscript *fs, ...);
+
+/*
+ * Call FRR script.
+ */
+#define frrscript_call(fs, ...) frrscript_lua_call((fs), __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __FRRSCRIPT_H__ */
index 038282a99b918015ff1d5c61141eac7f30b36b24..c5e32a56d9f3eb66c3f5897b8d9709ab2bdaabc3 100644 (file)
@@ -26,6 +26,7 @@ lib_libfrr_la_SOURCES = \
        lib/filter_nb.c \
        lib/frrcu.c \
        lib/frrlua.c \
+       lib/frrscript.c \
        lib/frr_pthread.c \
        lib/frrstr.c \
        lib/getopt.c \
@@ -184,6 +185,7 @@ pkginclude_HEADERS += \
        lib/filter.h \
        lib/freebsd-queue.h \
        lib/frrlua.h \
+       lib/frrscript.h \
        lib/frr_pthread.h \
        lib/frratomic.h \
        lib/frrcu.h \