aboutsummaryrefslogtreecommitdiffstats
path: root/docs/usecases
diff options
context:
space:
mode:
authorDave Barach <dave@barachs.net>2020-02-10 17:12:06 -0500
committerDave Barach <openvpp@barachs.net>2020-02-11 17:10:07 +0000
commitdee0a3bb04f4e8d4fb588a1d5220042220765b83 (patch)
tree1ceb9f9f92a5e074692ed7bee04f63daf4fcced6 /docs/usecases
parent30e7671c85087b5781a34bde45ef3846f5de8c17 (diff)
http_static: VPP web application HowTo
This sketch should save a lot of time working out uninteresting but important details. Type: docs Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: Icd5705230adfda6539360ef3c46ff3a68b3bed74
Diffstat (limited to 'docs/usecases')
-rw-r--r--docs/usecases/index.rst2
-rw-r--r--docs/usecases/webapp.md274
2 files changed, 275 insertions, 1 deletions
diff --git a/docs/usecases/index.rst b/docs/usecases/index.rst
index 3bcc129f6ae..878902f7db5 100644
--- a/docs/usecases/index.rst
+++ b/docs/usecases/index.rst
@@ -18,4 +18,4 @@ extensive list, but should give a sampling of the many features contained in FD.
homegateway
contiv/index.rst
networksim
-
+ webapp
diff --git a/docs/usecases/webapp.md b/docs/usecases/webapp.md
new file mode 100644
index 00000000000..3b1a3b7b5b7
--- /dev/null
+++ b/docs/usecases/webapp.md
@@ -0,0 +1,274 @@
+Building VPP web applications
+=============================
+
+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
+ handle_get_status (http_builtin_method_type_t reqtype,
+ u8 * request, http_session_t * hs)
+ {
+ 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 */
+ hs->data = s;
+ hs->data_offset = 0;
+ hs->cache_pool_index = ~0;
+ hs->free_data = 1; /* free s when done with it, in the framework */
+ return 0;
+ }
+```
+
+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
+---------------------------------------------
+
+Call http_static_server_register_builtin_handler() as shown. It's
+likely 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",
+ "http_static_server_register_builtin_handler");
+
+ 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
+<site-root>/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 <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:
+
+```
+ http static server www-root /myhugosite/public uri tcp://0.0.0.0/2345 cache-size 5m fifo-size 8192
+```
+
+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
+```