Fixed missing license headers
[osm/vim-emu.git] / src / emuvim / dcemulator / node.py
index b88913b..77a71a0 100755 (executable)
@@ -14,7 +14,7 @@ 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.
 
-Neither the name of the SONATA-NFV [, ANY ADDITIONAL AFFILIATION]
+Neither the name of the SONATA-NFV, Paderborn University
 nor the names of its contributors may be used to endorse or promote
 products derived from this software without specific prior written
 permission.
@@ -25,19 +25,18 @@ the Horizon 2020 and 5G-PPP programmes. The authors would like to
 acknowledge the contributions of their colleagues of the SONATA
 partner consortium (www.sonata-nfv.eu).
 """
-from mininet.node import Docker
+from mininet.node import Docker, OVSBridge
 from mininet.link import Link
 from emuvim.dcemulator.resourcemodel import NotEnoughResourcesAvailable
 import logging
-import time
-import json
+
 
 LOG = logging.getLogger("dcemulator.node")
 LOG.setLevel(logging.DEBUG)
 
 
 DCDPID_BASE = 1000  # start of switch dpid's used for data center switches
-
+EXTSAPDPID_BASE = 2000  # start of switch dpid's used for external SAP switches
 
 class EmulatorCompute(Docker):
     """
@@ -61,9 +60,17 @@ class EmulatorCompute(Docker):
         Helper method to receive information about the virtual networks
         this compute instance is connected to.
         """
-        # format list of tuples (name, Ip, MAC, isUp, status)
-        return [{'intf_name':str(i), 'ip':i.IP(), 'mac':i.MAC(), 'up':i.isUp(), 'status':i.status()}
-                for i in self.intfList()]
+        # get all links and find dc switch interface
+        networkStatusList = []
+        for i in self.intfList():
+            vnf_name = self.name
+            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': "{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
 
     def getStatus(self):
         """
@@ -75,19 +82,78 @@ class EmulatorCompute(Docker):
         status["docker_network"] = self.dcinfo['NetworkSettings']['IPAddress']
         status["image"] = self.dimage
         status["flavor_name"] = self.flavor_name
-        status["cpu_quota"] = self.cpu_quota
-        status["cpu_period"] = self.cpu_period
-        status["cpu_shares"] = self.cpu_shares
-        status["cpuset"] = self.cpuset
-        status["mem_limit"] = self.mem_limit
-        status["memswap_limit"] = self.memswap_limit
+        status["cpu_quota"] = self.resources.get('cpu_quota')
+        status["cpu_period"] = self.resources.get('cpu_period')
+        status["cpu_shares"] = self.resources.get('cpu_shares')
+        status["cpuset"] = self.resources.get('cpuset_cpus')
+        status["mem_limit"] = self.resources.get('mem_limit')
+        status["memswap_limit"] = self.resources.get('memswap_limit')
         status["state"] = self.dcli.inspect_container(self.dc)["State"]
         status["id"] = self.dcli.inspect_container(self.dc)["Id"]
+        status["short_id"] = self.dcli.inspect_container(self.dc)["Id"][:12]
+        status["hostname"] = self.dcli.inspect_container(self.dc)["Config"]['Hostname']
         status["datacenter"] = (None if self.datacenter is None
                                 else self.datacenter.label)
+
         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
@@ -105,7 +171,7 @@ class Datacenter(object):
         self.name = "dc%d" % Datacenter.DC_COUNTER
         Datacenter.DC_COUNTER += 1
         # use this for user defined names that can be longer than self.name
-        self.label = label  
+        self.label = label
         # dict to store arbitrary metadata (e.g. latitude and longitude)
         self.metadata = metadata
         # path to which resource information should be logged (e.g. for experiments). None = no logging
@@ -114,6 +180,8 @@ class Datacenter(object):
         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
 
@@ -140,7 +208,7 @@ class Datacenter(object):
     def start(self):
         pass
 
-    def startCompute(self, name, image=None, command=None, network=None, flavor_name="tiny"):
+    def startCompute(self, name, image=None, command=None, network=None, flavor_name="tiny", properties=dict(), **params):
         """
         Create a new container as compute resource and connect it to this
         data center.
@@ -149,6 +217,7 @@ class Datacenter(object):
         :param command: command (string)
         :param network: networks list({"ip": "10.0.0.254/8"}, {"ip": "11.0.0.254/24"})
         :param flavor_name: name of the flavor for this compute container
+        :param properties: dictionary of properties (key-value) that will be passed as environment variables
         :return:
         """
         assert name is not None
@@ -166,15 +235,27 @@ class Datacenter(object):
             if len(network) < 1:
                 network.append({})
 
+        # apply hard-set resource limits=0
+        cpu_percentage = params.get('cpu_percent')
+        if cpu_percentage:
+            params['cpu_period'] = self.net.cpu_period
+            params['cpu_quota'] = self.net.cpu_period * float(cpu_percentage)
+
+        env = properties
+        properties['VNF_NAME'] = name
         # create the container
         d = self.net.addDocker(
             "%s" % (name),
             dimage=image,
             dcmd=command,
             datacenter=self,
-            flavor_name=flavor_name
+            flavor_name=flavor_name,
+            environment = env,
+            **params
         )
 
+
+
         # apply resource limits to container if a resource model is defined
         if self._resource_model is not None:
             try:
@@ -198,6 +279,7 @@ class Datacenter(object):
             self.net.addLink(d, self.switch, params1=nw, cls=Link, intfName1=nw.get('id'))
         # do bookkeeping
         self.containers[name] = d
+
         return d  # we might use UUIDs for naming later on
 
     def stopCompute(self, name):
@@ -228,6 +310,20 @@ class Datacenter(object):
 
         return True
 
+    def attachExternalSAP(self, sap_name, sap_net, **params):
+        extSAP = EmulatorExtSAP(sap_name, sap_net, self, **params)
+        # link SAP to the DC switch
+        self.net.addLink(extSAP.switch, self.switch, cls=Link)
+        self.extSAPs[sap_name] = extSAP
+
+    def removeExternalSAP(self, sap_name):
+        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 a list of all running containers assigned to this
@@ -235,16 +331,27 @@ class Datacenter(object):
         """
         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
+            "metadata": self.metadata,
+            "vnf_list" : container_list,
+            "ext SAP list" : ext_saplist
         }
 
     def assignResourceModel(self, rm):