if dc_label is None or dc_label == 'None':
# return list with all compute nodes in all DCs
all_containers = []
+ all_extSAPs = []
for dc in dcs.itervalues():
all_containers += dc.listCompute()
- return [(c.name, c.getStatus()) for c in all_containers], 200, CORS_HEADER
+ all_extSAPs += dc.listExtSAPs()
+
+ extSAP_list = [(sap.name, sap.getStatus()) for sap in all_extSAPs]
+ container_list = [(c.name, c.getStatus()) for c in all_containers]
+ total_list = container_list + extSAP_list
+ return total_list, 200, CORS_HEADER
else:
# return list of compute nodes for specified DC
- return [(c.name, c.getStatus())
- for c in dcs.get(dc_label).listCompute()], 200, CORS_HEADER
+ container_list = [(c.name, c.getStatus()) for c in dcs.get(dc_label).listCompute()]
+ extSAP_list = [(sap.name, sap.getStatus()) for sap in dcs.get(dc_label).listExtSAPs()]
+ total_list = container_list + extSAP_list
+ return total_list, 200, CORS_HEADER
except Exception as ex:
logging.exception("API error.")
return ex.message, 500, CORS_HEADER
}
.interface_name {
- width: 100px;
+ width: 150px;
}
.interface_ip {
// clear table
$("#table_container").empty();
// header
- $("#table_container").append('<tr class="tbl-head"><td>Datacenter</td><td>Container</td><td>Image</td><td>docker0</td><td>Networking [datacenter port | interface | ip]</td></tr>');
+ $("#table_container").append('<tr class="tbl-head"><td>Datacenter</td><td>Container</td><td>Image</td><td>docker0</td><td>Networking [datacenter port | interface | ip]</td></tr>');
// fill table
$.each(data, function(i, item) {
var row_str = "";
--- /dev/null
+var API_HOST = "http://127.0.0.1:5001";
+var ERROR_ALERT = false;
+var TIMESTAMP = 0;
+var CONNECTED = false;
+var LATENESS_UPDATE_INTERVAL = 50;
+var DATA_UPDATE_INTERVAL = 1000 * 10;
+var LAST_UPDATE_TIMESTAMP_CONTAINER = 0;
+var LAST_UPDATE_TIMESTAMP_DATACENTER = 0;
+
+
+function update_lateness_loop() {
+ lateness_datacenter= (Date.now() - LAST_UPDATE_TIMESTAMP_DATACENTER) / 1000;
+ $("#lbl_lateness_datacenter").text("Lateness: " + Number(lateness_datacenter).toPrecision(3) + "s");
+ lateness_container= (Date.now() - LAST_UPDATE_TIMESTAMP_CONTAINER) / 1000;
+ $("#lbl_lateness_container").text("Lateness: " + Number(lateness_container).toPrecision(3) + "s");
+ // loop while connected
+ if(CONNECTED)
+ setTimeout(update_lateness_loop, LATENESS_UPDATE_INTERVAL)
+}
+
+
+function errorAjaxConnection()
+{
+ // only do once
+ if(!ERROR_ALERT)
+ {
+ ERROR_ALERT = true;
+ // show message
+ alert("ERROR!\nAPI request failed.\n\n Please check the backend connection.", function() {
+ // callback
+ ERROR_ALERT = false;
+ });
+ }
+}
+
+
+function update_table_datacenter(data)
+{
+ console.debug(data)
+ // clear table
+ $("#table_datacenter").empty();
+ // header
+ $("#table_datacenter").append('<tr class="tbl-head"><td>Label</td><td>Int. Name</td><td>Switch</td><td>Num. Containers</td><td>VNFs</td></tr>');
+ // fill table
+ $.each(data, function(i, item) {
+ var row_str = "";
+ row_str += '<tr class="tbl-row clickable_row" id="datacenter_row_' + i +'">';
+ row_str += '<td>' + item.label + '1</td>';
+ row_str += '<td>' + item.internalname + '</td>';
+ row_str += '<td>' + item.switch + '</td>';
+ row_str += '<td><span class="badge">' + item.n_running_containers + '</span></td>';
+ //row_str += '<td><span class="badge">' + Object.keys(item.metadata).length + '</span></td>';
+ row_str += '<td>' + item.vnf_list + '</span></td>';
+ row_str += '<tr>';
+ $("#table_datacenter").append(row_str);
+ });
+ $("#lbl_datacenter_count").text(data.length);
+ // update lateness counter
+ LAST_UPDATE_TIMESTAMP_DATACENTER = Date.now();
+}
+
+
+function update_table_container(data)
+{
+ console.debug(data)
+ // clear table
+ $("#table_container").empty();
+ // header
+ $("#table_container").append('<tr class="tbl-head"><td>Datacenter</td><td>Container</td><td>Image</td><td>docker0</td><td>Status</td></tr>');
+ // fill table
+ $.each(data, function(i, item) {
+ var row_str = "";
+ row_str += '<tr class="tbl-row clickable_row" id="container_row_' + i +'">';
+ row_str += '<td>' + item[1].datacenter + '</td>';
+ row_str += '<td>' + item[0] + '</td>';
+ row_str += '<td>' + item[1].image + '</td>';
+ row_str += '<td><code>' + item[1].docker_network + '<code></td>';
+ if(item[1].state.Status == "running")
+ row_str += '<td><span class="label label-success">running</span></td>';
+ else
+ row_str += '<td><span class="label label-danger">stopped</span></td>';
+ row_str += '<tr>';
+ $("#table_container").append(row_str);
+ });
+ $("#lbl_container_count").text(data.length);
+ // update lateness counter
+ LAST_UPDATE_TIMESTAMP_CONTAINER = Date.now();
+}
+
+
+function fetch_datacenter()
+{
+ // do HTTP request and trigger gui update on success
+ var request_url = API_HOST + "/restapi/datacenter";
+ console.debug("fetching from: " + request_url);
+ $.getJSON(request_url, update_table_datacenter);
+}
+
+
+function fetch_container()
+{
+ // do HTTP request and trigger gui update on success
+ var request_url = API_HOST + "/restapi/compute";
+ console.debug("fetching from: " + request_url);
+ $.getJSON(request_url, update_table_container);
+}
+
+
+function fetch_d3graph()
+{
+ // do HTTP request and trigger gui update on success
+ var request_url = API_HOST + "/restapi/network/d3jsgraph";
+ console.debug("fetching from: " + request_url);
+ //$.getJSON(request_url, update_graph);
+}
+
+function fetch_loop()
+{
+ // only fetch if we are connected
+ if(!CONNECTED)
+ return;
+
+ // download data
+ fetch_datacenter();
+ fetch_container();
+
+ // loop while connected
+ if(CONNECTED)
+ setTimeout(fetch_loop, DATA_UPDATE_INTERVAL);
+}
+
+
+function connect()
+{
+ console.info("connect()");
+ // get host address
+ API_HOST = "http://" + $("#text_api_host").val();
+ console.debug("API address: " + API_HOST);
+ // reset data
+ LAST_UPDATE_TIMESTAMP_DATACENTER = Date.now();
+ LAST_UPDATE_TIMESTAMP_CONTAINER = Date.now();
+ CONNECTED = true;
+ // restart lateness counter
+ update_lateness_loop();
+ // restart data fetch loop
+ fetch_loop();
+ // gui updates
+ $("#btn_disconnect").removeClass("disabled");
+ $("#btn_connect").addClass("disabled");
+}
+
+function disconnect()
+{
+ console.info("disconnect()");
+ CONNECTED = false;
+ // gui updates
+ $("#btn_connect").removeClass("disabled");
+ $("#btn_disconnect").addClass("disabled");
+}
+
+
+$(document).ready(function(){
+ console.info("document ready");
+ // setup global connection error handling
+ /*
+ $.ajaxSetup({
+ "error": errorAjaxConnection
+ });
+
+ // add listeners
+ $("#btn_connect").click(connect);
+ $("#btn_disconnect").click(disconnect);
+ */
+ setTimeout(fetch_datacenter, 1000);//fetch_datacenter();
+ setTimeout(fetch_container, 2000);//fetch_container();
+
+
+ // additional refresh on window focus
+ $(window).focus(function () {
+ if(CONNECTED)
+ {
+ fetch_datacenter();
+ fetch_container();
+ }
+ });
+
+});
from mininet.clean import cleanup
import networkx as nx
from emuvim.dcemulator.monitoring import DCNetworkMonitor
-from emuvim.dcemulator.node import Datacenter, EmulatorCompute
+from emuvim.dcemulator.node import Datacenter, EmulatorCompute, EmulatorExtSAP
from emuvim.dcemulator.resourcemodel import ResourceModelRegistrar
LOG = logging.getLogger("dcemulator.net")
# make sure that 'type' is set
params['type'] = params.get('type','sap_ext')
self.DCNetwork_graph.add_node(sap_name, type=params['type'])
- LOG.info('add ext sap: {0}'.format(sap_name))
return Containernet.addExtSAP(self, sap_name, sap_ip, **params)
def removeExtSAP(self, sap_name, **params):
dict.update({match[0]:m2})
return dict
- def find_connected_dc_interface(self, vnf_src_name, vnf_src_interface):
+ def find_connected_dc_interface(self, vnf_src_name, vnf_src_interface=None):
+
+ if vnf_src_interface is None:
+ # take first interface by default
+ connected_sw = self.DCNetwork_graph.neighbors(vnf_src_name)[0]
+ link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw]
+ vnf_src_interface = link_dict[0]['src_port_id']
+
for connected_sw in self.DCNetwork_graph.neighbors(vnf_src_name):
link_dict = self.DCNetwork_graph[vnf_src_name][connected_sw]
for link in link_dict:
vnf_interface = str(i)
dc_port_name = self.datacenter.net.find_connected_dc_interface(vnf_name, vnf_interface)
# format list of tuples (name, Ip, MAC, isUp, status, dc_portname)
- intf_dict = {'intf_name': str(i), 'ip': i.IP(), 'netmask': i.prefixLen, 'mac': i.MAC(), 'up': i.isUp(), 'status': i.status(), 'dc_portname': dc_port_name}
+ intf_dict = {'intf_name': str(i), 'ip': "{0}/{1}".format(i.IP(), i.prefixLen), 'netmask': i.prefixLen, 'mac': i.MAC(), 'up': i.isUp(), 'status': i.status(), 'dc_portname': dc_port_name}
networkStatusList.append(intf_dict)
return networkStatusList
return status
+class EmulatorExtSAP(object):
+ """
+ Emulator specific class that defines an external service access point (SAP) for the service.
+ Inherits from Containernet's OVSBridge class.
+ Represents a single OVS switch connected to a (logical)
+ data center.
+ We can add emulator specific helper functions to it.
+ """
+
+ def __init__(self, sap_name, sap_net, datacenter, **kwargs):
+
+ self.datacenter = datacenter # pointer to current DC
+ self.net = self.datacenter.net
+ self.name = sap_name
+
+ LOG.debug("Starting ext SAP instance %r in data center %r" % (sap_name, str(self.datacenter)))
+
+ # create SAP as separate OVS switch with an assigned ip address
+ self.ip = str(sap_net[1]) + '/' + str(sap_net.prefixlen)
+ self.subnet = sap_net
+ # allow connection to the external internet through the host
+ params = dict(NAT=True)
+ self.switch = self.net.addExtSAP(sap_name, self.ip, dpid=hex(self._get_next_extSAP_dpid())[2:], **params)
+ self.switch.start()
+
+ def _get_next_extSAP_dpid(self):
+ global EXTSAPDPID_BASE
+ EXTSAPDPID_BASE += 1
+ return EXTSAPDPID_BASE
+
+ def getNetworkStatus(self):
+ """
+ Helper method to receive information about the virtual networks
+ this compute instance is connected to.
+ """
+ # get all links and find dc switch interface
+ networkStatusList = []
+ for i in self.switch.intfList():
+ vnf_name = self.name
+ vnf_interface = str(i)
+ if vnf_interface == 'lo':
+ continue
+ dc_port_name = self.datacenter.net.find_connected_dc_interface(vnf_name, vnf_interface)
+ # format list of tuples (name, Ip, MAC, isUp, status, dc_portname)
+ intf_dict = {'intf_name': str(i), 'ip': self.ip, 'netmask': i.prefixLen, 'mac': i.MAC(), 'up': i.isUp(), 'status': i.status(), 'dc_portname': dc_port_name}
+ networkStatusList.append(intf_dict)
+
+ return networkStatusList
+
+ def getStatus(self):
+ return {
+ "name": self.switch.name,
+ "datacenter": self.datacenter.name,
+ "network": self.getNetworkStatus()
+ }
+
class Datacenter(object):
"""
Represents a logical data center to which compute resources
self.switch = None
# keep track of running containers
self.containers = {}
+ # keep track of attached external access points
+ self.extSAPs = {}
# pointer to assigned resource model
self._resource_model = None
DCDPID_BASE += 1
return DCDPID_BASE
- def _get_next_extSAP_dpid(self):
- global EXTSAPDPID_BASE
- EXTSAPDPID_BASE += 1
- return EXTSAPDPID_BASE
-
def create(self):
"""
Each data center is represented by a single switch to which
return True
def attachExternalSAP(self, sap_name, sap_net, **params):
- # create SAP as separate OVS switch with an assigned ip address
- sap_ip = str(sap_net[1]) + '/' + str(sap_net.prefixlen)
- # allow connection to the external internet through the host
- params = dict(NAT=True)
- sap_switch = self.net.addExtSAP(sap_name, sap_ip, dpid=hex(self._get_next_extSAP_dpid())[2:], **params)
- sap_switch.start()
-
+ extSAP = EmulatorExtSAP(sap_name, sap_net, self, **params)
# link SAP to the DC switch
- self.net.addLink(sap_switch, self.switch, cls=Link)
+ self.net.addLink(extSAP.switch, self.switch, cls=Link)
+ self.extSAPs[sap_name] = extSAP
def removeExternalSAP(self, sap_name):
- sap_switch = self.net.getNodeByName(sap_name)
- # link SAP to the DC switch
+ sap_switch = self.extSAPs[sap_name].switch
+ #sap_switch = self.net.getNodeByName(sap_name)
+ # remove link of SAP to the DC switch
self.net.removeLink(link=None, node1=sap_switch, node2=self.switch)
self.net.removeExtSAP(sap_name)
+ del self.extSAPs[sap_name]
def listCompute(self):
"""
"""
return list(self.containers.itervalues())
+ def listExtSAPs(self):
+ """
+ Return a list of all external SAPs assigned to this
+ data center.
+ """
+ return list(self.extSAPs.itervalues())
+
def getStatus(self):
"""
Return a dict with status information about this DC.
"""
container_list = [name for name in self.containers]
+ ext_saplist = [sap_name for sap_name in self.extSAPs]
return {
"label": self.label,
"internalname": self.name,
"switch": self.switch.name,
"n_running_containers": len(self.containers),
"metadata": self.metadata,
- "vnf_list" : container_list
+ "vnf_list" : container_list,
+ "ext SAP list" : ext_saplist
}
def assignResourceModel(self, rm):