From: Waldemar Kozaczuk <jwkozac...@gmail.com>
Committer: Nadav Har'El <n...@scylladb.com>
Branch: master

Modularized httpserver REST API

Broke down OSv REST API provides by httpserver-api into number of fine-grained plugins that implement specific subset of API and thus makes it possible to compose REST API
in "a la carte" fashion.

Deprecated file_mappings.cc that hard-coded serving specific static content (swagger-ui,osv-gui) in favor of more generic logic in global_server.cc that allows to configure redirects and file/directory
mappings rules via /tmp/httpserver.conf file.

Please see JSON example below that demonstrates two sections - redirects and file_mappings -
but YAML will obviously work as well.

{
  "ipaddress": "0.0.0.0",
  "port": "8000",
  "redirects": [
    {
      "path": "/",
      "target_path": "/dashboard/"
    }
  ],
  "file_mappings": [
    {
      "path": "/api-gui",
      "file": "/usr/mgmt/swagger-ui/dist/index.html",
"exact_match": true // path should be matched exactly otherwise treat path like a rule
    },
    {
      "path": "/api",
      "directory": "/usr/mgmt/api",
      "content_replace": "json"
    },
    {
      "path": "/dashboard_static",
      "directory": "/usr/mgmt/gui/dashboard_static"
    },
    {
      "path": "/dashboard",
      "file": "/usr/mgmt/gui/dashboard/index.html"
    },
    {
      "path": "/api-gui",
      "directory": "/usr/mgmt/swagger-ui/dist"
    }
  ]
}

Created default httpserver.conf that configures httpserver to serve same
 static content same way for backwards compatibility.

Added integration test to httpserver module to verify it serves static content
 per configuration in supplied /tmp/httpserver.conf.

Fixes #885

Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com>

Message-Id: <1503345255-22147-1-git-send-email-jwkozac...@gmail.com>

---
diff --git a/modules/httpserver-api/.gitignore b/modules/httpserver-api/.gitignore
--- a/modules/httpserver-api/.gitignore
+++ b/modules/httpserver-api/.gitignore
@@ -1,3 +1,4 @@
-libhttpserver.so
+*.so
 autogen/*
 obj/*
+*.manifest
diff --git a/modules/httpserver-api/Makefile b/modules/httpserver-api/Makefile
--- a/modules/httpserver-api/Makefile
+++ b/modules/httpserver-api/Makefile
@@ -24,10 +24,10 @@ libs-dir = $(miscbase)/usr/lib64
 boost-libs := -lboost_system -lboost_filesystem -lboost_regex

 # the build target executable:
-TARGET = httpserver
+TARGET = httpserver-api
 JSON_FILES := $(wildcard api-doc/listings/*.json)
JSON_CC_FILES := $(subst .json,.json.cc,$(subst api-doc/listings/,autogen/,$(JSON_FILES))) -CPP_FILES := $(JSON_CC_FILES) $(wildcard *.cc) $(wildcard json/*.cc) $(wildcard api/*.cc)
+CPP_FILES := $(JSON_CC_FILES) $(wildcard *.cc) $(wildcard json/*.cc)
 STUB_CPP_FILES := $(wildcard stub/*.cc)
 OBJ_FILES := $(addprefix obj/,$(CPP_FILES:.cc=.o))
 STUB_FILES := $(addprefix obj/,$(STUB_CPP_FILES:.cc=.o))
@@ -50,16 +50,18 @@ DEPS := $(OBJ_FILES:.o=.d)

 module: all

-all: init lib$(TARGET).so
+all: lib$(TARGET).so api_api api_app api_env api_file api_fs api_hardware api_network api_os api_trace + $(call quiet, cat _usr_*.manifest | sort | uniq > usr.manifest, CREATE_MANIFEST)

-init:
-       $(call very-quiet, mkdir -p obj)
-       $(call very-quiet, mkdir -p obj/stub)
-       $(call very-quiet, mkdir -p obj/json)
-       $(call very-quiet, mkdir -p obj/api)
-       $(call very-quiet, mkdir -p obj/autogen)
-       $(call very-quiet, mkdir -p autogen)
-.PHONY: init
+add_api_to_manifest = \
+ echo "/usr/mgmt/plugins/libhttpserver-$(1).so: `realpath libhttpserver-$(1).so`" > _usr_$(1).manifest
+
+add_boost_dependencies_to_manifest = \
+ ldd libhttpserver-$(1).so | grep boost | sed 's/ *[^ ] *\(.*\) => \(.*\) .*/\/usr\/lib\/\1: \2/' > _usr_$(1)_boost.manifest
+
+api_%: libhttpserver-api_%.so
+       $(call quiet, $(call add_api_to_manifest,$@), APPEND_TO_MANIFEST $@)
+       $(call very-quiet, $(call add_boost_dependencies_to_manifest,$@))

 stub-lib: $(STUB_FILES)
        $(call quiet, $(CXX) $(CXXFLAGS) -shared -o $(TARGET)-stub.so $^, LINK 
$@)
@@ -69,37 +71,49 @@ $(TARGET): $(OBJ_FILES) $(STUB_FILES)

 lib$(TARGET).so: $(OBJ_FILES)
