Implement feature 5949

Enable dynamic connectivity setup in multi-site Network Services

The code required to implement the feature is contained in `osm_ro/wim`
as much as possible.

* `wim/engine.py` works together with `nfvo.py` to implement the
  feature
* `wim/persistence.py` is equivalent to `nfvo_db.py` and try to
  encapsulate most of the SQL-specific code, implementing a persistence
  layer
* `wim/http_handler.py` extends `httpserver.py` adding WIM-related HTTP
  routes
* `wim/wim_thread.py` is similar to `vim_thread.py` and controls the
  execution of WIM-related tasks
* `wim/actions.py` and `wim/wan_link_actions.py` implement the action
  handling specific code, calling instances of the `wim/wimconn.py`
  subclasses

WIM connectors are still a work in progress

Individual change details (newer to older)

- Add errors for inconsistent state

- Delay re-scheduled tasks

- Move lock to inside the persistence object

- Better errors for connector failures

- Try to cache the wan_link information before it is deleted from the database

- Integrate WanLinkDelete to NFVO

- Add WanLinkDelete implementation draft with some tests

- Add basic wim network creation

- Add minimal documentation for actions

- Add checks to the create action

- Improve documentation, rearrange insert_pending and remove unused functions on WimThread

- Integrate Action classes in refresh_tasks

- Add Action classes to avoid intricate conditions

- Adding Proposed License

- Move grouping of actions to persistence

- Change WimThread to use SQL to do the heavy lifting

- Simplify WimThread reload_actions

- Add tests for derive_wan_links

- Implement find_common_wim(s)

- Add tests for create_wim_account

- Add migration scripts for version 33

- Changes to WIM and VIM threads for vim_wim_actions

- Implement wim_account management according to the discussion

- Add WimHandler integration inside httpserver

- Add quick instructions to run the tests

- Add WIM functional tests using real database

- Add DB WIM port mapping

- RO WIM-related console scripts

- Add WIM integration to NFVO

- Improve database support focusing on tests

- RO NBI WIM-related commands in HTTP server

- Adding WIM tables to MANO DB

- Add wim http handler initial implementation

- Move http utility functions to separated files

    This separation allows the code to be reused more easily and avoids
    circular dependencies.

    (The httpserver can import other modules implementing http routes,
    and those modules can then use the utility functions without having
    to import back httpserver)

- Add a HTTP handler class and custom route decorator

    These tools can be used to create independent groups of bottle
    routes/callbacks in a OOP fashion

- Extract http error codes and related logic to separated file

Change-Id: Icd5fc9fa345852b8cf571e48f427dc10bdbd24c5
Signed-off-by: Anderson Bravalheri <a.bravalheri@bristol.ac.uk>
diff --git a/osm_ro/http_tools/handler.py b/osm_ro/http_tools/handler.py
new file mode 100644
index 0000000..49249a8
--- /dev/null
+++ b/osm_ro/http_tools/handler.py
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+##
+# Copyright 2018 University of Bristol - High Performance Networks Research
+# Group
+# All Rights Reserved.
+#
+# Contributors: Anderson Bravalheri, Dimitrios Gkounis, Abubakar Siddique
+# Muqaddas, Navdeep Uniyal, Reza Nejabati and Dimitra Simeonidou
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact with: <highperformance-networks@bristol.ac.uk>
+#
+# Neither the name of the University of Bristol nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# This work has been performed in the context of DCMS UK 5G Testbeds
+# & Trials Programme and in the framework of the Metro-Haul project -
+# funded by the European Commission under Grant number 761727 through the
+# Horizon 2020 and 5G-PPP programmes.
+##
+
+from types import MethodType
+
+from bottle import Bottle
+
+
+class route(object):
+    """Decorator that stores route information, so creating the routes can be
+    postponed.
+
+    This allows methods (OOP) with bottle.
+
+    Arguments:
+        method: HTTP verb (e.g. ``'get'``, ``'post'``, ``'put'``, ...)
+        path: URL path that will be handled by the callback
+    """
+    def __init__(self, method, path, **kwargs):
+        kwargs['method'] = method.upper()
+        self.route_info = (path, kwargs)
+
+    def __call__(self, function):
+        function.route_info = self.route_info
+        return function
+
+
+class BaseHandler(object):
+    """Base class that allows isolated webapp implementation using Bottle,
+    when used in conjunction with the ``route`` decorator.
+
+    In this context, a ``Handler`` is meant to be a collection of Bottle
+    routes/callbacks related to a specific topic.
+
+    A ``Handler`` instance can produce a WSGI app that can be mounted or merged
+    inside another more general bottle app.
+
+    Example:
+
+        from http_tools.handler import Handler, route
+        from http_tools.errors import ErrorHandler
+
+        class MyHandler(Handler):
+            plugins = [ErrorHandler()]
+            url_base = '/my/url/base'
+
+            @route('GET', '/some/path/<var>')
+            def get_var(self, var):
+                return var
+
+        app = MyHandler.wsgi_app
+        # ^  Webapp with a `GET /my/url/base/some/path/<var>` route
+    """
+    _wsgi_app = None
+
+    url_base = ''
+    """String representing a path fragment to be prepended to the routes"""
+
+    plugins = []
+    """Bottle plugins to be installed when creating the WSGI app"""
+
+    @property
+    def wsgi_app(self):
+        """Create a WSGI app based on the implemented callbacks"""
+
+        if self._wsgi_app:
+            # Return if cached
+            return self._wsgi_app
+
+        app = Bottle()
+
+        members = (getattr(self, m) for m in dir(self) if m != 'wsgi_app')
+        callbacks = (m for m in members
+                     if isinstance(m, MethodType) and hasattr(m, 'route_info'))
+
+        for callback in callbacks:
+            path, kwargs = callback.route_info
+            kwargs.update(callback=callback, apply=self.plugins)
+            app.route(self.url_base + path, **kwargs)
+
+        self._wsgi_app = app
+
+        return app