RIFT OSM R1 Initial Submission

Signed-off-by: Jeremy Mordkoff <jeremy.mordkoff@riftio.com>
diff --git a/rwcal/rift/cal/client.py b/rwcal/rift/cal/client.py
new file mode 100644
index 0000000..4717b0b
--- /dev/null
+++ b/rwcal/rift/cal/client.py
@@ -0,0 +1,68 @@
+"""
+# 
+#   Copyright 2016 RIFT.IO Inc
+#
+#   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.
+#
+
+@file client.py
+@author Varun Prasad(varun.prasad@riftio.com)
+@date 2016-06-14
+"""
+
+import os
+
+import gi
+gi.require_version('RwcalYang', '1.0')
+gi.require_version('RwCal', '1.0')
+gi.require_version('RwLog', '1.0')
+
+from gi.repository import RwcalYang
+
+import rift.cal.utils as cal_utils
+
+
+class CloudsimClient(cal_utils.CloudSimCalMixin):
+    """Cloudsim client that handles interactions with the server.
+    """
+    def __init__(self, log):
+        super().__init__()
+        self.log = log
+
+    @property
+    def images(self):
+        _, images = self.cal.get_image_list(self.account)
+        return images.imageinfo_list or []
+
+    @property
+    def vlinks(self):
+        _, vlinks = self.cal.get_virtual_link_list(self.account)
+        return vlinks.virtual_link_info_list or []
+
+    @property
+    def vdus(self):
+        _, vdus = self.cal.get_vdu_list(self.account)
+        return vdus.vdu_info_list or []
+
+    def upload_image(self, location, name=None):
+        """Onboard image to cloudsim server."""
+
+        image = RwcalYang.ImageInfoItem()
+        image.name = name or os.path.basename(location)
+        image.location = location
+        image.disk_format = "qcow2"
+        rc, image.id = self.cal.create_image(self.account, image)
+
+        self.log.info("Image created: {}".format(image.as_dict()))
+
+        return image
diff --git a/rwcal/rift/cal/cloudsim b/rwcal/rift/cal/cloudsim
new file mode 100644
index 0000000..fc2e4dd
--- /dev/null
+++ b/rwcal/rift/cal/cloudsim
@@ -0,0 +1,248 @@
+#!/usr/bin/env python3
+
+import argparse
+import logging
+import os
+import sys
+
+import gi
+gi.require_version('RwcalYang', '1.0')
+gi.require_version('RwCal', '1.0')
+gi.require_version('RwLog', '1.0')
+
+import rift.cal.server as cal_server
+import rift.cal.client as cal_client
+import rift.cal.utils as cal_utils
+import rift.rwcal.cloudsim.lxc as lxc
+import rift.rwcal.cloudsim.lvm as lvm
+import rift.rwcal.cloudsim.shell as shell
+
+from prettytable import PrettyTable
+
+
+START_PARSER = "start"
+STOP_PARSER = "stop"
+CLEAN_PARSER = "clean"
+FCLEAN_PARSER = "force-clean"
+IMAGE_PARSER = "image-create"
+STATUS_PARSER = "status"
+
+
+class CloudsimOperations(cal_utils.CloudSimCalMixin):
+    def __init__(self, args):
+        super().__init__()
+        self.log = cal_utils.Logger(
+                    daemon_mode=False,
+                    log_name="Parser",
+                    log_level=logging.getLevelName(args.log_level)).logger
+
+        self.args = args
+        self.operations = cal_server.CloudsimServerOperations(self.log)
+        self.client = cal_client.CloudsimClient(self.log)
+        self._cal, self._account = None, None
+
+    @property
+    def log_file(self):
+        return cal_utils.Logger.LOG_FILE
+
+    @cal_utils.check_and_create_bridge
+    def start_server(self):
+        self.operations.start_server(foreground=self.args.foreground)
+
+    @cal_utils.check_and_create_bridge
+    def stop_server(self):
+        self.operations.stop_server()
+
+    @cal_utils.check_and_create_bridge
+    def clean_resources(self):
+        """Clean all resource using rest APIs. """
+        self.operations.clean_server(images=self.args.all)
+
+    @cal_utils.check_and_create_bridge
+    def upload_image(self):
+        """Onboard image to cloudsim server."""
+        self.client.upload_image(self.args.location, name=self.args.name)
+
+    def force_clean_resources(self):
+        """Force clean up all resource. """
+        self.log.info("Cleaning up logs")
+        shell.command("rm -f {}".format(self.log_file))
+
+        self.log.info("Cleaning up PID file")
+        shell.command("rm -f {}".format(self.operations.PID_FILE))
+
+        try:
+            self.log.info("Purging LXC resources")
+            for container in lxc.containers():
+                lxc.stop(container)
+
+            for container in lxc.containers():
+                lxc.destroy(container)
+
+            lvm.destroy('rift')
+
+        except shell.ProcessError:
+            self.log.exception("Unable to purge resources. Trying a force clean now.")
+            lxc.force_clean()
+
+    @cal_utils.check_and_create_bridge
+    def show_status(self):
+
+        cld_tbl = PrettyTable(['PID', 'Status', 'Log file'])
+
+        pid = self.operations.pid
+        if pid:
+            cld_tbl.add_row([pid, "RUNNING", self.log_file])
+        else:
+            cld_tbl.add_row(["-", "STOPPED", self.log_file])
+
+        print ("Cloudsim server:")
+        print (cld_tbl)
+
+        if not pid:
+            return
+
+        # Images
+        img_tbl = PrettyTable(['ID', 'Name', 'Format'])
+        vlink_tbl = PrettyTable([
+                'ID', 'Name', 'Bridge Name', 'State', 'Subnet', 'Ports', "IPs"])
+        vdu_tbl = PrettyTable([
+            'ID', 'Name', 'LXC Name', 'IP', 'State', 'Ports', "VLink ID"])
+
+
+        images = self.client.images
+        if images:
+            for image in images:
+                img_tbl.add_row([image.id, image.name, image.disk_format])
+
+            print ("Images:")
+            print (img_tbl)
+
+        vlinks = self.client.vlinks
+        if vlinks:
+            for vlink in vlinks:
+
+                ports, ips = [], []
+                for cp in vlink.connection_points:
+                    ports.append("{} ({})".format(cp.name, cp.connection_point_id))
+                    ips.append(cp.ip_address)
+
+                vlink_tbl.add_row([
+                    vlink.virtual_link_id,
+                    vlink.name,
+                    vlink.name[:15],
+                    vlink.state,
+                    vlink.subnet,
+                    "\n".join(ports),
+                    "\n".join(ips)])
+
+            print ("Vlink:")
+            print (vlink_tbl)
+
+
+        lxc_to_ip = lxc.ls_info()
+        def get_lxc_name(ip):
+            for lxc_name, ips in lxc_to_ip.items():
+                if str(ip) in ips:
+                    return lxc_name
+
+            return ""
+
+        vdus = self.client.vdus
+        if vdus:
+            for vdu in vdus:
+                ports, links = [], []
+                for cp in vdu.connection_points:
+                    ports.append("{} ({})".format(cp.name, cp.ip_address))
+                    links.append(cp.virtual_link_id)
+
+                vdu_tbl.add_row([
+                    vdu.vdu_id, vdu.name, get_lxc_name(vdu.public_ip), vdu.public_ip,
+                    vdu.state, "\n".join(ports), "\n".join(links)])
+
+            print ("VDU:")
+            print (vdu_tbl)
+
+
+def parse(arguments):
+    parser = argparse.ArgumentParser(description=__doc__,
+                                    formatter_class=argparse.RawDescriptionHelpFormatter)
+    parser.add_argument(
+            '--log-level', '-l',
+            default="WARNING",
+            type=str,
+            choices=["INFO", "DEBUG", "WARNING", "ERROR"],
+            help="Set log level, defaults to warning and above.")
+
+    subparsers = parser.add_subparsers()
+
+    start_parser = subparsers.add_parser(START_PARSER, help="Start the server")
+    start_parser.add_argument(
+            '--foreground', "-f",
+            help="Run the server in the foreground. The logs are sent to console.",
+            default=False,
+            action="store_true")
+    start_parser.set_defaults(which=START_PARSER)
+
+    stop_parser = subparsers.add_parser(STOP_PARSER, help="Stop the server")
+    stop_parser.set_defaults(which=STOP_PARSER)
+
+    clean_parser = subparsers.add_parser(
+            CLEAN_PARSER,
+            help="Clean LXC resources. By default all resources except " + \
+                 "images are cleared.")
+    clean_parser.add_argument(
+            '--all', '-a', 
+            help="Cleans up all resources including images",
+            default=False,
+            action="store_true")
+    clean_parser.set_defaults(which=CLEAN_PARSER)
+
+    fclean_parser = subparsers.add_parser(
+            FCLEAN_PARSER,
+            help="Force clean all lxc resources")
+    fclean_parser.set_defaults(which=FCLEAN_PARSER)
+
+    image_parser = subparsers.add_parser(IMAGE_PARSER, help="Upload images")
+    image_parser.add_argument(
+            '--name', '-n',
+            help="(Optional) Name of the image")
+    image_parser.add_argument(
+            '--location', '-l',
+            help="Image location. If name is not specified the basename of " + \
+                 "the image path is used.",
+            required=True)
+    image_parser.set_defaults(which=IMAGE_PARSER)
+
+    show_parser = subparsers.add_parser(
+            STATUS_PARSER,
+            help="Shows the current status of LXC")
+    show_parser.set_defaults(which=STATUS_PARSER)
+
+    args = parser.parse_args(arguments)
+
+    return args
+
+
+def main(args):
+
+    args = parse(args)
+
+    operations = CloudsimOperations(args)
+
+    if args.which == START_PARSER:
+        operations.start_server()
+    elif args.which == STOP_PARSER:
+        operations.stop_server()
+    elif args.which == FCLEAN_PARSER:
+        operations.force_clean_resources()
+    elif args.which == CLEAN_PARSER:
+        operations.clean_resources()
+    elif args.which == IMAGE_PARSER:
+        operations.upload_image()
+    elif args.which == STATUS_PARSER:
+        operations.show_status()
+
+
+if __name__ == "__main__":
+    main(sys.argv[1:])
diff --git a/rwcal/rift/cal/rwcal_status.py b/rwcal/rift/cal/rwcal_status.py
new file mode 100644
index 0000000..6867140
--- /dev/null
+++ b/rwcal/rift/cal/rwcal_status.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+# 
+#   Copyright 2016 RIFT.IO Inc
+#
+#   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.
+#
+#
+# @file rwcal_status.py
+# @brief This module defines Python utilities for dealing with rwcalstatus codes.
+
+import traceback
+import functools
+import gi
+gi.require_version('RwTypes', '1.0')
+
+from gi.repository import RwTypes, RwCal
+
+def rwcalstatus_from_exc_map(exc_map):
+    """ Creates an rwcalstatus decorator from a dictionary mapping exception
+    types to rwstatus codes, and return a error object containing Exception details
+    """
+
+    # A decorator that maps a Python exception to a particular return code.
+    # Also returns an object containing the error msg, traceback and rwstatus
+    # Automatically returns RW_SUCCESS when no Python exception was thrown.
+    # Prevents us from having to use try: except: handlers around every function call.
+
+    def rwstatus(arg=None, ret_on_failure=None):
+        def decorator(func):
+            @functools.wraps(func)
+            def wrapper(*args, **kwds):
+                rwcal_status = RwCal.RwcalStatus()
+                try:
+                    ret = func(*args, **kwds)
+
+                except Exception as e:
+                    rwcal_status.traceback = traceback.format_exc()
+                    rwcal_status.error_msg = str(e)
+
+                    ret_code = [status for exc, status in exc_map.items() if isinstance(e, exc)]
+                    ret_list = [None] if ret_on_failure is None else list(ret_on_failure)
+                    if len(ret_code):
+                        rwcal_status.status = ret_code[0]
+                    else:
+                        # If it was not explicitly mapped, print the full traceback as this
+                        # is not an anticipated error.
+                        traceback.print_exc()
+                        rwcal_status.status = RwTypes.RwStatus.FAILURE
+
+                    ret_list.insert(0, rwcal_status)
+                    return tuple(ret_list)
+
+
+                rwcal_status.status = RwTypes.RwStatus.SUCCESS
+                rwcal_status.traceback = ""
+                rwcal_status.error_msg = ""
+                ret_list = [rwcal_status]
+                if ret is not None:
+                    if type(ret) == tuple:
+                        ret_list.extend(ret)
+                    else:
+                        ret_list.append(ret)
+
+                return tuple(ret_list)
+
+            return wrapper
+
+        if isinstance(arg, dict):
+            exc_map.update(arg)
+            return decorator
+        elif ret_on_failure is not None:
+            return decorator
+        else:
+            return decorator(arg)
+
+    return rwstatus
diff --git a/rwcal/rift/cal/server/__init__.py b/rwcal/rift/cal/server/__init__.py
new file mode 100644
index 0000000..b81f6c5
--- /dev/null
+++ b/rwcal/rift/cal/server/__init__.py
@@ -0,0 +1,26 @@
+"""
+# 
+#   Copyright 2016 RIFT.IO Inc
+#
+#   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.
+#
+
+@file __init__.py
+@author Austin Cormier(austin.cormier@riftio.com)
+@author Varun Prasad(varun.prasad@riftio.com)
+@date 2016-06-14
+"""
+
+
+from .server import CalServer
+from .operations import CloudsimServerOperations
\ No newline at end of file
diff --git a/rwcal/rift/cal/server/app.py b/rwcal/rift/cal/server/app.py
new file mode 100644
index 0000000..355d653
--- /dev/null
+++ b/rwcal/rift/cal/server/app.py
@@ -0,0 +1,543 @@
+"""
+# 
+#   Copyright 2016 RIFT.IO Inc
+#
+#   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.
+#
+
+@file app.py
+@author Austin Cormier(austin.cormier@riftio.com)
+@author Varun Prasad(varun.prasad@riftio.com)
+@date 2016-06-14
+"""
+
+import asyncio
+import collections
+import concurrent.futures
+import logging
+import sys
+
+import tornado
+import tornado.httpserver
+import tornado.web
+import tornado.platform.asyncio
+
+import gi
+gi.require_version('RwcalYang', '1.0')
+gi.require_version('RwCal', '1.0')
+gi.require_version('RwLog', '1.0')
+gi.require_version('RwTypes', '1.0')
+from gi.repository import (
+    RwCal,
+    RwcalYang,
+    RwTypes,
+)
+
+logger = logging.getLogger(__name__)
+
+if sys.version_info < (3, 4, 4):
+    asyncio.ensure_future = asyncio.async
+
+
+class CalCallFailure(Exception):
+    pass
+
+
+class RPCParam(object):
+    def __init__(self, key, proto_type=None):
+        self.key = key
+        self.proto_type = proto_type
+
+
+class CalRequestHandler(tornado.web.RequestHandler):
+    def initialize(self, log, loop, cal, account, executor, cal_method,
+                   input_params=None, output_params=None):
+        self.log = log
+        self.loop = loop
+        self.cal = cal
+        self.account = account
+        self.executor = executor
+        self.cal_method = cal_method
+        self.input_params = input_params
+        self.output_params = output_params
+
+    def wrap_status_fn(self, fn, *args, **kwargs):
+
+        ret = fn(*args, **kwargs)
+        if not isinstance(ret, collections.Iterable):
+            ret = [ret]
+
+        rw_status = ret[0]
+
+        if type(rw_status) is RwCal.RwcalStatus:
+            rw_status = rw_status.status
+
+        if type(rw_status) != RwTypes.RwStatus:
+            raise ValueError("First return value of %s function was not a RwStatus" %
+                             fn.__name__)
+
+        if rw_status != RwTypes.RwStatus.SUCCESS:
+            msg = "%s returned %s" % (fn.__name__, str(rw_status))
+            self.log.error(msg)
+            raise CalCallFailure(msg)
+
+        return ret[1:]
+
+    @tornado.gen.coroutine
+    def post(self):
+        def body_to_cal_args():
+            cal_args = []
+            if self.input_params is None:
+                return cal_args
+
+            input_dict = tornado.escape.json_decode(self.request.body)
+            if len(input_dict) != len(self.input_params):
+                raise ValueError("Got %s parameters, expected %s" %
+                                 (len(input_dict), len(self.input_params)))
+
+            for input_param in self.input_params:
+                key = input_param.key
+                value = input_dict[key]
+                proto_type = input_param.proto_type
+
+                if proto_type is not None:
+                    proto_cls = getattr(RwcalYang, proto_type)
+                    self.log.debug("Deserializing into %s type", proto_cls)
+                    value = proto_cls.from_dict(value)
+
+                cal_args.append(value)
+
+            return cal_args
+
+        def cal_return_vals(return_vals):
+            output_params = self.output_params
+            if output_params is None:
+                output_params = []
+
+            if len(return_vals) != len(output_params):
+                raise ValueError("Got %s return values.  Expected %s",
+                                 len(return_vals), len(output_params))
+
+            write_dict = {"return_vals": []}
+            for i, output_param in enumerate(output_params):
+                key = output_param.key
+                proto_type = output_param.proto_type
+                output_value = return_vals[i]
+
+                if proto_type is not None:
+                    output_value = output_value.as_dict()
+
+                return_val = {
+                        "key": key,
+                        "value": output_value,
+                        "proto_type": proto_type,
+                        }
+
+                write_dict["return_vals"].append(return_val)
+
+            return write_dict
+
+        @asyncio.coroutine
+        def handle_request():
+            self.log.debug("Got cloudsimproxy POST request: %s", self.request.body)
+            cal_args = body_to_cal_args()
+
+            # Execute the CAL request in a seperate thread to prevent
+            # blocking the main loop.
+            return_vals = yield from self.loop.run_in_executor(
+                    self.executor,
+                    self.wrap_status_fn,
+                    getattr(self.cal, self.cal_method),
+                    self.account,
+                    *cal_args
+                    )
+
+            return cal_return_vals(return_vals)
+
+        f = asyncio.ensure_future(handle_request(), loop=self.loop)
+        return_dict = yield tornado.platform.asyncio.to_tornado_future(f)
+
+        self.log.debug("Responding to %s RPC with %s", self.cal_method, return_dict)
+
+        self.clear()
+        self.set_status(200)
+        self.write(return_dict)
+
+
+class CalProxyApp(tornado.web.Application):
+    def __init__(self, log, loop, cal_interface, cal_account):
+        self.log = log
+        self.loop = loop
+        self.cal = cal_interface
+        self.account = cal_account
+
+        attrs = dict(
+            log=self.log,
+            loop=self.loop,
+            cal=cal_interface,
+            account=cal_account,
+            # Create an executor with a single worker to prevent
+            # having multiple simulteneous calls into CAL (which is not threadsafe)
+            executor=concurrent.futures.ThreadPoolExecutor(1)
+            )
+
+        def mk_attrs(cal_method, input_params=None, output_params=None):
+            new_attrs = {
+                    "cal_method": cal_method,
+                    "input_params": input_params,
+                    "output_params": output_params
+                    }
+            new_attrs.update(attrs)
+
+            return new_attrs
+
+        super(CalProxyApp, self).__init__([
+            (r"/api/get_image_list", CalRequestHandler,
+                mk_attrs(
+                    cal_method="get_image_list",
+                    output_params=[
+                        RPCParam("images", "VimResources"),
+                        ]
+                    ),
+                ),
+
+            (r"/api/create_image", CalRequestHandler,
+                mk_attrs(
+                    cal_method="create_image",
+                    input_params=[
+                        RPCParam("image", "ImageInfoItem"),
+                        ],
+                    output_params=[
+                        RPCParam("image_id"),
+                        ]
+                    ),
+                ),
+
+            (r"/api/delete_image", CalRequestHandler,
+                mk_attrs(
+                    cal_method="delete_image",
+                    input_params=[
+                        RPCParam("image_id"),
+                        ],
+                    ),
+                ),
+
+            (r"/api/get_image", CalRequestHandler,
+                mk_attrs(
+                    cal_method="get_image",
+                    input_params=[
+                        RPCParam("image_id"),
+                        ],
+                    output_params=[
+                        RPCParam("image", "ImageInfoItem"),
+                        ],
+                    ),
+                ),
+
+            (r"/api/create_vm", CalRequestHandler,
+                mk_attrs(
+                    cal_method="create_vm",
+                    input_params=[
+                        RPCParam("vm", "VMInfoItem"),
+                        ],
+                    output_params=[
+                        RPCParam("vm_id"),
+                        ],
+                    ),
+                ),
+
+            (r"/api/start_vm", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="start_vm",
+                        input_params=[
+                            RPCParam("vm_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/stop_vm", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="stop_vm",
+                        input_params=[
+                            RPCParam("vm_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/delete_vm", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="delete_vm",
+                        input_params=[
+                            RPCParam("vm_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/reboot_vm", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="reboot_vm",
+                        input_params=[
+                            RPCParam("vm_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_vm_list", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_vm_list",
+                        output_params=[
+                            RPCParam("vms", "VimResources"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_vm", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_vm",
+                        input_params=[
+                            RPCParam("vm_id"),
+                            ],
+                        output_params=[
+                            RPCParam("vms", "VMInfoItem"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/create_flavor", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="create_flavor",
+                        input_params=[
+                            RPCParam("flavor", "FlavorInfoItem"),
+                            ],
+                        output_params=[
+                            RPCParam("flavor_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/delete_flavor", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="delete_flavor",
+                        input_params=[
+                            RPCParam("flavor_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_flavor_list", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_flavor_list",
+                        output_params=[
+                            RPCParam("flavors", "VimResources"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_flavor", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_flavor",
+                        input_params=[
+                            RPCParam("flavor_id"),
+                            ],
+                        output_params=[
+                            RPCParam("flavor", "FlavorInfoItem"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/create_network", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="create_network",
+                        input_params=[
+                            RPCParam("network", "NetworkInfoItem"),
+                            ],
+                        output_params=[
+                            RPCParam("network_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/delete_network", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="delete_network",
+                        input_params=[
+                            RPCParam("network_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_network", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_network",
+                        input_params=[
+                            RPCParam("network_id"),
+                            ],
+                        output_params=[
+                            RPCParam("network", "NetworkInfoItem"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_network_list", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_network_list",
+                        output_params=[
+                            RPCParam("networks", "VimResources"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_management_network", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_management_network",
+                        output_params=[
+                            RPCParam("network", "NetworkInfoItem"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/create_port", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="create_port",
+                        input_params=[
+                            RPCParam("port", "PortInfoItem"),
+                            ],
+                        output_params=[
+                            RPCParam("port_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/delete_port", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="delete_port",
+                        input_params=[
+                            RPCParam("port_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_port", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_port",
+                        input_params=[
+                            RPCParam("port_id"),
+                            ],
+                        output_params=[
+                            RPCParam("port", "PortInfoItem"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_port_list", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_port_list",
+                        output_params=[
+                            RPCParam("ports", "VimResources"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/create_virtual_link", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="create_virtual_link",
+                        input_params=[
+                            RPCParam("link_params", "VirtualLinkReqParams"),
+                            ],
+                        output_params=[
+                            RPCParam("link_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/delete_virtual_link", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="delete_virtual_link",
+                        input_params=[
+                            RPCParam("link_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_virtual_link", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_virtual_link",
+                        input_params=[
+                            RPCParam("link_id"),
+                            ],
+                        output_params=[
+                            RPCParam("response", "VirtualLinkInfoParams"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_virtual_link_list", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_virtual_link_list",
+                        output_params=[
+                            RPCParam("resources", "VNFResources"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/create_vdu", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="create_vdu",
+                        input_params=[
+                            RPCParam("vdu_params", "VDUInitParams"),
+                            ],
+                        output_params=[
+                            RPCParam("vdu_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/modify_vdu", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="modify_vdu",
+                        input_params=[
+                            RPCParam("vdu_params", "VDUModifyParams"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/delete_vdu", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="delete_vdu",
+                        input_params=[
+                            RPCParam("vdu_id"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_vdu", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_vdu",
+                        input_params=[
+                            RPCParam("vdu_id"),
+                            ],
+                        output_params=[
+                            RPCParam("response", "VDUInfoParams"),
+                            ],
+                        ),
+                    ),
+
+            (r"/api/get_vdu_list", CalRequestHandler,
+                    mk_attrs(
+                        cal_method="get_vdu_list",
+                        output_params=[
+                            RPCParam("resources", "VNFResources"),
+                            ],
+                        ),
+                    )
+            ])
diff --git a/rwcal/rift/cal/server/operations.py b/rwcal/rift/cal/server/operations.py
new file mode 100644
index 0000000..316525e
--- /dev/null
+++ b/rwcal/rift/cal/server/operations.py
@@ -0,0 +1,200 @@
+"""
+# 
+#   Copyright 2016 RIFT.IO Inc
+#
+#   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.
+#
+
+@file operations.py
+@author Varun Prasad(varun.prasad@riftio.com)
+@date 2016-06-14
+"""
+
+import daemon
+import daemon.pidfile
+import os
+import signal
+import subprocess
+import sys
+import time
+
+import gi
+gi.require_version('RwcalYang', '1.0')
+gi.require_version('RwCal', '1.0')
+gi.require_version('RwLog', '1.0')
+
+from . import server as cal_server
+import rift.cal.utils as cal_util
+import rift.rwcal.cloudsim.shell as shell
+
+
+
+class CloudsimServerOperations(cal_util.CloudSimCalMixin):
+    """Convenience class to provide start, stop and cleanup operations
+    
+    Attributes:
+        log (logging): Log instance
+        PID_FILE (str): Location to generate the PID file.
+    """
+    PID_FILE = "/var/log/rift/cloudsim_server.pid"
+
+    def __init__(self, log):
+        super().__init__()
+        self.log = log
+
+    @property
+    def pid(self):
+        pid = None
+        try:
+            with open(self.PID_FILE) as fh:
+                pid = fh.readlines()[0]
+                pid = int(pid.strip())
+        except IndexError:
+            self.log.error("Looks like the pid file does not contain a valid ID")
+        except OSError:
+            self.log.debug("No PID file found.")
+
+        return pid
+
+    def is_pid_exists(self, pid):
+        try:
+            os.kill(pid, 0)
+        except OSError:
+            return False
+
+        return True
+
+    def start_server(self, foreground=False):
+        """Start the tornado app """
+
+        # Before starting verify if all requirements are satisfied
+        cal_server.CalServer.verify_requirements(self.log)
+
+        # If the /var/log directory is not present, then create it first.
+        if not os.path.exists(os.path.dirname(self.PID_FILE)):
+            self.log.warning("Creating /var/log/rift directory for log storage")
+            os.makedirs(os.path.dirname(self.PID_FILE))
+
+        # Check if an exiting PID file is present, if so check if it has an
+        # associated proc, otherwise it's a zombie file so clean it.
+        # Otherwise the daemon fails silently.
+        if self.pid is not None and not self.is_pid_exists(self.pid):
+            self.log.warning("Removing stale PID file")
+            os.remove(self.PID_FILE)
+
+
+
+        def start(daemon_mode=False):
+
+            log = cal_util.Logger(daemon_mode=daemon_mode, log_name='')
+            log.logger.info("Starting the cloud server.")
+            server = cal_server.CalServer()
+            server.start()
+
+        if foreground:
+            # Write the PID file for consistency
+            with open(self.PID_FILE, mode='w') as fh:
+                fh.write(str(os.getpid()) + "\n")
+            start()
+        else:
+            context = daemon.DaemonContext(
+                pidfile=daemon.pidfile.PIDLockFile(self.PID_FILE))
+            with context:
+                start(daemon_mode=True)
+
+    def stop_server(self):
+        """Stop the daemon"""
+
+        def kill_pid(pid, sig):
+            self.log.info("Sending {} to PID: {}".format(str(sig), pid))
+            os.kill(pid, sig)
+
+
+        def search_and_kill():
+            """In case the PID file is not found, and the server is still
+            running, as a last resort we search thro' the process table
+            and stop the server."""
+            cmd = ["pgrep", "-u", "daemon,root", "python3"]
+
+            try:
+               pids = subprocess.check_output(cmd)
+            except subprocess.CalledProcessError:
+                self.log.error("No Cloudsim server process found. "
+                        "Please ensure Cloudsim server is running")
+                return
+
+            pids = map(int, pids.split())
+
+            for pid in pids:
+                if pid != os.getpid():
+                    kill_sequence(pid)
+
+        def wait_till_exit(pid, timeout=30, retry_interval=1):
+            start_time = time.time()
+
+            while True:
+                if not self.is_pid_exists(pid):
+                    msg = "Killed {}".format(pid)
+                    print (msg)
+                    return True
+
+                time_elapsed = time.time() - start_time
+                time_remaining = timeout - time_elapsed
+
+                self.log.info("Process still exists, trying again in {} sec(s)"
+                    .format(retry_interval))
+
+                if time_remaining <= 0:
+                    msg = 'Process {} has not yet terminated within {} secs. Trying SIGKILL'
+                    self.log.error(msg.format(pid, timeout))
+                    return False
+
+                time.sleep(min(time_remaining, retry_interval))
+
+        def kill_sequence(pid):
+            kill_pid(pid, signal.SIGHUP)
+            wait_till_exit(pid, timeout=10, retry_interval=2)
+            kill_pid(pid, signal.SIGKILL)
+            status = wait_till_exit(pid)
+
+            if status:
+                # Remove the lock file.
+                shell.command("rm -f {}".format(self.PID_FILE))
+
+        pid = self.pid
+        if pid is not None:
+            self.log.warning("Server running with PID: {} found, "
+                             "trying to stop it".format(pid))
+            kill_sequence(pid)
+        else:
+            self.log.warning("No PID file found. Searching the process "
+                            "table to find PID")
+            search_and_kill()
+
+    def clean_server(self, images=False):
+        """Clean all resource using rest APIs. """
+
+        # Delete VDUs
+        _, vdus = self.cal.get_vdu_list(self.account)
+        for vdu in vdus.vdu_info_list:
+            self.cal.delete_vdu(self.account, vdu.vdu_id)
+
+        # Delete Vlinks
+        _, vlinks = self.cal.get_virtual_link_list(self.account)
+        for vlink in vlinks.virtual_link_info_list:
+            self.cal.delete_virtual_link(self.account, vlink.virtual_link_id)
+
+        if images:
+            _, images = self.cal.get_image_list(self.account)
+            for image in images.image_info_list:
+                self.cal.delete_image(self.account, image.id)
diff --git a/rwcal/rift/cal/server/server.py b/rwcal/rift/cal/server/server.py
new file mode 100644
index 0000000..ef8b0d4
--- /dev/null
+++ b/rwcal/rift/cal/server/server.py
@@ -0,0 +1,151 @@
+"""
+# 
+#   Copyright 2016 RIFT.IO Inc
+#
+#   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.
+#
+
+@file cal_server.py
+@author Austin Cormier(austin.cormier@riftio.com)
+@author Varun Prasad(varun.prasad@riftio.com)
+@date 2016-06-14
+"""
+
+import asyncio
+import logging
+import os
+import signal
+import sys
+
+import tornado
+import tornado.httpserver
+import tornado.web
+import tornado.platform.asyncio
+
+import gi
+gi.require_version('RwcalYang', '1.0')
+gi.require_version('RwCal', '1.0')
+gi.require_version('RwLog', '1.0')
+gi.require_version('RwTypes', '1.0')
+from gi.repository import (
+    RwcalYang,
+    RwLog
+)
+
+import rw_peas
+import rift.tasklets
+import rift.rwcal.cloudsim.net
+import rift.rwcal.cloudsim.lvm as lvm
+import rift.rwcal.cloudsim.lxc as lxc
+import rift.rwcal.cloudsim.shell as shell
+
+from . import app
+
+logger = logging.getLogger(__name__)
+
+if sys.version_info < (3, 4, 4):
+    asyncio.ensure_future = asyncio.async
+
+
+class CalServer():
+    HTTP_PORT = 9002
+    cal_interface = None
+
+    @staticmethod
+    def verify_requirements(log):
+        """
+        Check if all the requirements are met
+        1. bridgeutils should be installed
+        2. The user should be root
+        """
+        try:
+            shell.command('/usr/sbin/brctl show')
+        except shell.ProcessError:
+            log.exception('/usr/sbin/brctl command not found, please install '
+                'bridge-utils (yum install bridge-utils)')
+            sys.exit(1)
+
+        if os.geteuid() != 0:
+            log.error("User should be root to start the server.")
+            sys.exit(1)
+
+    def __init__(self, logging_level=logging.DEBUG):
+        self.app = None
+        self.server = None
+        self.log_hdl = RwLog.Ctx.new("a")
+        self.log = logger
+        self.log.setLevel(logging_level)
+
+    def get_cal_interface(self):
+        self.log.debug("Creating CAL interface.")
+        if CalServer.cal_interface is None:
+            plugin = rw_peas.PeasPlugin('rwcal_cloudsim', 'RwCal-1.0')
+            engine, info, extension = plugin()
+
+            CalServer.cal_interface = plugin.get_interface("Cloud")
+            CalServer.cal_interface.init(self.log_hdl)
+
+        return CalServer.cal_interface
+
+    def cleanup(self):
+        self.log.info("Cleaning up resources and backing store.")
+        for container in lxc.containers():
+            self.log.debug("Stopping {}".format(container))
+            lxc.stop(container)
+
+        for container in lxc.containers():
+            lxc.destroy(container)
+
+        lvm.destroy('rift')
+
+
+    def start(self):
+        """Start the server."""
+
+        cal = self.get_cal_interface()
+        account = RwcalYang.CloudAccount(account_type="cloudsim")
+
+        tornado.platform.asyncio.AsyncIOMainLoop().install()
+        loop = asyncio.get_event_loop()
+
+        self.app = app.CalProxyApp(self.log, loop, cal, account)
+        self.server = tornado.httpserver.HTTPServer(self.app)
+
+        self.log.info("Starting Cal Proxy Http Server on port %s",
+                      CalServer.HTTP_PORT)
+        self.server.listen(CalServer.HTTP_PORT)
+
+        def startup():
+            self.log.info("Creating a default network")
+            rift.rwcal.cloudsim.net.virsh_initialize_default()
+            self.log.info("Creating backing store")
+            lvm.create('rift')
+
+        loop.add_signal_handler(signal.SIGHUP, self.cleanup)
+        loop.add_signal_handler(signal.SIGTERM, self.cleanup)
+
+        try:
+            loop.run_in_executor(None, startup)
+            loop.run_forever()
+        except KeyboardInterrupt:
+            self.cleanup()
+        except Exception as exc:
+            self.log.exception(exc)
+
+
+    def stop(self):
+      try:
+         self.server.stop()
+      except Exception:
+         self.log.exception("Caught Exception in LP stop:", sys.exc_info()[0])
+         raise
diff --git a/rwcal/rift/cal/utils.py b/rwcal/rift/cal/utils.py
new file mode 100644
index 0000000..c99bf9d
--- /dev/null
+++ b/rwcal/rift/cal/utils.py
@@ -0,0 +1,123 @@
+"""
+# 
+#   Copyright 2016 RIFT.IO Inc
+#
+#   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.
+#
+
+@file utils.py
+@author Varun Prasad(varun.prasad@riftio.com)
+@date 2016-06-14
+"""
+
+import logging
+import os
+import sys
+
+import gi
+gi.require_version('RwcalYang', '1.0')
+gi.require_version('RwLog', '1.0')
+
+from gi.repository import RwcalYang
+import rift.rwcal.cloudsim.net as net
+import rwlogger
+import rw_peas
+
+
+class Logger():
+    """A wrapper to hold all logging related configuration. """
+    LOG_FILE = "/var/log/rift/cloudsim_server.log"
+    FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+
+    def __init__(self, daemon_mode=True, log_name=__name__, log_level=logging.DEBUG):
+        """
+        Args:
+            daemon_mode (bool, optional): If set, then logs are pushed to the
+                    file.
+            log_name (str, optional): Logger name
+            log_level (<Log level>, optional): INFO, DEBUG ..
+        """
+        self.logger = logging.getLogger(log_name)
+        logging.basicConfig(level=log_level, format=self.FORMAT)
+
+        if daemon_mode:
+            handler = logging.FileHandler(self.LOG_FILE)
+            handler.setFormatter(logging.Formatter(self.FORMAT))
+            self.logger.addHandler(handler)
+
+
+
+class CloudSimCalMixin(object):
+    """Mixin class to provide cal plugin and account access to classes.
+    """
+
+    def __init__(self):
+        self._cal, self._account = None, None
+
+    @property
+    def cal(self):
+        if not self._cal:
+            self.load_plugin()
+        return self._cal
+
+    @property
+    def account(self):
+        if not self._account:
+            self.load_plugin()
+        return self._account
+
+    def load_plugin(self):
+        """Load the cal plugin and account
+
+        Returns:
+            Tuple (Cal, Account)
+        """
+        plugin = rw_peas.PeasPlugin('rwcal_cloudsimproxy', 'RwCal-1.0')
+        engine, info, extension = plugin()
+
+        rwloggerctx = rwlogger.RwLog.Ctx.new("Cal-Log")
+        cal = plugin.get_interface("Cloud")
+        rc = cal.init(rwloggerctx)
+
+        account = RwcalYang.CloudAccount()
+        account.account_type = "cloudsim_proxy"
+        account.cloudsim_proxy.host = "192.168.122.1"
+
+        self._cal, self._account = cal, account
+
+
+def check_and_create_bridge(func):
+    """Decorator that checks if a bridge is available in the VM, if not checks
+    for permission and tries to create one.
+    """
+
+    def func_wrapper(*args, **kwargs):
+        logging.debug("Checking if bridge exists")
+
+        if net.bridge_exists('virbr0'):
+            logging.debug("Bridge exists, can proceed with further operations.")
+        else:
+            logging.warning("No Bridge exists, trying to create one.")
+
+            if os.geteuid() != 0:
+                logging.error("No bridge exists and cannot create one due to "
+                    "insufficient privileges. Please create it manually using "
+                    "'virsh net-start default' or re-run the same command as root.")
+                sys.exit(1)
+
+            net.virsh_initialize_default()
+
+        return func(*args, **kwargs)
+
+    return func_wrapper
+