$(call quiet, $(CXX) $(CXXFLAGS) -shared $(STATIC_LIBS) -o $@ $^ $(DYN_LIBS), LINK $@) - ldd $@ | grep boost | sed 's/ *[^ ] *\(.*\) => \(.*\) .*/\/\1: \2/' > usr.manifest + $(call very-quiet, ldd $@ | grep boost | sed 's/ *[^ ] *\(.*\) => \(.*\) .*/\usr\/lib\/\1: \2/' > _usr_$(TARGET).manifest)
+
+libhttpserver-api_%.so: obj/autogen/%.json.o obj/api/%.o
+ $(call quiet, $(CXX) $(CXXFLAGS) -shared $(STATIC_LIBS) -o $@ $^ $(DYN_LIBS), LINK $@)

 ifneq ($(MAKECMDGOALS),clean)
 -include $(DEPS)
 endif

 autogen/%.cc: api-doc/listings/% json2code.py
+       $(call very-quiet, mkdir -p autogen)
        $(call quiet,./json2code.py -f $< -ns json, GEN $@)

+init_obj = \
+       $(call very-quiet, mkdir -p obj/stub) \
+       $(call very-quiet, mkdir -p obj/json) \
+       $(call very-quiet, mkdir -p obj/api) \
+       $(call very-quiet, mkdir -p obj/autogen)
+
 obj/%.o: %.cc
+       $(call init_obj)
        $(call quiet, $(CXX) $(CXXFLAGS) -c -MMD  -o $@ $<, CXX $@)

 clean:
        $(call quiet, $(RM) $(TARGET), CLEAN)
