2021-08-19 11:38:06 +02:00
|
|
|
|
Web applications with VPP
|
|
|
|
|
=========================
|
|
|
|
|
|
|
|
|
|
Vpp includes a versatile http/https “static” server plugin. We quote the
|
|
|
|
|
word static in the previous sentence because the server is easily
|
|
|
|
|
extended. This note describes how to build a Hugo site which includes
|
|
|
|
|
both monitoring and control functions.
|
|
|
|
|
|
|
|
|
|
Let’s assume that we have a vpp data-plane plugin which needs a
|
|
|
|
|
monitoring and control web application. Here’s how to build one.
|
|
|
|
|
|
|
|
|
|
Step 1: Add URL handlers
|
|
|
|
|
------------------------
|
|
|
|
|
|
|
|
|
|
Individual URL handlers are pretty straightforward. You can return just
|
|
|
|
|
about anything you like, but as we work through the example you’ll see
|
|
|
|
|
why returning data in .json format tends to work out pretty well.
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
static int
|
2024-08-01 16:36:44 +02:00
|
|
|
|
handle_get_status (hss_url_handler_args_t *args)
|
2021-08-19 11:38:06 +02:00
|
|
|
|
{
|
|
|
|
|
my_main_t *mm = &my_main;
|
|
|
|
|
u8 *s = 0;
|
|
|
|
|
|
|
|
|
|
/* Construct a .json reply */
|
|
|
|
|
s = format (s, "{\"status\": {");
|
|
|
|
|
s = format (s, " \"thing1\": \"%s\",", mm->thing1_value_string);
|
|
|
|
|
s = format (s, " \"thing2\": \"%s\",", mm->thing2_value_string);
|
|
|
|
|
/* ... etc ... */
|
|
|
|
|
s = format (s, " \"lastthing\": \"%s\"", mm->last_value_string);
|
|
|
|
|
s = format (s, "}}");
|
|
|
|
|
|
|
|
|
|
/* And tell the static server plugin how to send the results */
|
2024-08-01 16:36:44 +02:00
|
|
|
|
args->data = s;
|
|
|
|
|
args->data_len = vec_len (s);
|
|
|
|
|
args->ct = HTTP_CONTENT_APP_JSON;
|
|
|
|
|
args->free_vec_data = 1; /* free s when done with it, in the framework */
|
|
|
|
|
return HSS_URL_HANDLER_OK;
|
2021-08-19 11:38:06 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Words to the Wise: Chrome has a very nice set of debugging tools. Select
|
|
|
|
|
“More Tools -> Developer Tools”. Right-hand sidebar appears with html
|
|
|
|
|
source code, a javascript debugger, network results including .json
|
|
|
|
|
objects, and so on.
|
|
|
|
|
|
|
|
|
|
Note: .json object format is **intolerant** of both missing and extra
|
|
|
|
|
commas, missing and extra curly-braces. It’s easy to waste a
|
|
|
|
|
considerable amount of time debugging .json bugs.
|
|
|
|
|
|
|
|
|
|
Step 2: Register URL handlers with the server
|
|
|
|
|
---------------------------------------------
|
|
|
|
|
|
2024-08-01 16:36:44 +02:00
|
|
|
|
Call ``hss_register_url_handler`` as shown. It’s likely
|
2021-08-19 11:38:06 +02:00
|
|
|
|
but not guaranteed that the static server plugin will be available.
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
plugin_url_init (vlib_main_t * vm)
|
|
|
|
|
{
|
|
|
|
|
void (*fp) (void *, char *, int);
|
|
|
|
|
|
|
|
|
|
/* Look up the builtin URL registration handler */
|
|
|
|
|
fp = vlib_get_plugin_symbol ("http_static_plugin.so",
|
2024-08-01 16:36:44 +02:00
|
|
|
|
"hss_register_url_handler");
|
2021-08-19 11:38:06 +02:00
|
|
|
|
|
|
|
|
|
if (fp == 0)
|
|
|
|
|
{
|
|
|
|
|
clib_warning ("http_static_plugin.so not loaded...");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(*fp) (handle_get_status, "status.json", HTTP_BUILTIN_METHOD_GET);
|
|
|
|
|
(*fp) (handle_get_run, "run.json", HTTP_BUILTIN_METHOD_GET);
|
|
|
|
|
(*fp) (handle_get_reset, "reset.json", HTTP_BUILTIN_METHOD_GET);
|
|
|
|
|
(*fp) (handle_get_stop, "stop.json", HTTP_BUILTIN_METHOD_GET);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Make sure to start the http static server **before** calling
|
|
|
|
|
plugin_url_init(…), or the registrations will disappear.
|
|
|
|
|
|
|
|
|
|
Step 3: Install Hugo, pick a theme, and create a site
|
|
|
|
|
-----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
Please refer to the Hugo documentation.
|
|
|
|
|
|
|
|
|
|
See `the Hugo Quick Start
|
|
|
|
|
Page <https://gohugo.io/getting-started/quick-start>`__. Prebuilt binary
|
|
|
|
|
artifacts for many different environments are available on `the Hugo
|
|
|
|
|
release page <https://github.com/gohugoio/hugo/releases>`__.
|
|
|
|
|
|
|
|
|
|
To pick a theme, visit `the Hugo Theme
|
|
|
|
|
site <https://themes.gohugo.io>`__. Decide what you need your site to
|
|
|
|
|
look like. Stay away from complex themes unless you’re prepared to spend
|
|
|
|
|
considerable time tweaking and tuning.
|
|
|
|
|
|
|
|
|
|
The “Introduction” theme is a good choice for a simple site, YMMV.
|
|
|
|
|
|
|
|
|
|
Step 4: Create a “rawhtml” shortcode
|
|
|
|
|
------------------------------------
|
|
|
|
|
|
|
|
|
|
Once you’ve initialized your new site, create the directory
|
|
|
|
|
/layouts/shortcodes. Create the file “rawhtml.html” in that directory,
|
|
|
|
|
with the following contents:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
<!-- raw html -->
|
|
|
|
|
{{.Inner}}
|
|
|
|
|
|
|
|
|
|
This is a key trick which allows a static Hugo site to include
|
|
|
|
|
javascript code.
|
|
|
|
|
|
|
|
|
|
Step 5: create Hugo content which interacts with vpp
|
|
|
|
|
----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
Now it’s time to do some web front-end coding in javascript. Of course,
|
|
|
|
|
you can create static text, images, etc. as described in the Hugo
|
|
|
|
|
documentation. Nothing changes in that respect.
|
|
|
|
|
|
|
|
|
|
To include dynamically-generated data in your Hugo pages, splat down
|
|
|
|
|
some
|
|
|
|
|
|
|
|
|
|
.. raw:: html
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
|
|
HTML tags, and define a few buttons:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
{{< rawhtml >}}
|
|
|
|
|
<div id="Thing1"></div>
|
|
|
|
|
<div id="Thing2"></div>
|
|
|
|
|
<div id="Lastthing"></div>
|
|
|
|
|
<input type="button" value="Run" onclick="runButtonClick()">
|
|
|
|
|
<input type="button" value="Reset" onclick="resetButtonClick()">
|
|
|
|
|
<input type="button" value="Stop" onclick="stopButtonClick()">
|
|
|
|
|
<div id="Message"></div>
|
|
|
|
|
{{< /rawhtml >}}
|
|
|
|
|
|
|
|
|
|
Time for some javascript code to interact with vpp:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
{{< rawhtml >}}
|
|
|
|
|
<script>
|
|
|
|
|
async function getStatusJson() {
|
|
|
|
|
pump_url = location.href + "status.json";
|
|
|
|
|
const json = await fetch(pump_url, {
|
|
|
|
|
method: 'GET',
|
|
|
|
|
mode: 'no-cors',
|
|
|
|
|
cache: 'no-cache',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
.then((response) => response.json())
|
|
|
|
|
.catch(function(error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return json.status;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
async function sendButton(which) {
|
|
|
|
|
my_url = location.href + which + ".json";
|
|
|
|
|
const json = await fetch(my_url, {
|
|
|
|
|
method: 'GET',
|
|
|
|
|
mode: 'no-cors',
|
|
|
|
|
cache: 'no-cache',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
.then((response) => response.json())
|
|
|
|
|
.catch(function(error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
});
|
|
|
|
|
return json.message;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
async function getStatus() {
|
|
|
|
|
const status = await getStatusJson();
|
|
|
|
|
|
|
|
|
|
document.getElementById("Thing1").innerHTML = status.thing1;
|
|
|
|
|
document.getElementById("Thing2").innerHTML = status.thing2;
|
|
|
|
|
document.getElementById("Lastthing").innerHTML = status.lastthing;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
async function runButtonClick() {
|
|
|
|
|
const json = await sendButton("run");
|
|
|
|
|
document.getElementById("Message").innerHTML = json.Message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function resetButtonClick() {
|
|
|
|
|
const json = await sendButton("reset");
|
|
|
|
|
document.getElementById("Message").innerHTML = json.Message;
|
|
|
|
|
}
|
|
|
|
|
async function stopButtonClick() {
|
|
|
|
|
const json = await sendButton("stop");
|
|
|
|
|
document.getElementById("Message").innerHTML = json.Message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getStatus();
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
{{< /rawhtml >}}
|
|
|
|
|
|
|
|
|
|
At this level, javascript coding is pretty simple. Unless you know
|
|
|
|
|
exactly what you’re doing, please follow the async function / await
|
|
|
|
|
pattern shown above.
|
|
|
|
|
|
|
|
|
|
Step 6: compile the website
|
|
|
|
|
---------------------------
|
|
|
|
|
|
|
|
|
|
At the top of the website workspace, simply type “hugo”. The compiled
|
|
|
|
|
website lands in the “public” subdirectory.
|
|
|
|
|
|
|
|
|
|
You can use the Hugo static server - with suitable stub javascript code
|
|
|
|
|
- to see what your site will eventually look like. To start the hugo
|
|
|
|
|
static server, type “hugo server”. Browse to “http://localhost:1313”.
|
|
|
|
|
|
|
|
|
|
Step 7: configure vpp
|
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
|
|
In terms of command-line args: you may wish to use poll-sleep-usec 100
|
|
|
|
|
to keep the load average low. Totally appropriate if vpp won’t be
|
|
|
|
|
processing a lot of packets or handling high-rate http/https traffic.
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
unix {
|
|
|
|
|
...
|
|
|
|
|
poll-sleep-usec 100
|
|
|
|
|
startup-config ... see below ...
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
If you wish to provide an https site, configure tls. The simplest tls
|
|
|
|
|
configuration uses a built-in test certificate - which will annoy Chrome
|
|
|
|
|
/ Firefox - but it’s sufficient for testing:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
tls {
|
|
|
|
|
use-test-cert-in-ca
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vpp startup configuration
|
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
|
|
Enable the vpp static server by way of the startup config mentioned
|
|
|
|
|
above:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
2024-08-01 16:36:44 +02:00
|
|
|
|
http static server url-handlers www-root /myhugosite/public uri tcp://0.0.0.0/2345 cache-size 5m fifo-size 8192
|
2021-08-19 11:38:06 +02:00
|
|
|
|
|
|
|
|
|
The www-root must be specified, and must correctly name the compiled
|
|
|
|
|
hugo site root. If your Hugo site is located at /myhugosite, specify
|
|
|
|
|
“www-root /myhugosite/public” in the “http static server” stanza. The
|
|
|
|
|
uri shown above binds to TCP port 2345.
|
|
|
|
|
|
|
|
|
|
If you’re using https, use a uri like “tls://0.0.0.0/443” instead of the
|
|
|
|
|
uri shown above.
|
|
|
|
|
|
|
|
|
|
You may want to add a Linux host interface to view the full-up site
|
|
|
|
|
locally:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
create tap host-if-name lstack host-ip4-addr 192.168.10.2/24
|
|
|
|
|
set int ip address tap0 192.168.10.1/24
|
|
|
|
|
set int state tap0 up
|