X-Git-Url: https://gerrit.akraino.org/r/gitweb?a=blobdiff_plain;f=cmframework%2Fsrc%2Fcmframework%2Futils%2Fcmtopologicalsort.py;fp=cmframework%2Fsrc%2Fcmframework%2Futils%2Fcmtopologicalsort.py;h=5923ab5373ccdf8292d1dab992571bea0d05297a;hb=c389bdee7b3845b55f443dbf04c0ce4083a55886;hp=0000000000000000000000000000000000000000;hpb=5030f0c004701dd422c78c71c014ef60f48139fc;p=ta%2Fconfig-manager.git diff --git a/cmframework/src/cmframework/utils/cmtopologicalsort.py b/cmframework/src/cmframework/utils/cmtopologicalsort.py new file mode 100644 index 0000000..5923ab5 --- /dev/null +++ b/cmframework/src/cmframework/utils/cmtopologicalsort.py @@ -0,0 +1,142 @@ +# Copyright 2019 Nokia + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. +from __future__ import print_function +from cmframework.apis import cmerror + + +class TopSort(object): + """ + This class implements a topological sort algorithm. + + It expects a dependency graph as an input and returns a sorted list as an + output. The returned list contains lists sorted accordingly to the dependency + graph: + + Usage Example: + # The following graph indicates that 2 depends on 11, 9 depends on 11,8,10 and so on... + graph = {2: [11], + 9: [11, 8, 10], + 10: [11, 3], + 11: [7, 5], + 8: [7, 3]} + + sort = TopSort(graph) + try: + sorted = sort.sort() + for entry in sorted: + print('%r' % entry) + except cmerror.CMError as exp: + print('Got exception %s' % str(exp)) + + The above example will generate the following output: + [3, 5, 7] + [8, 11] + [2, 10] + [9] + """ + + def __init__(self, graph): + self.graph = graph + self.output = {} + self.recursionlevel = 0 + + def _get_dependent_entries(self, entry): + result = [] + for e, deps in self.graph.iteritems(): + if entry in deps: + result.append(e) + return result + + def _update_dependent_entries(self, entry): + maximumrecursion = (len(self.graph)) * (len(self.graph)) + if (self.recursionlevel + 1) >= maximumrecursion: + raise cmerror.CMError('cyclic dependency detected, graph %r' % self.graph) + self.recursionlevel += 1 + dependententries = self._get_dependent_entries(entry) + entrydepth = self.output[entry] + for e in dependententries: + if e in self.output: + if entrydepth >= self.output[e]: + self.output[e] = entrydepth + 1 + self._update_dependent_entries(e) + + def sort(self): + for entry, deps in self.graph.iteritems(): + depth = 0 + if entry in self.output: + depth = self.output[entry] + + # calculate new depth according to dependencies + newdepth = depth + for dep in deps: + if dep in self.output: + weight = self.output[dep] + else: + weight = 0 + self.output[dep] = 0 + if weight >= newdepth: + newdepth = weight + 1 + + # if our depth is changed we need to update the entries depending on us + self.output[entry] = newdepth + if newdepth > depth and entry in self.output: + self.recursionlevel = 0 + self._update_dependent_entries(entry) + + return self._getsorted() + + def _getsorted(self): + import operator + sortedoutput = sorted(self.output.items(), key=operator.itemgetter(1)) + result = {} + for entry in sortedoutput: + if entry[1] not in result: + result[entry[1]] = [] + result[entry[1]].append(entry[0]) + + returned = [] + for entry, data in result.iteritems(): + returned.append(data) + + return returned + + +def main(): + graph1 = {2: [11], + 9: [11, 8, 10], + 10: [11, 3], + 11: [7, 5], + 8: [7, 3]} + + graph2 = {'playbook2.yaml': [], 'playbook1.yaml': ['playbook2.yaml']} + + topsort1 = TopSort(graph1) + topsort2 = TopSort(graph2) + + try: + print(graph1) + sortedlists1 = topsort1.sort() + for entry in sortedlists1: + print('%r' % entry) + + print(graph2) + sortedlists2 = topsort2.sort() + for entry in sortedlists2: + print('%r' % entry) + except cmerror.CMError as exp: + print(str(exp)) + + +if __name__ == '__main__': + main()