-       $(call very-quiet, $(RM) lib$(TARGET).so)
-       $(call very-quiet, $(RM) -rf obj/*)
-       $(call very-quiet, $(RM) -rf autogen/*)
+       $(call very-quiet, $(RM) lib*.so)
+       $(call very-quiet, $(RM) -rf obj)
+       $(call very-quiet, $(RM) -rf autogen)
+       $(call very-quiet, $(RM) -f *usr*.manifest)

 check: check-http check-ssl

 check-http:
        # Test plain HTTP
        cd $(src) && \
        make image=httpserver-api,jetty && \
-       PYTHONPATH=$(src)/scripts modules/httpserver-api/tests/testhttpserver.py
+ PYTHONPATH=$(src)/scripts modules/httpserver-api/tests/testhttpserver-api.py

 check-ssl:
        # Test SSL
        cd $(src) && \
        make image=httpserver-api.fg_ssl,certs,jetty && \
-       PYTHONPATH=$(src)/scripts 
modules/httpserver-api/tests/testhttpserver.py \
+ PYTHONPATH=$(src)/scripts modules/httpserver-api/tests/testhttpserver-api.py \
                --cert modules/certs/build/client.pem \
                --key modules/certs/build/client.key \
                --cacert modules/certs/build/cacert.pem
diff --git a/modules/httpserver-api/api/api.cc b/modules/httpserver-api/api/api.cc
--- a/modules/httpserver-api/api/api.cc
+++ b/modules/httpserver-api/api/api.cc
@@ -240,6 +240,10 @@ class api_param_handler : public handler_base {
     routes& _routes;
 };

+extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
+    httpserver::api::api::init(*routes);
+}
+
 void init(routes& routes)
 {
     api_json_init_path("Advanced API options");
diff --git a/modules/httpserver-api/api/app.cc b/modules/httpserver-api/api/app.cc
--- a/modules/httpserver-api/api/app.cc
+++ b/modules/httpserver-api/api/app.cc
@@ -49,6 +49,10 @@ static std::string exec_app(const std::string& cmnd_line, bool new_program) {
     return app_ids;
 }

+extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
+    httpserver::api::app::init(*routes);
+}
+
 void init(routes& routes)
 {
     app_json_init_path("app API");
diff --git a/modules/httpserver-api/api/env.cc b/modules/httpserver-api/api/env.cc
--- a/modules/httpserver-api/api/env.cc
+++ b/modules/httpserver-api/api/env.cc
@@ -19,6 +19,10 @@ using namespace json;
 using namespace std;
 using namespace env_json;

+extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
+    httpserver::api::env::init(*routes);
+}
+
 void init(routes& routes)
 {
     env_json_init_path("Environment variables API");
diff --git a/modules/httpserver-api/api/file.cc b/modules/httpserver-api/api/file.cc
--- a/modules/httpserver-api/api/file.cc
+++ b/modules/httpserver-api/api/file.cc
@@ -443,6 +443,10 @@ class put_file_handler : public handler_base {
     }
 };

+extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
+    httpserver::api::file::init(*routes);
+}
+
 void init(routes& routes)
 {
     file_json_init_path("file API");
diff --git a/modules/httpserver-api/api/files_mapping.cc b/modules/httpserver-api/api/files_mapping.cc
--- a/modules/httpserver-api/api/files_mapping.cc
+++ b/modules/httpserver-api/api/files_mapping.cc
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2014 Cloudius Systems, Ltd.
- *
- * This work is open source software, licensed under the terms of the
- * BSD license as described in the LICENSE file in the top-level directory.
- */
-
-#include "files_mapping.hh"
-#include "routes.hh"
-#include "transformers.hh"
-namespace httpserver {
-
-namespace api {
-
-namespace files_mapping {
-
-void init(routes& routes)
-{
-    function_handler* redirect =
-        new function_handler(
-    [](const_req req) {
-        throw redirect_exception(req.get_protocol_name() + "://" +
-                req.get_header("Host") + "/dashboard/");
-        // The return is required so the lambda expression would have
-        // the right signature
-        return "";
-    });
-    routes.put(GET, "/", redirect);
-    file_handler* index = new file_handler(
-        "/usr/mgmt/swagger-ui/dist/index.html", nullptr, true);
-    routes.put(GET, "/api-gui", index);
-
-    directory_handler* api = new directory_handler("/usr/mgmt/api",
-            new content_replace("json"));
-    routes.add(GET, url("/api").remainder("path"), api);
-    routes.add(GET, url("/dashboard_static").remainder("path"),
-               new directory_handler("/usr/mgmt/gui/dashboard_static"));
-    routes.add(GET, url("/dashboard").remainder("path"),
-               new file_handler("/usr/mgmt/gui/dashboard/index.html"));
-    routes.add(GET, url("/api-gui").remainder("path"),
-               new directory_handler("/usr/mgmt/swagger-ui/dist"));
-}
-
-}
-}
-}
diff --git a/modules/httpserver-api/api/files_mapping.hh b/modules/httpserver-api/api/files_mapping.hh
--- a/modules/httpserver-api/api/files_mapping.hh
+++ b/modules/httpserver-api/api/files_mapping.hh
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2014 Cloudius Systems, Ltd.
- *
- * This work is open source software, licensed under the terms of the
- * BSD license as described in the LICENSE file in the top-level directory.
- */
-
-#ifndef FILES_MAPPING_HH_
-#define FILES_MAPPING_HH_
-
-#include "routes.hh"
-
-namespace httpserver {
-
-namespace api {
-
-namespace files_mapping {
-
-/**
- * Initialize the routes object with specific routes mapping
- * @param routes - the routes object to fill
- */
-void init(routes& routes);
-
-}
-}
-}
-
-#endif /* FILES_MAPPING_HH_ */
diff --git a/modules/httpserver-api/api/fs.cc b/modules/httpserver-api/api/fs.cc
--- a/modules/httpserver-api/api/fs.cc
+++ b/modules/httpserver-api/api/fs.cc
@@ -32,6 +32,10 @@ static void fill_dfstat(DFStat& dfstat, const osv::mount_desc& mount, const stru
     dfstat.ffree = st.f_ffree;
 }

+extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
+    httpserver::api::fs::init(*routes);
+}
+
 void init(routes& routes) {

     fs_json_init_path("FS core API");
diff --git a/modules/httpserver-api/api/hardware.cc b/modules/httpserver-api/api/hardware.cc
--- a/modules/httpserver-api/api/hardware.cc
+++ b/modules/httpserver-api/api/hardware.cc
@@ -24,6 +24,10 @@ using namespace std;
 using namespace json;
 using namespace hardware_json;

+extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
+    httpserver::api::hardware::init(*routes);
+}
+
 void init(routes& routes)
 {
     hardware_json_init_path("Hardware management API");
diff --git a/modules/httpserver-api/api/network.cc b/modules/httpserver-api/api/network.cc
--- a/modules/httpserver-api/api/network.cc
+++ b/modules/httpserver-api/api/network.cc
@@ -43,6 +43,10 @@ static Interface get_interface(const string& name, ifnet* ifp, long time)
     return f;
 }

+extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
+    httpserver::api::network::init(*routes);
+}
+
 /**
  * Initialize the routes object with specific routes mapping
  * @param routes - the routes object to fill
diff --git a/modules/httpserver-api/api/os.cc b/modules/httpserver-api/api/os.cc
--- a/modules/httpserver-api/api/os.cc
+++ b/modules/httpserver-api/api/os.cc
@@ -33,6 +33,10 @@ using namespace std;
 using namespace json;
 using namespace os_json;

+extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
+    httpserver::api::os::init(*routes);
+}
+
 void init(routes& routes)
 {
     os_json_init_path("OS core API");
diff --git a/modules/httpserver-api/api/trace.cc b/modules/httpserver-api/api/trace.cc
--- a/modules/httpserver-api/api/trace.cc
+++ b/modules/httpserver-api/api/trace.cc
@@ -25,6 +25,10 @@ using namespace httpserver::json::trace_json;
 static std::unordered_map<tracepoint_base*,
     std::unique_ptr<tracepoint_counter>> counters;

+extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
+    httpserver::api::trace::init(*routes);
+}
+
 /**
  * Initialize the routes object with specific routes mapping
  * @param routes - the routes object to fill
diff --git a/modules/httpserver-api/global_server.cc b/modules/httpserver-api/global_server.cc
--- a/modules/httpserver-api/global_server.cc
+++ b/modules/httpserver-api/global_server.cc
@@ -6,17 +6,7 @@
  */

 #include "global_server.hh"
-#include "api/os.hh"
-#include "api/fs.hh"
-#include "api/files_mapping.hh"
-#include "api/file.hh"
-#include "api/trace.hh"
-#include "api/env.hh"
-#include "api/hardware.hh"
-#include "api/api.hh"
 #include "path_holder.hh"
-#include "api/network.hh"
-#include "api/app.hh"
 #include <iostream>
 #include <osv/app.hh>
 #include <fstream>
@@ -25,6 +15,8 @@
 #include <boost/foreach.hpp>
 #include "yaml-cpp/yaml.h"
 #include "json/api_docs.hh"
