01701a5aada32053fd8410230c7308643d9c96f3
[osm/LCM.git] / osm_lcm / lcm_utils.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2018 Telefonica S.A.
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License"); you may
7 # not use this file except in compliance with the License. You may obtain
8 # a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15 # License for the specific language governing permissions and limitations
16 # under the License.
17 ##
18
19 from collections import OrderedDict
20 # from osm_common.dbbase import DbException
21
22 __author__ = "Alfonso Tierno"
23
24
25 class LcmException(Exception):
26 pass
27
28
29 class LcmExceptionNoMgmtIP(LcmException):
30 pass
31
32
33 class LcmExceptionExit(LcmException):
34 pass
35
36
37 def versiontuple(v):
38 """utility for compare dot separate versions. Fills with zeros to proper number comparison
39 package version will be something like 4.0.1.post11+gb3f024d.dirty-1. Where 4.0.1 is the git tag, postXX is the
40 number of commits from this tag, and +XXXXXXX is the git commit short id. Total length is 16 with until 999 commits
41 """
42 filled = []
43 for point in v.split("."):
44 filled.append(point.zfill(16))
45 return tuple(filled)
46
47
48 class TaskRegistry:
49 """
50 Implements a registry of task needed for later cancelation, look for related tasks that must be completed before
51 etc. It stores a four level dict
52 First level is the topic, ns, vim_account, sdn
53 Second level is the _id
54 Third level is the operation id
55 Fourth level is a descriptive name, the value is the task class
56 """
57
58 def __init__(self):
59 self.task_registry = {
60 "ns": {},
61 "nsi": {},
62 "vim_account": {},
63 "wim_account": {},
64 "sdn": {},
65 }
66
67 def register(self, topic, _id, op_id, task_name, task):
68 """
69 Register a new task
70 :param topic: Can be "ns", "nsi", "vim_account", "sdn"
71 :param _id: _id of the related item
72 :param op_id: id of the operation of the related item
73 :param task_name: Task descriptive name, as create, instantiate, terminate. Must be unique in this op_id
74 :param task: Task class
75 :return: none
76 """
77 if _id not in self.task_registry[topic]:
78 self.task_registry[topic][_id] = OrderedDict()
79 if op_id not in self.task_registry[topic][_id]:
80 self.task_registry[topic][_id][op_id] = {task_name: task}
81 else:
82 self.task_registry[topic][_id][op_id][task_name] = task
83 # print("registering task", topic, _id, op_id, task_name, task)
84
85 def remove(self, topic, _id, op_id, task_name=None):
86 """
87 When task is ended, it should be removed. It ignores missing tasks. It also removes tasks done with this _id
88 :param topic: Can be "ns", "nsi", "vim_account", "sdn"
89 :param _id: _id of the related item
90 :param op_id: id of the operation of the related item
91 :param task_name: Task descriptive name. If none it deletes all tasks with same _id and op_id
92 :return: None
93 """
94 if not self.task_registry[topic].get(_id):
95 return
96 if not task_name:
97 self.task_registry[topic][_id].pop(op_id, None)
98 elif self.task_registry[topic][_id].get(op_id):
99 self.task_registry[topic][_id][op_id].pop(task_name, None)
100
101 # delete done tasks
102 for op_id_ in list(self.task_registry[topic][_id]):
103 for name, task in self.task_registry[topic][_id][op_id_].items():
104 if not task.done():
105 break
106 else:
107 del self.task_registry[topic][_id][op_id_]
108 if not self.task_registry[topic][_id]:
109 del self.task_registry[topic][_id]
110
111 def lookfor_related(self, topic, _id, my_op_id=None):
112 task_list = []
113 task_name_list = []
114 if _id not in self.task_registry[topic]:
115 return "", task_name_list
116 for op_id in reversed(self.task_registry[topic][_id]):
117 if my_op_id:
118 if my_op_id == op_id:
119 my_op_id = None # so that the next task is taken
120 continue
121
122 for task_name, task in self.task_registry[topic][_id][op_id].items():
123 if not task.done():
124 task_list.append(task)
125 task_name_list.append(task_name)
126 break
127 return ", ".join(task_name_list), task_list
128
129 def cancel(self, topic, _id, target_op_id=None, target_task_name=None):
130 """
131 Cancel all active tasks of a concrete ns, nsi, vim_account, sdn identified for _id. If op_id is supplied only
132 this is cancelled, and the same with task_name
133 """
134 if not self.task_registry[topic].get(_id):
135 return
136 for op_id in reversed(self.task_registry[topic][_id]):
137 if target_op_id and target_op_id != op_id:
138 continue
139 for task_name, task in self.task_registry[topic][_id][op_id].items():
140 if target_task_name and target_task_name != task_name:
141 continue
142 # result =
143 task.cancel()
144 # if result:
145 # self.logger.debug("{} _id={} order_id={} task={} cancelled".format(topic, _id, op_id, task_name))
146
147
148 class LcmBase:
149
150 def __init__(self, db, msg, fs, logger):
151 """
152
153 :param db: database connection
154 """
155 self.db = db
156 self.msg = msg
157 self.fs = fs
158 self.logger = logger
159
160 def update_db_2(self, item, _id, _desc):
161 """
162 Updates database with _desc information. If success _desc is cleared
163 :param item:
164 :param _id:
165 :param _desc: dictionary with the content to update. Keys are dot separated keys for
166 :return: None. Exception is raised on error
167 """
168 if not _desc:
169 return
170 self.db.set_one(item, {"_id": _id}, _desc)
171 _desc.clear()
172 # except DbException as e:
173 # self.logger.error("Updating {} _id={} with '{}'. Error: {}".format(item, _id, _desc, e))