Starting point

Change-Id: I86dab2674688f6d818ce2fb0861ea893d6841df3
diff --git a/src/tosca/parser.py b/src/tosca/parser.py
new file mode 100644
index 0000000..7eb3381
--- /dev/null
+++ b/src/tosca/parser.py
@@ -0,0 +1,176 @@
+from toscaparser.tosca_template import ToscaTemplate
+from default import TOSCA_RECIPES_DIR
+from resources import RESOURCES
+from grpc._channel import _Rendezvous
+
+class TOSCA_Parser:
+
+    def compute_dependencies(self):
+        nodetemplates_by_name = {}
+        for nodetemplate in self.template.nodetemplates:
+            nodetemplates_by_name[nodetemplate.name] = nodetemplate
+
+        self.nodetemplates_by_name = nodetemplates_by_name
+
+        for nodetemplate in self.template.nodetemplates:
+            nodetemplate.dependencies = []
+            nodetemplate.dependencies_names = []
+            for reqs in nodetemplate.requirements:
+                for (k,v) in reqs.items():
+                    name = v["node"]
+                    if (name in nodetemplates_by_name):
+                        nodetemplate.dependencies.append(nodetemplates_by_name[name])
+                        nodetemplate.dependencies_names.append(name)
+
+                    # go another level deep, as our requirements can have requirements...
+                    for sd_req in v.get("requirements",[]):
+                        for (sd_req_k, sd_req_v) in sd_req.items():
+                            name = sd_req_v["node"]
+                            if (name in nodetemplates_by_name):
+                                nodetemplate.dependencies.append(nodetemplates_by_name[name])
+                                nodetemplate.dependencies_names.append(name)
+
+    def topsort_dependencies(self):
+        # stolen from observer
+        g = self.nodetemplates_by_name
+
+        # Get set of all nodes, including those without outgoing edges
+        keys = set(g.keys())
+        values = set({})
+        for v in g.values():
+            values = values | set(v.dependencies_names)
+
+        all_nodes = list(keys | values)
+        steps = all_nodes
+
+
+        # Final order
+        order = []
+
+        # DFS stack, not using recursion
+        stack = []
+
+        # Unmarked set
+        unmarked = all_nodes
+
+        # visiting = [] - skip, don't expect 1000s of nodes, |E|/|V| is small
+
+        while unmarked:
+            stack.insert(0, unmarked[0])  # push first unmarked
+
+            while (stack):
+                n = stack[0]
+                add = True
+                try:
+                    for m in g[n].dependencies_names:
+                        if (m in unmarked):
+                            add = False
+                            stack.insert(0, m)
+                except KeyError:
+                    pass
+                if (add):
+                    if (n in steps and n not in order):
+                        order.append(n)
+                    item = stack.pop(0)
+                    try:
+                        unmarked.remove(item)
+                    except ValueError:
+                        pass
+
+        noorder = list(set(steps) - set(order))
+        return order + noorder
+
+    def execute(self):
+        for nodetemplate in self.ordered_nodetemplates:
+            self.execute_nodetemplate(nodetemplate)
+
+    def execute_nodetemplate(self, nodetemplate):
+        node_class = nodetemplate.type.replace("tosca.nodes.", "")
+        if node_class not in RESOURCES:
+            raise Exception("Nodetemplate %s's type %s is not a known resource" % (nodetemplate.name, node_class))
+
+        # find the class corresponding to a node
+        cls = RESOURCES[node_class]
+
+
+        # read properties from TOSCA
+        data = nodetemplate.templates[nodetemplate.name]['properties']
+
+        TOSCA_Parser.creat_or_update(cls, data)
+
+    @staticmethod
+    def populate_model(model, data):
+        for k,v in data.iteritems():
+            setattr(model, k, v)
+        return model
+
+    @staticmethod
+    def creat_or_update(cls, data):
+
+        # default case
+        if data.get('name'):
+            used_key = 'name'
+        else:
+            used_key = data.keys()[0]
+
+        models = cls.objects.filter(**{used_key: data[used_key]})
+
+        if len(models) == 1:
+            print "[XOS-Tosca] Model %s already exist, updating..." % data[used_key]
+            model = models[0]
+        elif len(models) == 0:
+            model = cls.objects.new()
+            print "[XOS-Tosca] Model %s is new, creating..." % data[used_key]
+        else:
+            raise Exception("[XOS-Tosca] Model %s has multiple instances, I can't handle it" % data[used_key])
+
+        model = TOSCA_Parser.populate_model(model, data)
+        model.save()
+
+
+    @staticmethod
+    def _translate_exception(msg):
+        readable = []
+        for line in msg.splitlines():
+            print line
+            if line.strip().startswith('MissingRequiredFieldError'):
+                readable.append(line)
+
+        if len(readable) > 0:
+            return '/n'.join(readable)
+        else:
+            return msg
+
+    def __init__(self, recipe):
+
+        self.template = None
+        self.nodetemplates_by_name = None
+        self.ordered_nodetemplates = []
+        self.ordered_names = None
+
+        tmp_file_path = TOSCA_RECIPES_DIR + '/tmp.yaml'
+
+        # write the receive recipe in a tmp file
+        tmp_file = open(tmp_file_path, 'w')
+        tmp_file.write(recipe)
+        tmp_file.close()
+
+        try:
+            self.template = ToscaTemplate(tmp_file_path)
+            self.compute_dependencies()
+            self.ordered_names = self.topsort_dependencies()
+            for name in self.ordered_names:
+                if name in self.nodetemplates_by_name:
+                    self.ordered_nodetemplates.append(self.nodetemplates_by_name[name])
+
+            self.execute()
+
+        except Exception as e:
+            print e
+            import sys, os
+            exc_type, exc_obj, exc_tb = sys.exc_info()
+            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
+            print(exc_type, fname, exc_tb.tb_lineno)
+            raise Exception(self._translate_exception(e.message))
+
+