+#include <osv/debug.h>
+#include "transformers.hh"

 namespace httpserver {

@@ -43,32 +35,43 @@ bool global_server::run(po::variables_map& _config)
     if (get().s != nullptr) {
         return false;
     }
+    //TODO: It would be nice to add config option that specifies location
+ //of httpserver.conf. Also maybe the default location should be /etc/httpserver.conf + //but it seems that cloud-init module has some dependency on it --> look at
+    // modules/cloud-init/server-module.hh
     std::ifstream f("/tmp/httpserver.conf");
     if (f.is_open()) {
         try {
             YAML::Node doc = YAML::Load(f);
             for (auto node : doc) {
-                std::function<std::string(const YAML::Node&)> to_string;
-
-                to_string = [&](const YAML::Node & n) -> std::string {
-                    std::string s;
-                    if (n.IsSequence()) {
-                        for (auto & sn : n) {
-                            if (!s.empty()) {
-                                s += ',';
+                auto key = node.first.as<std::string>();
+                if (key == "redirects" && node.second.IsSequence()) {
+                    get().setup_redirects(node.second);
+                }
+ else if (key == "file_mappings" && node.second.IsSequence()) {
+                    get().setup_file_mappings(node.second);
+                }
+                else {
+ std::function<std::string(const YAML::Node&)> to_string;
+
+                    to_string = [&](const YAML::Node & n) -> std::string {
+                        std::string s;
+                        if (n.IsSequence()) {
+                            for (auto & sn : n) {
+                                if (!s.empty()) {
+                                    s += ',';
+                                }
+                                s += to_string(sn);
                             }
-                            s += to_string(sn);
+                        } else {
+                            s = n.as<std::string>();
                         }
-                    } else {
-                        s = n.as<std::string>();
-                    }
-                    return s;
-                };
+                        return s;
+                    };

-                auto key = node.first.as<std::string>();
-                auto val = to_string(node.second);
-
-                get().set(key, val);
+                    auto val = to_string(node.second);
+                    get().set(key, val);
+                }
             }
         } catch (const std::exception& e) {
std::cout << "httpserver Failed reading the configuration file " << e.what() << std::endl;
@@ -87,18 +90,69 @@ bool global_server::run(po::variables_map& _config)

     osv::this_application::on_termination_request([&] {
         get().s->close();
+        for( auto plugin : get().plugins) {
+            dlclose(plugin);
+        }
     });

     get().s->run();
     return true;
 }

+void global_server::setup_file_mappings(const YAML::Node& file_mappings_node) {
+    for (auto node : file_mappings_node) {
+        const YAML::Node path = node["path"];
+        if (path && node["directory"]) {
+ const std::string directory = node["directory"].as<std::string>(); + const YAML::Node content_replace_node = node["content_replace"];
+            directory_handler* handler = content_replace_node ?
+ new directory_handler(directory, new content_replace(content_replace_node.as<std::string>())) :
+                                         new directory_handler(directory);
+ _routes.add(GET, url(path.as<std::string>()).remainder("path"), handler); + debug("httpserver: setup directory mapping: [%s] -> [%s]\n", path.as<std::string>().c_str(), directory.c_str());
+        }
+        else if (path && node["file"]) {
+            const std::string file = node["file"].as<std::string>();
+            const YAML::Node exact_match_node = node["exact_match"];
+            file_handler* handler = new file_handler(file, nullptr, true);
+            if (exact_match_node && exact_match_node.as<bool>()) {
+                _routes.put(GET, path.as<std::string>(), handler);
+            }
+            else {
+ _routes.add(GET, url(path.as<std::string>()).remainder("path"), handler);
+            }
+ debug("httpserver: setup file mapping: [%s] -> [%s]\n", path.as<std::string>().c_str(), file.c_str());
+        }
+    }
+}
+
+void global_server::setup_redirects(const YAML::Node& redirects_node) {
+    for (auto node : redirects_node) {
+        const YAML::Node path_node = node["path"];
+        const YAML::Node target_path_node = node["target_path"];
+        if (path_node && target_path_node) {
+            const std::string path = path_node.as<std::string>();
+ const std::string target_path = target_path_node.as<std::string>();
+
+            function_handler* redirect =
+                    new function_handler(
+                            [target_path](const_req req) {
+ throw redirect_exception(req.get_protocol_name() + "://" + + req.get_header("Host") + target_path); + // The return is required so the lambda expression would have
+                                // the right signature
+                                return "";
+                            });
+            _routes.put(GET, path, redirect);
+ debug("httpserver: setup redirect: [%s] -> [%s]\n", path.c_str(), target_path.c_str());
+        }
+    }
+}

 global_server::global_server()
     : s(nullptr)
 {
     set_routes();
-
 }

 global_server& global_server::set(po::variables_map& _config)
@@ -116,47 +170,48 @@ global_server& global_server::set(const std::string& key,
     boost::program_options::variable_value v(val, false);
     config.insert(std::make_pair(std::string(key), v));
     return *this;
-
 }

 void global_server::set_routes()
 {
-
     path_holder::set_routes(&_routes);
     json::api_doc_init(_routes);
-    api::network::init(_routes);
-    api::os::init(_routes);
-    api::fs::init(_routes);
-    api::file::init(_routes);
-    api::trace::init(_routes);
-    api::env::init(_routes);
-    api::api::init(_routes);
-    api::app::init(_routes);
-    api::files_mapping::init(_routes);
-    api::hardware::init(_routes);

     {
         namespace fs = boost::filesystem;
-        using init_func_t = void(void*);
-        void* plugin;
-        init_func_t* plugin_init;
         fs::path plugin_path("/usr/mgmt/plugins/");
if (!fs::exists(plugin_path) && !fs::is_directory(plugin_path)) return; - BOOST_FOREACH(const fs::path& p, std::make_pair(fs::directory_iterator(plugin_path), + BOOST_FOREACH(const fs::path& path, std::make_pair(fs::directory_iterator(plugin_path),
                                                fs::directory_iterator())) {
-            if (fs::extension(p)==".so") {
-                plugin = dlopen(p.c_str(), RTLD_LAZY);
-                if ( plugin == nullptr ) {
-                    continue;
-                }
- plugin_init = reinterpret_cast<init_func_t*>(dlsym(plugin, "init"));
-                if ( plugin_init == nullptr ) {
-                    continue;
-                }
-                plugin_init(reinterpret_cast<void*>(&_routes));
+            if (fs::extension(path)==".so") {
+                load_plugin(path.string());
             }
         }
     }
 }

+void global_server::load_plugin(const std::string& path)
+{
+    void* plugin = dlopen(path.c_str(), RTLD_LAZY);
+    if ( plugin == nullptr ) {
+        return;
+    }
+    //
+ // The httpserver plugin needs to export proper initialization function intended to register + // any new routes. The function should be named "httpserver_plugin_register_routes" and
+    // defined to follow signature like so:
+ // extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
+    //      ....
+    //   }
+ // This is necessary to avoid C++ symbol mangling so that dlsym() can find the symbol
+    using init_func_t = void(routes*);
+ init_func_t* httpserver_plugin_register_routes = reinterpret_cast<init_func_t*>(dlsym(plugin, "httpserver_plugin_register_routes"));
+    if ( httpserver_plugin_register_routes == nullptr ) {
+        dlclose(plugin);
+        return;
+    }
+    plugins.push_back(plugin);
+    httpserver_plugin_register_routes(&_routes);
+    debug("httpserver: loaded plugin from path: %s\n",path.c_str());
+}
 }
diff --git a/modules/httpserver-api/global_server.hh b/modules/httpserver-api/global_server.hh
--- a/modules/httpserver-api/global_server.hh
+++ b/modules/httpserver-api/global_server.hh
@@ -9,9 +9,11 @@
 #define GLOBAL_SERVER_HH_
 #include "routes.hh"
 #include "server.hh"
+#include <vector>
 #include <boost/program_options/variables_map.hpp>
 #include <mutex>
 #include <condition_variable>
+#include <external/x64/misc.bin/usr/include/yaml-cpp/node/iterator.h>

 namespace po = boost::program_options;

@@ -65,10 +67,14 @@ private:

     global_server();
     void set_routes();
+    void setup_redirects(const YAML::Node& node);
+    void setup_file_mappings(const YAML::Node& node);
+    void load_plugin(const std::string& path);
     static global_server* instance;
     routes _routes;
     http::server::server* s;
     po::variables_map config;
+    std::vector<void*> plugins;

     /**
      * set configuration based on command line.
diff --git a/modules/httpserver-api/module.py b/modules/httpserver-api/module.py
--- a/modules/httpserver-api/module.py
+++ b/modules/httpserver-api/module.py
@@ -8,7 +8,7 @@
 _exe = '/libhttpserver.so'

 usr_files = FileMap()
-usr_files.add(os.path.join(_module, 'libhttpserver.so')).to(_exe)
+usr_files.add(os.path.join(_module, 'libhttpserver-api.so')).to(_exe)
 usr_files.add(os.path.join(_module, 'api-doc')).to('/usr/mgmt/api')

 api.require('openssl')
diff --git a/modules/httpserver-api/tests/basetest.py b/modules/httpserver-api/tests/basetest.py
--- a/modules/httpserver-api/tests/basetest.py
+++ b/modules/httpserver-api/tests/basetest.py
@@ -66,7 +66,10 @@ def is_reachable(cls):
         try:
             s.connect((cls._client.get_host(), cls._client.get_port()))
             s.close()
-            return cls.is_jvm_up()
+            if cls.config.check_jvm:
+                return cls.is_jvm_up()
+            else:
+                return True
         except socket.error:
             return False

@@ -149,8 +152,10 @@ def shutdown(cls):

     @classmethod
     def start_image(cls):
- jvm_plugin_api_listings_path = os.path.join(os.path.realpath(os.path.dirname(__file__)),'../../httpserver-jvm-plugin/api-doc/listings') - cls.jvm_api = cls.get_json_api_from_directory(jvm_plugin_api_listings_path,"jvm.json")
+        if cls.config.check_jvm:
+            jvm_plugin_api_listings_path = \
+ os.path.join(os.path.realpath(os.path.dirname(__file__)),'../../httpserver-jvm-plugin/api-doc/listings') + cls.jvm_api = cls.get_json_api_from_directory(jvm_plugin_api_listings_path,"jvm.json")
         cls.os_api = cls.get_json_api("os.json")
         if not cls.config.connect:
             cls.os_process = cls.exec_os()
diff --git a/modules/httpserver-api/tests/testhttpserver-api.py b/modules/httpserver-api/tests/testhttpserver-api.py
--- a/modules/httpserver-api/tests/testhttpserver-api.py
+++ b/modules/httpserver-api/tests/testhttpserver-api.py
@@ -30,6 +30,7 @@ def tearDownClass(cls):

 if __name__ == '__main__':
     basetest.Basetest.set_config(parser)
+    basetest.Basetest.config.check_jvm = True
     basetest.Basetest.start_image()
     del sys.argv[1:]
api_tests = unittest.TestLoader().discover(os.path.join(module_base, 'tests', 'api'), pattern='*.py') diff --git a/modules/httpserver-html5-gui/Makefile b/modules/httpserver-html5-gui/Makefile
--- a/modules/httpserver-html5-gui/Makefile
+++ b/modules/httpserver-html5-gui/Makefile
@@ -19,4 +19,12 @@ gui: osv-gui/.git
 osv-gui/.git:
cd $(src) && git submodule update --init -f modules/httpserver-html5-gui/osv-gui

+check:
+       # Test if HTTP servers serves properly static content
+       cd $(src) && \
+       scripts/build image=httpserver-html5-gui.fg && \
+ PYTHONPATH=$(src)/scripts:$(src)/modules/httpserver-api/tests modules/httpserver-html5-gui/tests/testhttpserver.py
+
+.PHONY: check
+
 .SECONDARY:
diff --git a/modules/httpserver-html5-gui/httpserver.conf b/modules/httpserver-html5-gui/httpserver.conf
--- a/modules/httpserver-html5-gui/httpserver.conf
+++ b/modules/httpserver-html5-gui/httpserver.conf
@@ -0,0 +1,34 @@
+{
+  "ipaddress": "0.0.0.0",
+  "port": "8000",
+  "redirects": [
+    {
+      "path": "/",
+      "target_path": "/dashboard/"
+    }
+  ],
+  "file_mappings": [
+    {
+      "path": "/api-gui",
+      "file": "/usr/mgmt/swagger-ui/dist/index.html",
+      "exact_match": true
+    },
+    {
+      "path": "/api",
+      "directory": "/usr/mgmt/api",
+      "content_replace": "json"
+    },
+    {
+      "path": "/dashboard_static",
+      "directory": "/usr/mgmt/gui/dashboard_static"
+    },
+    {
+      "path": "/dashboard",
+      "file": "/usr/mgmt/gui/dashboard/index.html"
+    },
+    {
+      "path": "/api-gui",
+      "directory": "/usr/mgmt/swagger-ui/dist"
+    }
+  ]
+}
diff --git a/modules/httpserver-html5-gui/module.py b/modules/httpserver-html5-gui/module.py
--- a/modules/httpserver-html5-gui/module.py
+++ b/modules/httpserver-html5-gui/module.py
@@ -8,6 +8,7 @@
 usr_files = FileMap()
usr_files.add(os.path.join(_module, 'swagger-ui', 'dist')).to('/usr/mgmt/swagger-ui/dist')
 usr_files.add(os.path.join(_module, 'osv-gui/public')).to('/usr/mgmt/gui')
+usr_files.add(os.path.join(_module, 
'httpserver.conf')).to('/tmp/httpserver.conf')

 api.require('httpserver-api')

@@ -21,4 +22,4 @@
 fg_ssl = api.run(_exe + ' --ssl')
 fg_cors = api.run(_exe + ' --access-allow=true')

-default = daemon
+default = daemon
\ No newline at end of file
diff --git a/modules/httpserver-html5-gui/tests/static/teststatic.py b/modules/httpserver-html5-gui/tests/static/teststatic.py
--- a/modules/httpserver-html5-gui/tests/static/teststatic.py
+++ b/modules/httpserver-html5-gui/tests/static/teststatic.py
@@ -0,0 +1,31 @@
+import requests
+import basetest
+
+class teststatic(basetest.Basetest):
+    def testredirect(self):
+        r = self.get("/")
+        self.assertEqual(r.status_code, 301)
+        self.assertEqual(r.url, self.get_url("/"))
+
+    def testapiguifile(self):
+        r = self.get("/api-gui/")
+        self.assertEqual(r.status_code, 200)
+
+    def testapi(self):
+        r = self.get("/api/listings/api.json")
+        self.assertEqual(r.status_code, 200)
+
+    def testdashboard_static(self):
+        r = self.get("/dashboard_static/smoothie.js")
+        self.assertEqual(r.status_code, 200)
+
+    def testdashboard(self):
+        r = self.get("/dashboard/")
+        self.assertEqual(r.status_code, 200)
+
+    def testapigui(self):
+        r = self.get("/api-gui/css/screen.css")
+        self.assertEqual(r.status_code, 200)
+
+    def get(self,path):
+        return requests.get(self.get_url(path), allow_redirects=False)
diff --git a/modules/httpserver-html5-gui/tests/testhttpserver.py b/modules/httpserver-html5-gui/tests/testhttpserver.py
--- a/modules/httpserver-html5-gui/tests/testhttpserver.py
+++ b/modules/httpserver-html5-gui/tests/testhttpserver.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+import sys
+import argparse
+import os
+import unittest
+import basetest
+
+from osv import client
+
+parser = argparse.ArgumentParser(description="""Testing the Httpserver""")
+
+module_base = os.path.join(os.path.realpath(os.path.dirname(__file__)), '..')
+osv_base = os.path.join(module_base, '..', '..')
+
+parser.add_argument('--connect', help='Connect to an existing image', action='store_true') +parser.add_argument('--run_script', help='path to the run image script', default=os.path.join(osv_base, 'scripts', 'run.py'))
+parser.add_argument('--cmd', help='the command to execute')
+parser.add_argument('--use_sudo', help='Use sudo with -n option instead of port forwarding', action='store_true') +parser.add_argument('--jsondir', help='location of the json files', default=os.path.join(module_base, '../httpserver-api/api-doc/listings/'))
+client.Client.add_arguments(parser)
+
+if __name__ == '__main__':
+    basetest.Basetest.set_config(parser)
+    basetest.Basetest.config.check_jvm = False
+    basetest.Basetest.start_image()
+    del sys.argv[1:]
+ tests = unittest.TestLoader().discover(os.path.join(module_base, 'tests', 'static'), pattern='*.py')
+    test_suite = unittest.TestSuite(tests)
+    unittest.TextTestRunner(verbosity=2).run(test_suite)
+    basetest.Basetest.shutdown()
diff --git a/modules/httpserver-jolokia-plugin/jolokia.cc b/modules/httpserver-jolokia-plugin/jolokia.cc
--- a/modules/httpserver-jolokia-plugin/jolokia.cc
+++ b/modules/httpserver-jolokia-plugin/jolokia.cc
@@ -26,9 +26,8 @@ static void verify_jvm() {
         throw httpserver::not_found_exception("JVM not running");
     }
 }
-extern "C" void init(void* arg) {
-    auto r = reinterpret_cast<httpserver::routes*>(arg);
-    httpserver::api::jolokia::init(*r);
+extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
+    httpserver::api::jolokia::init(*routes);
 }
 /**
  * Initialize the routes object with specific routes mapping
diff --git a/modules/httpserver-jvm-plugin/jvm.cc b/modules/httpserver-jvm-plugin/jvm.cc
--- a/modules/httpserver-jvm-plugin/jvm.cc
+++ b/modules/httpserver-jvm-plugin/jvm.cc
@@ -61,9 +61,8 @@ class set_jmx_handler : public handler_base {
     }
 };

-extern "C" void init(void* arg) {
-    auto r = reinterpret_cast<httpserver::routes*>(arg);
-    httpserver::api::jvm::init(*r);
+extern "C" void httpserver_plugin_register_routes(httpserver::routes* routes) {
+    httpserver::api::jvm::init(*routes);
 }
 /**
  * Initialize the routes object with specific routes mapping

--
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to