Implemented Openstack Neutron SFC API

Signed-off-by: Malte Splietker <malte.splietker@gmail.com>
diff --git a/src/emuvim/api/openstack/resources/__init__.py b/src/emuvim/api/openstack/resources/__init__.py
index ffddf54..fca6c6c 100755
--- a/src/emuvim/api/openstack/resources/__init__.py
+++ b/src/emuvim/api/openstack/resources/__init__.py
@@ -2,6 +2,10 @@
 from model import Model
 from net import Net
 from port import Port
+from port_pair import PortPair
+from port_pair_group import PortPairGroup
+from flow_classifier import FlowClassifier
+from port_chain import PortChain
 from resource import Resource
 from router import Router
 from server import Server
diff --git a/src/emuvim/api/openstack/resources/flow_classifier.py b/src/emuvim/api/openstack/resources/flow_classifier.py
new file mode 100644
index 0000000..dff6638
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/flow_classifier.py
@@ -0,0 +1,52 @@
+import uuid
+
+
+class FlowClassifier(object):
+    def __init__(self, name):
+        self.id = str(uuid.uuid4())
+        self.tenant_id = "abcdefghijklmnopqrstuvwxyz123456"
+        self.name = name
+        self.description = ""
+        self.ethertype = "IPv4"
+        self.protocol = None
+        self.source_port_range_min = 0
+        self.source_port_range_max = 0
+        self.destination_port_range_min = 0
+        self.destination_port_range_max = 0
+        self.source_ip_prefix = None
+        self.destination_ip_prefix = None
+        self.logical_source_port = ""
+        self.logical_destination_port = ""
+        self.l7_parameters = dict()
+
+    def create_dict(self, compute):
+        representation = {
+            "name": self.name,
+            "tenant_id": self.tenant_id,
+            "description": self.description,
+            "id": self.id,
+        }
+        if self.ethertype:
+            representation["ethertype"] = self.ethertype
+        if self.protocol:
+            representation["protocol"] = self.protocol
+        if self.source_port_range_min:
+            representation["source_port_range_min"] = self.source_port_range_min
+        if self.source_port_range_max:
+            representation["source_port_range_max"] = self.source_port_range_max
+        if self.destination_port_range_min:
+            representation["destination_port_range_min"] = self.destination_port_range_min
+        if self.destination_port_range_max:
+            representation["destination_port_range_max"] = self.destination_port_range_max
+        if self.source_ip_prefix:
+            representation["source_ip_prefix"] = self.source_ip_prefix
+        if self.destination_ip_prefix:
+            representation["destination_ip_prefix"] = self.destination_ip_prefix
+        if len(self.logical_source_port):
+            representation["logical_source_port"] = self.logical_source_port
+        if len(self.logical_destination_port):
+            representation["logical_destination_port"] = self.logical_destination_port
+        if len(self.l7_parameters.items()):
+            representation["l7_parameters"] = self.l7_parameters
+
+        return representation
diff --git a/src/emuvim/api/openstack/resources/port_chain.py b/src/emuvim/api/openstack/resources/port_chain.py
new file mode 100644
index 0000000..2e91159
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/port_chain.py
@@ -0,0 +1,71 @@
+import random
+import uuid
+import logging
+
+
+class PortChain(object):
+    def __init__(self, name):
+        self.id = str(uuid.uuid4())
+        self.tenant_id = "abcdefghijklmnopqrstuvwxyz123456"
+        self.name = name
+        self.description = ""
+        self.port_pair_groups = list()
+        self.flow_classifiers = list()
+        self.chain_parameters = dict()
+
+        # Cookie for internal identification of installed flows (e.g. to delete them)
+        self.cookie = random.randint(1, 0xffffffff)
+
+    def create_dict(self, compute):
+        representation = {
+            "name": self.name,
+            "tenant_id": self.tenant_id,
+            "description": self.description,
+            "flow_classifiers": self.flow_classifiers,
+            "port_pair_groups": self.port_pair_groups,
+            "id": self.id
+        }
+        return representation
+
+    def install(self, compute):
+        for flow_classifier_id in self.flow_classifiers:
+            flow_classifier = compute.find_flow_classifier_by_name_or_id(flow_classifier_id)
+            if flow_classifier:
+                pass
+                # TODO: for every flow classifier create match and pass it to setChain
+
+        for group_id in self.port_pair_groups:
+            port_pair_group = compute.find_port_pair_group_by_name_or_id(group_id)
+            for port_pair_id in port_pair_group.port_pairs:
+                port_pair = compute.find_port_pair_by_name_or_id(port_pair_id)
+
+                server_ingress = None
+                server_egress = None
+                for server in compute.computeUnits.values():
+                    if port_pair.ingress.name in server.port_names:
+                        server_ingress = server
+                    elif port_pair.egress.name in server.port_names:
+                        server_egress = server
+
+                # TODO: Not sure, if this should throw an error
+                if not server_ingress:
+                    logging.warn("Neutron SFC: ingress port %s not connected." % str(port_pair.ingress.name))
+                    continue
+                if not server_egress:
+                    logging.warn("Neutron SFC: egress port %s not connected." % str(port_pair.egress.name))
+                    continue
+
+                compute.dc.net.setChain(
+                    server_ingress.name, server_egress.name,
+                    port_pair.ingress.intf_name, port_pair.egress.intf_name,
+                    cmd="add-flow", cookie=self.cookie, priority=10, bidirectional=False,
+                    monitor=False
+                )
+
+    def uninstall(self, compute):
+        # TODO: implement
+        logging.warn("Removing flows is currently not implemented.")
+
+    def update(self):
+        # TODO: implement
+        logging.warn("Updating flows is currently not implemented.")
diff --git a/src/emuvim/api/openstack/resources/port_pair.py b/src/emuvim/api/openstack/resources/port_pair.py
new file mode 100644
index 0000000..38b983e
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/port_pair.py
@@ -0,0 +1,23 @@
+import uuid
+
+
+class PortPair(object):
+    def __init__(self, name):
+        self.id = str(uuid.uuid4())
+        self.tenant_id = "abcdefghijklmnopqrstuvwxyz123456"
+        self.name = name
+        self.description = ""
+        self.ingress = None
+        self.egress = None
+        self.service_function_parameters = dict()
+
+    def create_dict(self, compute):
+        representation = {
+            "name": self.name,
+            "tenant_id": self.tenant_id,
+            "description": self.description,
+            "ingress": self.ingress.id,
+            "egress": self.egress.id,
+            "id": self.id
+        }
+        return representation
diff --git a/src/emuvim/api/openstack/resources/port_pair_group.py b/src/emuvim/api/openstack/resources/port_pair_group.py
new file mode 100644
index 0000000..9514689
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/port_pair_group.py
@@ -0,0 +1,22 @@
+import uuid
+
+
+class PortPairGroup(object):
+    def __init__(self, name):
+        self.id = str(uuid.uuid4())
+        self.tenant_id = "abcdefghijklmnopqrstuvwxyz123456"
+        self.name = name
+        self.description = ""
+        self.port_pairs = list()
+        self.port_pair_group_parameters = dict()
+
+    def create_dict(self, compute):
+        representation = {
+            "name": self.name,
+            "tenant_id": self.tenant_id,
+            "description": self.description,
+            "port_pairs": self.port_pairs,
+            "port_pair_group_parameters": self.port_pair_group_parameters,
+            "id": self.id
+        }
+        return representation
diff --git a/src/emuvim/api/openstack/resources/server.py b/src/emuvim/api/openstack/resources/server.py
index d20a2d4..654521a 100755
--- a/src/emuvim/api/openstack/resources/server.py
+++ b/src/emuvim/api/openstack/resources/server.py
@@ -7,6 +7,7 @@
         self.image = image
         self.command = command
         self.port_names = list()
+        self.properties = dict()
         self.flavor = flavor
         self.son_emu_command = None
         self.emulator_compute = None