Alessandro Pilotti пре 9 година
родитељ
комит
c040b9a29f

+ 5 - 1
coriolis/api/v1/views/migration_view.py

@@ -3,6 +3,8 @@
 
 import itertools
 
+from coriolis.api.v1.views import replica_tasks_execution_view
+
 
 def _format_migration(req, migration, keys=None):
     def transform(key, value):
@@ -14,7 +16,9 @@ def _format_migration(req, migration, keys=None):
         transform(k, v) for k, v in migration.items()))
 
     # Migrations have a single tasks execution
-    execution = migration_dict["executions"][0]
+    execution = replica_tasks_execution_view.format_replica_tasks_execution(
+        req, migration_dict["executions"][0])
+
     migration_dict["status"] = execution["status"]
     tasks = execution.get("tasks")
     if tasks:

+ 24 - 3
coriolis/api/v1/views/replica_tasks_execution_view.py

@@ -3,22 +3,43 @@
 
 import itertools
 
+from coriolis import constants
+from coriolis import utils
 
-def _format_replica_tasks_execution(req, execution, keys=None):
+
+def _sort_tasks(tasks):
+    non_error_only_tasks = [t for t in tasks if
+                            t["depends_on"] or not t["on_error"]]
+    # Include error only tasks only if executed
+    error_only_tasks = [t for t in tasks if t["status"] !=
+                        constants.TASK_STATUS_ON_ERROR_ONLY and
+                        t not in non_error_only_tasks]
+
+    sorted_tasks = utils.topological_graph_sorting(
+        non_error_only_tasks, sort_key="task_type")
+    sorted_tasks += utils.topological_graph_sorting(
+        error_only_tasks, sort_key="task_type")
+    return sorted_tasks
+
+
+def format_replica_tasks_execution(req, execution, keys=None):
     def transform(key, value):
         if keys and key not in keys:
             return
         yield (key, value)
 
+    if "tasks" in execution:
+        execution["tasks"] = _sort_tasks(execution["tasks"])
+
     return dict(itertools.chain.from_iterable(
         transform(k, v) for k, v in execution.items()))
 
 
 def single(req, execution):
-    return {"execution": _format_replica_tasks_execution(req, execution)}
+    return {"execution": format_replica_tasks_execution(req, execution)}
 
 
 def collection(req, executions):
-    formatted_executions = [_format_replica_tasks_execution(req, m)
+    formatted_executions = [format_replica_tasks_execution(req, m)
                             for m in executions]
     return {'executions': formatted_executions}

+ 36 - 0
coriolis/utils.py

@@ -278,3 +278,39 @@ def to_dict(obj, max_depth=10):
         return jsonutils.to_primitive(
             value, convert_instances, convert_datetime, level, max_depth)
     return jsonutils.loads(jsonutils.dumps(obj, default=_to_primitive))
+
+
+def topological_graph_sorting(items, id="id", depends_on="depends_on",
+                              sort_key=None):
+    """
+    Kahn's algorithm
+    """
+    if sort_key:
+        # Sort siblings
+        items = sorted(items, key=lambda t: t[sort_key], reverse=True)
+
+    a = []
+    for i in items:
+        a.append({"id": i[id],
+                  "depends_on": list(i[depends_on] or []),
+                  "item": i})
+
+    s = []
+    l = []
+    for n in a:
+        if not n["depends_on"]:
+            s.append(n)
+    while s:
+        n = s.pop()
+        l.append(n["item"])
+
+        for m in a:
+            if n["id"] in m["depends_on"]:
+                m["depends_on"].remove(n["id"])
+                if not m["depends_on"]:
+                    s.append(m)
+
+    if len(l) != len(a):
+        raise ValueError("The graph contains cycles")
+
+    return l