--- /dev/null
+import logging
+import threading
+from flask import Flask, request
+from flask_restful import Resource,Api
+import json
+
+
+
+logging.basicConfig(level=logging.INFO)
+
+
+dcs = {}
+
+class RestApiEndpoint(object):
+
+ """
+ Simple API endpoint that offers a REST
+ interface. This interface will be used by the
+ default command line client.
+ """
+ global dcs
+
+ def __init__(self, listenip, port):
+ self.ip = listenip
+ self.port = port
+
+ # setup Flask
+ self.app = Flask(__name__)
+ self.api = Api(self.app)
+
+ # setup endpoints
+ self.api.add_resource(ComputeList, "/restapi/compute/<dc_label>")
+ self.api.add_resource(ComputeStart, "/restapi/compute/<dc_label>/<compute_name>/start")
+ self.api.add_resource(ComputeStop, "/restapi/compute/<dc_label>/<compute_name>/stop")
+ self.api.add_resource(ComputeStatus, "/restapi/compute/<dc_label>/<compute_name>")
+ self.api.add_resource(DatacenterList, "/restapi/datacenter")
+ self.api.add_resource(DatacenterStatus, "/restapi/datacenter/<dc_label>")
+
+ logging.debug("Created API endpoint %s(%s:%d)" % (self.__class__.__name__, self.ip, self.port))
+
+
+ def connectDatacenter(self, dc):
+ dcs[dc.label] = dc
+ logging.info("Connected DC(%s) to API endpoint %s(%s:%d)" % (dc.label, self.__class__.__name__, self.ip, self.port))
+
+ def start(self):
+ thread = threading.Thread(target= self._start_flask, args=())
+ thread.daemon = True
+ thread.start()
+ logging.info("Started API endpoint @ http://%s:%d" % (self.ip, self.port))
+
+
+ def _start_flask(self):
+ self.app.run(self.ip, self.port, debug=True, use_reloader=False)
+
+
+class ComputeStart(Resource):
+ """
+ Start a new compute instance: A docker container (note: zerorpc does not support keyword arguments)
+ :param dc_label: name of the DC
+ :param compute_name: compute container name
+ :param image: image name
+ :param command: command to execute
+ :param network: list of all interface of the vnf, with their parameters (id=id1,ip=x.x.x.x/x),...
+ :return: networks list({"id":"input","ip": "10.0.0.254/8"}, {"id":"output","ip": "11.0.0.254/24"})
+ """
+ global dcs
+
+ def put(self, dc_label, compute_name):
+ logging.debug("API CALL: compute start")
+ try:
+
+ image = json.loads(request.json).get("image")
+ network = json.loads(request.json).get("network")
+ command = json.loads(request.json).get("docker_command")
+ c = dcs.get(dc_label).startCompute(
+ compute_name, image= image, command= command, network= network)
+ # return docker inspect dict
+ return c. getStatus(), 200
+ except Exception as ex:
+ logging.exception("API error.")
+ return ex.message, 500
+
+class ComputeStop(Resource):
+
+ global dcs
+
+ def get(self, dc_label, compute_name):
+ logging.debug("API CALL: compute stop")
+ try:
+ return dcs.get(dc_label).stopCompute(compute_name), 200
+ except Exception as ex:
+ logging.exception("API error.")
+ return ex.message,500
+
+
+class ComputeList(Resource):
+
+ global dcs
+
+ def get(self, dc_label):
+ logging.debug("API CALL: compute list")
+ try:
+ if dc_label == 'None':
+ # return list with all compute nodes in all DCs
+ all_containers = []
+ for dc in dcs.itervalues():
+ all_containers += dc.listCompute()
+ return [(c.name, c.getStatus()) for c in all_containers], 200
+ else:
+ # return list of compute nodes for specified DC
+ return [(c.name, c.getStatus())
+ for c in dcs.get(dc_label).listCompute()], 200
+ except Exception as ex:
+ logging.exception("API error.")
+ return ex.message, 500
+
+
+class ComputeStatus(Resource):
+
+ global dcs
+
+ def get(self, dc_label, compute_name):
+
+ logging.debug("API CALL: compute list")
+
+ try:
+ return dcs.get(dc_label).containers.get(compute_name).getStatus(), 200
+ except Exception as ex:
+ logging.exception("API error.")
+ return ex.message, 500
+
+class DatacenterList(Resource):
+
+ global dcs
+
+ def get(self):
+ logging.debug("API CALL: datacenter list")
+ try:
+ return [d.getStatus() for d in dcs.itervalues()], 200
+ except Exception as ex:
+ logging.exception("API error.")
+ return ex.message, 500
+
+class DatacenterStatus(Resource):
+
+ global dcs
+
+ def get(self, dc_label):
+ logging.debug("API CALL: datacenter status")
+ try:
+ return dcs.get(dc_label).getStatus(), 200
+ except Exception as ex:
+ logging.exception("API error.")
+ return ex.message, 500
--- /dev/null
+from requests import get,put
+from tabulate import tabulate
+import pprint
+import argparse
+import json
+
+pp = pprint.PrettyPrinter(indent=4)
+
+class RestApiClient():
+
+ def __init__(self):
+ self.cmds = {}
+
+ def execute_command(self, args):
+ if getattr(self, args["command"]) is not None:
+ # call the local method with the same name as the command arg
+ getattr(self, args["command"])(args)
+ else:
+ print("Command not implemented.")
+
+ def start(self, args):
+
+ nw_list = list()
+ if args.get("network") is not None:
+ nw_list = self._parse_network(args.get("network"))
+ req = {'image':args.get("image"),
+ 'command':args.get("docker_command"),
+ 'network':nw_list}
+
+ responce = put("%s/restapi/compute/%s/%s/start" %
+ (args.get("endpoint"),
+ args.get("datacenter"),
+ args.get("name")),
+ json = json.dumps(req))
+ pp.pprint(responce.json())
+ def stop(self, args):
+
+ responce = get("%s/restapi/compute/%s/%s/stop" %
+ (args.get("endpoint"),
+ args.get("datacenter"),
+ args.get("name")))
+ pp.pprint(responce.json())
+
+ def list(self,args):
+
+ list = get('%s/restapi/compute/%s' % (args.get("endpoint"),args.get('datacenter'))).json()
+
+ table = []
+ for c in list:
+ # for each container add a line to the output table
+ if len(c) > 1:
+ name = c[0]
+ status = c[1]
+ eth0ip = None
+ eth0status = "down"
+ if len(status.get("network")) > 0:
+ eth0ip = status.get("network")[0].get("ip")
+ eth0status = "up" if status.get(
+ "network")[0].get("up") else "down"
+ table.append([status.get("datacenter"),
+ name,
+ status.get("image"),
+ eth0ip,
+ eth0status,
+ status.get("state").get("Status")])
+
+ headers = ["Datacenter",
+ "Container",
+ "Image",
+ "eth0 IP",
+ "eth0 status",
+ "Status"]
+ print(tabulate(table, headers=headers, tablefmt="grid"))
+
+ def status(self,args):
+
+ list = get("%s/restapi/compute/%s/%s" %
+ (args.get("endpoint"),
+ args.get("datacenter"),
+ args.get("name"))).json()
+ pp.pprint(list)
+
+
+
+ def _parse_network(self, network_str):
+ '''
+ parse the options for all network interfaces of the vnf
+ :param network_str: (id=x,ip=x.x.x.x/x), ...
+ :return: list of dicts [{"id":x,"ip":"x.x.x.x/x"}, ...]
+ '''
+ nw_list = list()
+ networks = network_str[1:-1].split('),(')
+ for nw in networks:
+ nw_dict = dict(tuple(e.split('=')) for e in nw.split(','))
+ nw_list.append(nw_dict)
+
+ return nw_list
+
+
+parser = argparse.ArgumentParser(description='son-emu datacenter')
+parser.add_argument(
+ "command",
+ choices=['start', 'stop', 'list', 'status', 'profile'],
+ help="Action to be executed.")
+parser.add_argument(
+ "--datacenter", "-d", dest="datacenter",
+ help="Data center to which the command should be applied.")
+parser.add_argument(
+ "--name", "-n", dest="name",
+ help="Name of compute instance e.g. 'vnf1'.")
+parser.add_argument(
+ "--image","-i", dest="image",
+ help="Name of container image to be used e.g. 'ubuntu:trusty'")
+parser.add_argument(
+ "--dcmd", "-c", dest="docker_command",
+ help="Startup command of the container e.g. './start.sh'")
+parser.add_argument(
+ "--net", dest="network",
+ help="Network properties of a compute instance e.g. \
+ '(id=input,ip=10.0.10.3/24),(id=output,ip=10.0.10.4/24)' for multiple interfaces.")
+parser.add_argument(
+ "--input", "-in", dest="input",
+ help="input interface of the vnf to profile")
+parser.add_argument(
+ "--output", "-out", dest="output",
+ help="output interface of the vnf to profile")
+parser.add_argument(
+ "--endpoint", "-e", dest="endpoint",
+ default="http://127.0.0.1:5000",
+ help="UUID of the plugin to be manipulated.")
+
+def main(argv):
+ args = vars(parser.parse_args(argv))
+ c = RestApiClient()
+ c.execute_command(args)
\ No newline at end of file
--- /dev/null
+from requests import get
+from tabulate import tabulate
+import pprint
+import argparse
+
+pp = pprint.PrettyPrinter(indent=4)
+
+class RestApiClient():
+
+ def __init__(self):
+ self.cmds = {}
+
+ def execute_command(self, args):
+ if getattr(self, args["command"]) is not None:
+ # call the local method with the same name as the command arg
+ getattr(self, args["command"])(args)
+ else:
+ print("Command not implemented.")
+
+ def list(self,args):
+ list = get('%s/restapi/datacenter' % args.get('endpoint')).json()
+ table = []
+ for d in list:
+ # for each dc add a line to the output table
+ if len(d) > 0:
+ table.append([d.get("label"),
+ d.get("internalname"),
+ d.get("switch"),
+ d.get("n_running_containers"),
+ len(d.get("metadata"))])
+ headers = ["Label",
+ "Internal Name",
+ "Switch",
+ "# Containers",
+ "# Metadata Items"]
+ print (tabulate(table, headers=headers, tablefmt="grid"))
+
+ def status(self,args):
+ list = get('%s/restapi/datacenter/%s' % ( args.get("endpoint"), args.get("datacenter"))).json()
+ table = []
+ table.append([list.get('label'),
+ list.get('internalname'),
+ list.get('switch'),
+ list.get('n_running_containers'),
+ len(list.get('metadata'))])
+
+ headers = ["Label",
+ "Internal Name",
+ "Switch",
+ "# Containers",
+ "# Metadata Items"]
+
+ print (tabulate(table, headers=headers, tablefmt="grid"))
+
+
+parser = argparse.ArgumentParser(description='son-emu datacenter')
+parser.add_argument(
+ "command",
+ choices=['list', 'status'],
+ help="Action to be executed.")
+parser.add_argument(
+ "--datacenter", "-d", dest="datacenter",
+ help="Data center to which the command should be applied.")
+parser.add_argument(
+ "--endpoint", "-e", dest="endpoint",
+ default="http://127.0.0.1:5000",
+ help="UUID of the plugin to be manipulated.")
+
+
+def main(argv):
+ args = vars(parser.parse_args(argv))
+ c = RestApiClient()
+ c.execute_command(args)
+
--- /dev/null
+"""
+Helper module that implements helpers for test implementations.
+"""
+
+import unittest
+import os
+import subprocess
+import docker
+from emuvim.dcemulator.net import DCNetwork
+from emuvim.api.rest.compute import RestApiEndpoint
+from mininet.clean import cleanup
+from mininet.node import Controller
+
+class SimpleTestTopology(unittest.TestCase):
+ """
+ Helper class to do basic test setups.
+ s1 -- s2 -- s3 -- ... -- sN
+ """
+
+ def __init__(self, *args, **kwargs):
+ self.net = None
+ self.api = None
+ self.s = [] # list of switches
+ self.h = [] # list of hosts
+ self.d = [] # list of docker containers
+ self.dc = [] # list of data centers
+ self.docker_cli = None
+ super(SimpleTestTopology, self).__init__(*args, **kwargs)
+
+ def createNet(
+ self,
+ nswitches=0, ndatacenter=0, nhosts=0, ndockers=0,
+ autolinkswitches=False, controller=Controller, **kwargs):
+ """
+ Creates a Mininet instance and automatically adds some
+ nodes to it.
+
+ Attention, we should always use Mininet's default controller
+ for our tests. Only use other controllers if you want to test
+ specific controller functionality.
+ """
+ self.net = DCNetwork(controller=controller, **kwargs)
+ self.api = RestApiEndpoint("127.0.0.1",5000)
+ # add some switches
+ # start from s1 because ovs does not like to have dpid = 0
+ # and switch name-number is being used by mininet to set the dpid
+ for i in range(1, nswitches+1):
+ self.s.append(self.net.addSwitch('s%d' % i))
+ # if specified, chain all switches
+ if autolinkswitches:
+ for i in range(0, len(self.s) - 1):
+ self.net.addLink(self.s[i], self.s[i + 1])
+ # add some data centers
+ for i in range(0, ndatacenter):
+ self.dc.append(
+ self.net.addDatacenter(
+ 'datacenter%d' % i,
+ metadata={"unittest_dc": i}))
+ # connect data centers to the endpoint
+ for i in range(0, ndatacenter):
+ self.api.connectDatacenter(self.dc[i])
+ # add some hosts
+ for i in range(0, nhosts):
+ self.h.append(self.net.addHost('h%d' % i))
+ # add some dockers
+ for i in range(0, ndockers):
+ self.d.append(self.net.addDocker('d%d' % i, dimage="ubuntu:trusty"))
+
+ def startApi(self):
+ self.api.start()
+
+ def startNet(self):
+ self.net.start()
+
+ def stopNet(self):
+ self.net.stop()
+
+ def getDockerCli(self):
+ """
+ Helper to interact with local docker instance.
+ """
+ if self.docker_cli is None:
+ self.docker_cli = docker.Client(
+ base_url='unix://var/run/docker.sock')
+ return self.docker_cli
+
+ def getContainernetContainers(self):
+ """
+ List the containers managed by containernet
+ """
+ return self.getDockerCli().containers(filters={"label": "com.containernet"})
+
+ @staticmethod
+ def setUp():
+ pass
+
+ @staticmethod
+ def tearDown():
+ cleanup()
+ # make sure that all pending docker containers are killed
+ with open(os.devnull, 'w') as devnull:
+ subprocess.call(
+ "sudo docker rm -f $(sudo docker ps --filter 'label=com.containernet' -a -q)",
+ stdout=devnull,
+ stderr=devnull,
+ shell=True)
\ No newline at end of file
--- /dev/null
+"""
+Test suite to automatically test emulator REST API endpoints.
+"""
+
+import time
+import unittest
+from emuvim.test.api_base import SimpleTestTopology
+import subprocess
+
+
+class testRestApi( SimpleTestTopology ):
+ """
+ Tests to check the REST API endpoints of the emulator.
+ """
+
+ def testRestApi(self):
+
+ # create network
+ self.createNet(nswitches=0, ndatacenter=2, nhosts=2, ndockers=0)
+ # setup links
+ self.net.addLink(self.dc[0], self.h[0])
+ self.net.addLink(self.h[1], self.dc[1])
+ self.net.addLink(self.dc[0], self.dc[1])
+ # start api
+ self.startApi()
+ # start Mininet network
+ self.startNet()
+ print('compute start datacenter0, vnf1 ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ subprocess.call("son-emu-cli compute start -d datacenter0 -n vnf1", shell=True)
+ print('compute start datacenter0, vnf2 ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ subprocess.call("son-emu-cli compute start -d datacenter0 -n vnf2", shell=True)
+ print('compute start datacenter1, vnf3 ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ subprocess.call("son-emu-cli compute start -d datacenter1 -n vnf3", shell=True)
+ print('compute list ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ subprocess.call("son-emu-cli compute list", shell=True)
+ print('compute stop datacenter0, vnf2 ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ subprocess.call("son-emu-cli compute stop -d datacenter0 -n vnf2", shell=True)
+ print('compute list ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ subprocess.call("son-emu-cli compute list", shell=True)
+ print('compute status datacenter0, vnf1 ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ subprocess.call("son-emu-cli compute status -d datacenter0 -n vnf1", shell=True)
+ print('datacenter list ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ subprocess.call("son-emu-cli datacenter list", shell=True)
+ print('datacenter status datacenter0 ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
+ subprocess.call("son-emu-cli datacenter status -d datacenter0", shell=True)
+ self.stopNet()
+
+
+if __name__ == '__main__':
+ unittest.main()