blob: 3dbaa3f8dd721018105f4c1f591e97c0251fbf8c [file] [log] [blame]
tiernob24258a2018-10-04 18:39:49 +02001# -*- coding: utf-8 -*-
2
tiernod125caf2018-11-22 16:05:54 +00003# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12# implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
garciadeblas6e88d9c2024-08-15 10:55:04 +020016from pyrage import x25519
yshah53cc9eb2024-07-05 13:06:31 +000017import logging
rshri2d386cb2024-07-05 14:35:51 +000018import random
19import string
tiernob24258a2018-10-04 18:39:49 +020020from uuid import uuid4
21from http import HTTPStatus
22from time import time
aticig2b5e1232022-08-10 17:30:12 +030023from osm_common.dbbase import deep_update_rfc7396, DbException
rshri2d386cb2024-07-05 14:35:51 +000024from osm_common.msgbase import MsgException
25from osm_common.fsbase import FsException
tierno23acf402019-08-28 13:36:34 +000026from osm_nbi.validation import validate_input, ValidationError, is_valid_uuid
tierno1c38f2f2020-03-24 11:51:39 +000027from yaml import safe_load, YAMLError
tiernob24258a2018-10-04 18:39:49 +020028
29__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
30
31
32class EngineException(Exception):
tiernob24258a2018-10-04 18:39:49 +020033 def __init__(self, message, http_code=HTTPStatus.BAD_REQUEST):
34 self.http_code = http_code
tierno23acf402019-08-28 13:36:34 +000035 super(Exception, self).__init__(message)
tiernob24258a2018-10-04 18:39:49 +020036
garciadeblasf2af4a12023-01-24 16:56:54 +010037
aticig2b5e1232022-08-10 17:30:12 +030038class NBIBadArgumentsException(Exception):
39 """
40 Bad argument values exception
41 """
42
43 def __init__(self, message: str = "", bad_args: list = None):
44 Exception.__init__(self, message)
45 self.message = message
46 self.bad_args = bad_args
47
48 def __str__(self):
garciadeblasf2af4a12023-01-24 16:56:54 +010049 return "{}, Bad arguments: {}".format(self.message, self.bad_args)
50
tiernob24258a2018-10-04 18:39:49 +020051
tierno714954e2019-11-29 13:43:26 +000052def deep_get(target_dict, key_list):
53 """
54 Get a value from target_dict entering in the nested keys. If keys does not exist, it returns None
55 Example target_dict={a: {b: 5}}; key_list=[a,b] returns 5; both key_list=[a,b,c] and key_list=[f,h] return None
56 :param target_dict: dictionary to be read
57 :param key_list: list of keys to read from target_dict
58 :return: The wanted value if exist, None otherwise
59 """
60 for key in key_list:
61 if not isinstance(target_dict, dict) or key not in target_dict:
62 return None
63 target_dict = target_dict[key]
64 return target_dict
65
66
garciadeblasf2af4a12023-01-24 16:56:54 +010067def detect_descriptor_usage(descriptor: dict, db_collection: str, db: object) -> bool:
aticig2b5e1232022-08-10 17:30:12 +030068 """Detect the descriptor usage state.
69
70 Args:
71 descriptor (dict): VNF or NS Descriptor as dictionary
72 db_collection (str): collection name which is looked for in DB
73 db (object): name of db object
74
75 Returns:
76 True if descriptor is in use else None
77
78 """
79 try:
80 if not descriptor:
81 raise NBIBadArgumentsException(
82 "Argument is mandatory and can not be empty", "descriptor"
83 )
84
85 if not db:
86 raise NBIBadArgumentsException("A valid DB object should be provided", "db")
87
88 search_dict = {
89 "vnfds": ("vnfrs", "vnfd-id"),
90 "nsds": ("nsrs", "nsd-id"),
kayal2001f71c2e82024-06-25 15:26:24 +053091 "ns_config_template": ("ns_config_template", "_id"),
aticig2b5e1232022-08-10 17:30:12 +030092 }
93
94 if db_collection not in search_dict:
garciadeblasf2af4a12023-01-24 16:56:54 +010095 raise NBIBadArgumentsException(
96 "db_collection should be equal to vnfds or nsds", "db_collection"
97 )
aticig2b5e1232022-08-10 17:30:12 +030098
99 record_list = db.get_list(
100 search_dict[db_collection][0],
101 {search_dict[db_collection][1]: descriptor["_id"]},
102 )
103
104 if record_list:
105 return True
106
107 except (DbException, KeyError, NBIBadArgumentsException) as error:
garciadeblasf2af4a12023-01-24 16:56:54 +0100108 raise EngineException(
109 f"Error occured while detecting the descriptor usage: {error}"
110 )
aticig2b5e1232022-08-10 17:30:12 +0300111
112
113def update_descriptor_usage_state(
114 descriptor: dict, db_collection: str, db: object
115) -> None:
116 """Updates the descriptor usage state.
117
118 Args:
119 descriptor (dict): VNF or NS Descriptor as dictionary
120 db_collection (str): collection name which is looked for in DB
121 db (object): name of db object
122
123 Returns:
124 None
125
126 """
127 try:
128 descriptor_update = {
129 "_admin.usageState": "NOT_IN_USE",
130 }
131
132 if detect_descriptor_usage(descriptor, db_collection, db):
133 descriptor_update = {
134 "_admin.usageState": "IN_USE",
135 }
136
garciadeblasf2af4a12023-01-24 16:56:54 +0100137 db.set_one(
138 db_collection, {"_id": descriptor["_id"]}, update_dict=descriptor_update
139 )
aticig2b5e1232022-08-10 17:30:12 +0300140
141 except (DbException, KeyError, NBIBadArgumentsException) as error:
garciadeblasf2af4a12023-01-24 16:56:54 +0100142 raise EngineException(
143 f"Error occured while updating the descriptor usage state: {error}"
144 )
aticig2b5e1232022-08-10 17:30:12 +0300145
146
tiernob24258a2018-10-04 18:39:49 +0200147def get_iterable(input_var):
148 """
149 Returns an iterable, in case input_var is None it just returns an empty tuple
150 :param input_var: can be a list, tuple or None
151 :return: input_var or () if it is None
152 """
153 if input_var is None:
154 return ()
155 return input_var
156
157
158def versiontuple(v):
159 """utility for compare dot separate versions. Fills with zeros to proper number comparison"""
160 filled = []
161 for point in v.split("."):
162 filled.append(point.zfill(8))
163 return tuple(filled)
164
165
tiernocddb07d2020-10-06 08:28:00 +0000166def increment_ip_mac(ip_mac, vm_index=1):
167 if not isinstance(ip_mac, str):
168 return ip_mac
169 try:
170 # try with ipv4 look for last dot
171 i = ip_mac.rfind(".")
172 if i > 0:
173 i += 1
174 return "{}{}".format(ip_mac[:i], int(ip_mac[i:]) + vm_index)
175 # try with ipv6 or mac look for last colon. Operate in hex
176 i = ip_mac.rfind(":")
177 if i > 0:
178 i += 1
179 # format in hex, len can be 2 for mac or 4 for ipv6
garciadeblas4568a372021-03-24 09:19:48 +0100180 return ("{}{:0" + str(len(ip_mac) - i) + "x}").format(
181 ip_mac[:i], int(ip_mac[i:], 16) + vm_index
182 )
tiernocddb07d2020-10-06 08:28:00 +0000183 except Exception:
184 pass
185 return None
186
187
tiernob24258a2018-10-04 18:39:49 +0200188class BaseTopic:
189 # static variables for all instance classes
garciadeblas4568a372021-03-24 09:19:48 +0100190 topic = None # to_override
191 topic_msg = None # to_override
192 quota_name = None # to_override. If not provided topic will be used for quota_name
193 schema_new = None # to_override
tiernob24258a2018-10-04 18:39:49 +0200194 schema_edit = None # to_override
tierno65ca36d2019-02-12 19:27:52 +0100195 multiproject = True # True if this Topic can be shared by several projects. Then it contains _admin.projects_read
tiernob24258a2018-10-04 18:39:49 +0200196
delacruzramo32bab472019-09-13 12:24:22 +0200197 default_quota = 500
198
delacruzramoc061f562019-04-05 11:00:02 +0200199 # Alternative ID Fields for some Topics
garciadeblas4568a372021-03-24 09:19:48 +0100200 alt_id_field = {"projects": "name", "users": "username", "roles": "name"}
delacruzramoc061f562019-04-05 11:00:02 +0200201
delacruzramo32bab472019-09-13 12:24:22 +0200202 def __init__(self, db, fs, msg, auth):
tiernob24258a2018-10-04 18:39:49 +0200203 self.db = db
204 self.fs = fs
205 self.msg = msg
yshah53cc9eb2024-07-05 13:06:31 +0000206 self.logger = logging.getLogger("nbi.base")
delacruzramo32bab472019-09-13 12:24:22 +0200207 self.auth = auth
tiernob24258a2018-10-04 18:39:49 +0200208
209 @staticmethod
delacruzramoc061f562019-04-05 11:00:02 +0200210 def id_field(topic, value):
tierno65ca36d2019-02-12 19:27:52 +0100211 """Returns ID Field for given topic and field value"""
delacruzramoceb8baf2019-06-21 14:25:38 +0200212 if topic in BaseTopic.alt_id_field.keys() and not is_valid_uuid(value):
delacruzramoc061f562019-04-05 11:00:02 +0200213 return BaseTopic.alt_id_field[topic]
214 else:
215 return "_id"
216
217 @staticmethod
tiernob24258a2018-10-04 18:39:49 +0200218 def _remove_envelop(indata=None):
219 if not indata:
220 return {}
221 return indata
222
delacruzramo32bab472019-09-13 12:24:22 +0200223 def check_quota(self, session):
224 """
225 Check whether topic quota is exceeded by the given project
226 Used by relevant topics' 'new' function to decide whether or not creation of the new item should be allowed
tierno6b02b052020-06-02 10:07:41 +0000227 :param session[project_id]: projects (tuple) for which quota should be checked
228 :param session[force]: boolean. If true, skip quota checking
delacruzramo32bab472019-09-13 12:24:22 +0200229 :return: None
230 :raise:
231 DbException if project not found
tierno6b02b052020-06-02 10:07:41 +0000232 ValidationError if quota exceeded in one of the projects
delacruzramo32bab472019-09-13 12:24:22 +0200233 """
tiernod7749582020-05-28 10:41:10 +0000234 if session["force"]:
delacruzramo32bab472019-09-13 12:24:22 +0200235 return
236 projects = session["project_id"]
237 for project in projects:
238 proj = self.auth.get_project(project)
239 pid = proj["_id"]
tierno6b02b052020-06-02 10:07:41 +0000240 quota_name = self.quota_name or self.topic
241 quota = proj.get("quotas", {}).get(quota_name, self.default_quota)
delacruzramo32bab472019-09-13 12:24:22 +0200242 count = self.db.count(self.topic, {"_admin.projects_read": pid})
243 if count >= quota:
244 name = proj["name"]
garciadeblas4568a372021-03-24 09:19:48 +0100245 raise ValidationError(
246 "quota ({}={}) exceeded for project {} ({})".format(
247 quota_name, quota, name, pid
248 ),
249 http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
250 )
delacruzramo32bab472019-09-13 12:24:22 +0200251
tiernob24258a2018-10-04 18:39:49 +0200252 def _validate_input_new(self, input, force=False):
253 """
254 Validates input user content for a new entry. It uses jsonschema. Some overrides will use pyangbind
255 :param input: user input content for the new topic
256 :param force: may be used for being more tolerant
257 :return: The same input content, or a changed version of it.
258 """
259 if self.schema_new:
260 validate_input(input, self.schema_new)
261 return input
262
Frank Brydendeba68e2020-07-27 13:55:11 +0000263 def _validate_input_edit(self, input, content, force=False):
tiernob24258a2018-10-04 18:39:49 +0200264 """
265 Validates input user content for an edition. It uses jsonschema. Some overrides will use pyangbind
266 :param input: user input content for the new topic
267 :param force: may be used for being more tolerant
268 :return: The same input content, or a changed version of it.
269 """
270 if self.schema_edit:
271 validate_input(input, self.schema_edit)
272 return input
273
274 @staticmethod
tierno65ca36d2019-02-12 19:27:52 +0100275 def _get_project_filter(session):
tiernob24258a2018-10-04 18:39:49 +0200276 """
277 Generates a filter dictionary for querying database, so that only allowed items for this project can be
tiernof5f2e3f2020-03-23 14:42:10 +0000278 addressed. Only proprietary or public can be used. Allowed projects are at _admin.project_read/write. If it is
tiernob24258a2018-10-04 18:39:49 +0200279 not present or contains ANY mean public.
tierno65ca36d2019-02-12 19:27:52 +0100280 :param session: contains:
281 project_id: project list this session has rights to access. Can be empty, one or several
garciadeblas4568a372021-03-24 09:19:48 +0100282 set_project: items created will contain this project list
tierno65ca36d2019-02-12 19:27:52 +0100283 force: True or False
284 public: True, False or None
285 method: "list", "show", "write", "delete"
286 admin: True or False
287 :return: dictionary with project filter
tiernob24258a2018-10-04 18:39:49 +0200288 """
tierno65ca36d2019-02-12 19:27:52 +0100289 p_filter = {}
290 project_filter_n = []
291 project_filter = list(session["project_id"])
tiernob24258a2018-10-04 18:39:49 +0200292
tierno65ca36d2019-02-12 19:27:52 +0100293 if session["method"] not in ("list", "delete"):
294 if project_filter:
295 project_filter.append("ANY")
296 elif session["public"] is not None:
297 if session["public"]:
298 project_filter.append("ANY")
299 else:
300 project_filter_n.append("ANY")
301
302 if session.get("PROJECT.ne"):
303 project_filter_n.append(session["PROJECT.ne"])
304
305 if project_filter:
garciadeblas4568a372021-03-24 09:19:48 +0100306 if session["method"] in ("list", "show", "delete") or session.get(
307 "set_project"
308 ):
tierno65ca36d2019-02-12 19:27:52 +0100309 p_filter["_admin.projects_read.cont"] = project_filter
310 else:
311 p_filter["_admin.projects_write.cont"] = project_filter
312 if project_filter_n:
garciadeblas4568a372021-03-24 09:19:48 +0100313 if session["method"] in ("list", "show", "delete") or session.get(
314 "set_project"
315 ):
tierno65ca36d2019-02-12 19:27:52 +0100316 p_filter["_admin.projects_read.ncont"] = project_filter_n
317 else:
318 p_filter["_admin.projects_write.ncont"] = project_filter_n
319
320 return p_filter
321
322 def check_conflict_on_new(self, session, indata):
tiernob24258a2018-10-04 18:39:49 +0200323 """
324 Check that the data to be inserted is valid
tierno65ca36d2019-02-12 19:27:52 +0100325 :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
tiernob24258a2018-10-04 18:39:49 +0200326 :param indata: data to be inserted
tiernob24258a2018-10-04 18:39:49 +0200327 :return: None or raises EngineException
328 """
329 pass
330
tierno65ca36d2019-02-12 19:27:52 +0100331 def check_conflict_on_edit(self, session, final_content, edit_content, _id):
tiernob24258a2018-10-04 18:39:49 +0200332 """
333 Check that the data to be edited/uploaded is valid
tierno65ca36d2019-02-12 19:27:52 +0100334 :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
tiernobdebce92019-07-01 15:36:49 +0000335 :param final_content: data once modified. This method may change it.
tiernob24258a2018-10-04 18:39:49 +0200336 :param edit_content: incremental data that contains the modifications to apply
337 :param _id: internal _id
bravofb995ea22021-02-10 10:57:52 -0300338 :return: final_content or raises EngineException
tiernob24258a2018-10-04 18:39:49 +0200339 """
tierno65ca36d2019-02-12 19:27:52 +0100340 if not self.multiproject:
bravofb995ea22021-02-10 10:57:52 -0300341 return final_content
tierno65ca36d2019-02-12 19:27:52 +0100342 # Change public status
343 if session["public"] is not None:
garciadeblas4568a372021-03-24 09:19:48 +0100344 if (
345 session["public"]
346 and "ANY" not in final_content["_admin"]["projects_read"]
347 ):
tierno65ca36d2019-02-12 19:27:52 +0100348 final_content["_admin"]["projects_read"].append("ANY")
349 final_content["_admin"]["projects_write"].clear()
garciadeblas4568a372021-03-24 09:19:48 +0100350 if (
351 not session["public"]
352 and "ANY" in final_content["_admin"]["projects_read"]
353 ):
tierno65ca36d2019-02-12 19:27:52 +0100354 final_content["_admin"]["projects_read"].remove("ANY")
355
356 # Change project status
357 if session.get("set_project"):
358 for p in session["set_project"]:
359 if p not in final_content["_admin"]["projects_read"]:
360 final_content["_admin"]["projects_read"].append(p)
tiernob24258a2018-10-04 18:39:49 +0200361
bravofb995ea22021-02-10 10:57:52 -0300362 return final_content
363
tiernob24258a2018-10-04 18:39:49 +0200364 def check_unique_name(self, session, name, _id=None):
365 """
366 Check that the name is unique for this project
tierno65ca36d2019-02-12 19:27:52 +0100367 :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
tiernob24258a2018-10-04 18:39:49 +0200368 :param name: name to be checked
369 :param _id: If not None, ignore this entry that are going to change
370 :return: None or raises EngineException
371 """
tierno1f029d82019-06-13 22:37:04 +0000372 if not self.multiproject:
373 _filter = {}
374 else:
375 _filter = self._get_project_filter(session)
tiernob24258a2018-10-04 18:39:49 +0200376 _filter["name"] = name
377 if _id:
378 _filter["_id.neq"] = _id
garciadeblas4568a372021-03-24 09:19:48 +0100379 if self.db.get_one(
380 self.topic, _filter, fail_on_empty=False, fail_on_more=False
381 ):
382 raise EngineException(
383 "name '{}' already exists for {}".format(name, self.topic),
384 HTTPStatus.CONFLICT,
385 )
tiernob24258a2018-10-04 18:39:49 +0200386
387 @staticmethod
388 def format_on_new(content, project_id=None, make_public=False):
389 """
390 Modifies content descriptor to include _admin
391 :param content: descriptor to be modified
tierno65ca36d2019-02-12 19:27:52 +0100392 :param project_id: if included, it add project read/write permissions. Can be None or a list
tiernob24258a2018-10-04 18:39:49 +0200393 :param make_public: if included it is generated as public for reading.
tiernobdebce92019-07-01 15:36:49 +0000394 :return: op_id: operation id on asynchronous operation, None otherwise. In addition content is modified
tiernob24258a2018-10-04 18:39:49 +0200395 """
396 now = time()
397 if "_admin" not in content:
398 content["_admin"] = {}
399 if not content["_admin"].get("created"):
400 content["_admin"]["created"] = now
401 content["_admin"]["modified"] = now
402 if not content.get("_id"):
403 content["_id"] = str(uuid4())
tierno65ca36d2019-02-12 19:27:52 +0100404 if project_id is not None:
tiernob24258a2018-10-04 18:39:49 +0200405 if not content["_admin"].get("projects_read"):
tierno65ca36d2019-02-12 19:27:52 +0100406 content["_admin"]["projects_read"] = list(project_id)
tiernob24258a2018-10-04 18:39:49 +0200407 if make_public:
408 content["_admin"]["projects_read"].append("ANY")
409 if not content["_admin"].get("projects_write"):
tierno65ca36d2019-02-12 19:27:52 +0100410 content["_admin"]["projects_write"] = list(project_id)
tiernobdebce92019-07-01 15:36:49 +0000411 return None
tiernob24258a2018-10-04 18:39:49 +0200412
413 @staticmethod
garciadeblas80561f12024-11-20 12:04:53 +0100414 def format_on_operation(content, operation_type, operation_params=None):
415 op_id = str(uuid4())
rshri2d386cb2024-07-05 14:35:51 +0000416 now = time()
417 if "operationHistory" not in content:
418 content["operationHistory"] = []
419
420 operation = {}
421 operation["operationType"] = operation_type
rshri2d386cb2024-07-05 14:35:51 +0000422 operation["op_id"] = op_id
423 operation["result"] = None
rshri2d386cb2024-07-05 14:35:51 +0000424 operation["creationDate"] = now
425 operation["endDate"] = None
garciadeblas80561f12024-11-20 12:04:53 +0100426 operation["workflowState"] = operation["resourceState"] = operation[
427 "operationState"
428 ] = operation["gitOperationInfo"] = None
429 operation["operationParams"] = operation_params
rshri2d386cb2024-07-05 14:35:51 +0000430
431 content["operationHistory"].append(operation)
432 return op_id
433
434 @staticmethod
tiernob24258a2018-10-04 18:39:49 +0200435 def format_on_edit(final_content, edit_content):
tiernobdebce92019-07-01 15:36:49 +0000436 """
437 Modifies final_content to admin information upon edition
438 :param final_content: final content to be stored at database
439 :param edit_content: user requested update content
440 :return: operation id, if this edit implies an asynchronous operation; None otherwise
441 """
tiernob24258a2018-10-04 18:39:49 +0200442 if final_content.get("_admin"):
443 now = time()
444 final_content["_admin"]["modified"] = now
tiernobdebce92019-07-01 15:36:49 +0000445 return None
tiernob24258a2018-10-04 18:39:49 +0200446
tiernobee3bad2019-12-05 12:26:01 +0000447 def _send_msg(self, action, content, not_send_msg=None):
448 if self.topic_msg and not_send_msg is not False:
agarwalat53471982020-10-08 13:06:14 +0000449 content = content.copy()
tiernob24258a2018-10-04 18:39:49 +0200450 content.pop("_admin", None)
tiernobee3bad2019-12-05 12:26:01 +0000451 if isinstance(not_send_msg, list):
452 not_send_msg.append((self.topic_msg, action, content))
453 else:
454 self.msg.write(self.topic_msg, action, content)
tiernob24258a2018-10-04 18:39:49 +0200455
tiernob4844ab2019-05-23 08:42:12 +0000456 def check_conflict_on_del(self, session, _id, db_content):
tiernob24258a2018-10-04 18:39:49 +0200457 """
458 Check if deletion can be done because of dependencies if it is not force. To override
tierno65ca36d2019-02-12 19:27:52 +0100459 :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
460 :param _id: internal _id
tiernob4844ab2019-05-23 08:42:12 +0000461 :param db_content: The database content of this item _id
tiernob24258a2018-10-04 18:39:49 +0200462 :return: None if ok or raises EngineException with the conflict
463 """
464 pass
465
466 @staticmethod
tierno1c38f2f2020-03-24 11:51:39 +0000467 def _update_input_with_kwargs(desc, kwargs, yaml_format=False):
tiernob24258a2018-10-04 18:39:49 +0200468 """
469 Update descriptor with the kwargs. It contains dot separated keys
470 :param desc: dictionary to be updated
471 :param kwargs: plain dictionary to be used for updating.
tierno1c38f2f2020-03-24 11:51:39 +0000472 :param yaml_format: get kwargs values as yaml format.
delacruzramoc061f562019-04-05 11:00:02 +0200473 :return: None, 'desc' is modified. It raises EngineException.
tiernob24258a2018-10-04 18:39:49 +0200474 """
475 if not kwargs:
476 return
477 try:
478 for k, v in kwargs.items():
479 update_content = desc
480 kitem_old = None
481 klist = k.split(".")
482 for kitem in klist:
483 if kitem_old is not None:
484 update_content = update_content[kitem_old]
485 if isinstance(update_content, dict):
486 kitem_old = kitem
tiernoac55f062020-06-17 07:42:30 +0000487 if not isinstance(update_content.get(kitem_old), (dict, list)):
488 update_content[kitem_old] = {}
tiernob24258a2018-10-04 18:39:49 +0200489 elif isinstance(update_content, list):
tiernoac55f062020-06-17 07:42:30 +0000490 # key must be an index of the list, must be integer
tiernob24258a2018-10-04 18:39:49 +0200491 kitem_old = int(kitem)
tiernoac55f062020-06-17 07:42:30 +0000492 # if index greater than list, extend the list
493 if kitem_old >= len(update_content):
garciadeblas4568a372021-03-24 09:19:48 +0100494 update_content += [None] * (
495 kitem_old - len(update_content) + 1
496 )
tiernoac55f062020-06-17 07:42:30 +0000497 if not isinstance(update_content[kitem_old], (dict, list)):
498 update_content[kitem_old] = {}
tiernob24258a2018-10-04 18:39:49 +0200499 else:
500 raise EngineException(
garciadeblas4568a372021-03-24 09:19:48 +0100501 "Invalid query string '{}'. Descriptor is not a list nor dict at '{}'".format(
502 k, kitem
503 )
504 )
tiernoac55f062020-06-17 07:42:30 +0000505 if v is None:
506 del update_content[kitem_old]
507 else:
508 update_content[kitem_old] = v if not yaml_format else safe_load(v)
tiernob24258a2018-10-04 18:39:49 +0200509 except KeyError:
510 raise EngineException(
garciadeblas4568a372021-03-24 09:19:48 +0100511 "Invalid query string '{}'. Descriptor does not contain '{}'".format(
512 k, kitem_old
513 )
514 )
tiernob24258a2018-10-04 18:39:49 +0200515 except ValueError:
garciadeblas4568a372021-03-24 09:19:48 +0100516 raise EngineException(
517 "Invalid query string '{}'. Expected integer index list instead of '{}'".format(
518 k, kitem
519 )
520 )
tiernob24258a2018-10-04 18:39:49 +0200521 except IndexError:
522 raise EngineException(
garciadeblas4568a372021-03-24 09:19:48 +0100523 "Invalid query string '{}'. Index '{}' out of range".format(
524 k, kitem_old
525 )
526 )
tierno1c38f2f2020-03-24 11:51:39 +0000527 except YAMLError:
528 raise EngineException("Invalid query string '{}' yaml format".format(k))
tiernob24258a2018-10-04 18:39:49 +0200529
Frank Bryden19b97522020-07-10 12:32:02 +0000530 def sol005_projection(self, data):
531 # Projection was moved to child classes
532 return data
533
K Sai Kiran57589552021-01-27 21:38:34 +0530534 def show(self, session, _id, filter_q=None, api_req=False):
tiernob24258a2018-10-04 18:39:49 +0200535 """
536 Get complete information on an topic
tierno65ca36d2019-02-12 19:27:52 +0100537 :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
tiernob24258a2018-10-04 18:39:49 +0200538 :param _id: server internal id
K Sai Kiran57589552021-01-27 21:38:34 +0530539 :param filter_q: dict: query parameter
Frank Bryden19b97522020-07-10 12:32:02 +0000540 :param api_req: True if this call is serving an external API request. False if serving internal request.
tiernob24258a2018-10-04 18:39:49 +0200541 :return: dictionary, raise exception if not found.
542 """
tierno1f029d82019-06-13 22:37:04 +0000543 if not self.multiproject:
544 filter_db = {}
545 else:
546 filter_db = self._get_project_filter(session)
delacruzramoc061f562019-04-05 11:00:02 +0200547 # To allow project&user addressing by name AS WELL AS _id
548 filter_db[BaseTopic.id_field(self.topic, _id)] = _id
Frank Bryden19b97522020-07-10 12:32:02 +0000549 data = self.db.get_one(self.topic, filter_db)
550
551 # Only perform SOL005 projection if we are serving an external request
552 if api_req:
553 self.sol005_projection(data)
Frank Bryden19b97522020-07-10 12:32:02 +0000554 return data
garciadeblas4568a372021-03-24 09:19:48 +0100555
tiernob24258a2018-10-04 18:39:49 +0200556 # TODO transform data for SOL005 URL requests
557 # TODO remove _admin if not admin
558
559 def get_file(self, session, _id, path=None, accept_header=None):
560 """
561 Only implemented for descriptor topics. Return the file content of a descriptor
tierno65ca36d2019-02-12 19:27:52 +0100562 :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
tiernob24258a2018-10-04 18:39:49 +0200563 :param _id: Identity of the item to get content
564 :param path: artifact path or "$DESCRIPTOR" or None
565 :param accept_header: Content of Accept header. Must contain applition/zip or/and text/plain
566 :return: opened file or raises an exception
567 """
garciadeblas4568a372021-03-24 09:19:48 +0100568 raise EngineException(
569 "Method get_file not valid for this topic", HTTPStatus.INTERNAL_SERVER_ERROR
570 )
tiernob24258a2018-10-04 18:39:49 +0200571
Frank Bryden19b97522020-07-10 12:32:02 +0000572 def list(self, session, filter_q=None, api_req=False):
tiernob24258a2018-10-04 18:39:49 +0200573 """
574 Get a list of the topic that matches a filter
575 :param session: contains the used login username and working project
576 :param filter_q: filter of data to be applied
Frank Bryden19b97522020-07-10 12:32:02 +0000577 :param api_req: True if this call is serving an external API request. False if serving internal request.
tiernob24258a2018-10-04 18:39:49 +0200578 :return: The list, it can be empty if no one match the filter.
579 """
580 if not filter_q:
581 filter_q = {}
tierno1f029d82019-06-13 22:37:04 +0000582 if self.multiproject:
583 filter_q.update(self._get_project_filter(session))
tiernob24258a2018-10-04 18:39:49 +0200584
585 # TODO transform data for SOL005 URL requests. Transform filtering
586 # TODO implement "field-type" query string SOL005
Frank Bryden19b97522020-07-10 12:32:02 +0000587 data = self.db.get_list(self.topic, filter_q)
588
589 # Only perform SOL005 projection if we are serving an external request
590 if api_req:
591 data = [self.sol005_projection(inst) for inst in data]
garciadeblas4568a372021-03-24 09:19:48 +0100592
Frank Bryden19b97522020-07-10 12:32:02 +0000593 return data
tiernob24258a2018-10-04 18:39:49 +0200594
tierno65ca36d2019-02-12 19:27:52 +0100595 def new(self, rollback, session, indata=None, kwargs=None, headers=None):
tiernob24258a2018-10-04 18:39:49 +0200596 """
597 Creates a new entry into database.
598 :param rollback: list to append created items at database in case a rollback may to be done
tierno65ca36d2019-02-12 19:27:52 +0100599 :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
tiernob24258a2018-10-04 18:39:49 +0200600 :param indata: data to be inserted
601 :param kwargs: used to override the indata descriptor
602 :param headers: http request headers
tiernobdebce92019-07-01 15:36:49 +0000603 :return: _id, op_id:
604 _id: identity of the inserted data.
605 op_id: operation id if this is asynchronous, None otherwise
tiernob24258a2018-10-04 18:39:49 +0200606 """
607 try:
delacruzramo32bab472019-09-13 12:24:22 +0200608 if self.multiproject:
609 self.check_quota(session)
610
tiernob24258a2018-10-04 18:39:49 +0200611 content = self._remove_envelop(indata)
612
613 # Override descriptor with query string kwargs
614 self._update_input_with_kwargs(content, kwargs)
tierno65ca36d2019-02-12 19:27:52 +0100615 content = self._validate_input_new(content, force=session["force"])
616 self.check_conflict_on_new(session, content)
garciadeblas4568a372021-03-24 09:19:48 +0100617 op_id = self.format_on_new(
618 content, project_id=session["project_id"], make_public=session["public"]
619 )
tiernob24258a2018-10-04 18:39:49 +0200620 _id = self.db.create(self.topic, content)
621 rollback.append({"topic": self.topic, "_id": _id})
tiernobdebce92019-07-01 15:36:49 +0000622 if op_id:
623 content["op_id"] = op_id
tierno15a1f682019-10-16 09:00:13 +0000624 self._send_msg("created", content)
tiernobdebce92019-07-01 15:36:49 +0000625 return _id, op_id
tiernob24258a2018-10-04 18:39:49 +0200626 except ValidationError as e:
627 raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
628
tierno65ca36d2019-02-12 19:27:52 +0100629 def upload_content(self, session, _id, indata, kwargs, headers):
tiernob24258a2018-10-04 18:39:49 +0200630 """
631 Only implemented for descriptor topics. Used for receiving content by chunks (with a transaction_id header
632 and/or gzip file. It will store and extract)
tierno65ca36d2019-02-12 19:27:52 +0100633 :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
tiernob24258a2018-10-04 18:39:49 +0200634 :param _id : the database id of entry to be updated
635 :param indata: http body request
636 :param kwargs: user query string to override parameters. NOT USED
637 :param headers: http request headers
tiernob24258a2018-10-04 18:39:49 +0200638 :return: True package has is completely uploaded or False if partial content has been uplodaed.
639 Raise exception on error
640 """
garciadeblas4568a372021-03-24 09:19:48 +0100641 raise EngineException(
642 "Method upload_content not valid for this topic",
643 HTTPStatus.INTERNAL_SERVER_ERROR,
644 )
tiernob24258a2018-10-04 18:39:49 +0200645
rshri2d386cb2024-07-05 14:35:51 +0000646 def create_gitname(self, content, session, _id=None):
647 if not self.multiproject:
648 _filter = {}
649 else:
650 _filter = self._get_project_filter(session)
garciadeblasb6025472024-08-15 09:50:55 +0200651 _filter["git_name"] = content["name"]
rshri2d386cb2024-07-05 14:35:51 +0000652 if _id:
653 _filter["_id.neq"] = _id
654 if self.db.get_one(
655 self.topic, _filter, fail_on_empty=False, fail_on_more=False
656 ):
657 n = 5
658 # using random.choices()
659 # generating random strings
660 res = "".join(random.choices(string.ascii_lowercase + string.digits, k=n))
661 res1 = content["name"]
662 new_name1 = res1 + res
663 new_name = new_name1.lower()
664 return new_name
665 else:
666 return content["name"]
667
668 def new_profile(self, rollback, session, indata=None, kwargs=None, headers=None):
669 step = "name unique check"
670 try:
671 self.check_unique_name(session, indata["name"])
672
673 step = "validating input parameters"
674 profile_request = self._remove_envelop(indata)
675 self._update_input_with_kwargs(profile_request, kwargs)
676 profile_request = self._validate_input_new(
677 profile_request, session["force"]
678 )
679 operation_params = profile_request
680
681 step = "filling profile details from input data"
682 profile_create = self._create_profile(profile_request, session)
683
684 step = "creating profile at database"
685 self.format_on_new(
686 profile_create, session["project_id"], make_public=session["public"]
687 )
688 profile_create["current_operation"] = None
689 op_id = self.format_on_operation(
690 profile_create,
691 "create",
692 operation_params,
693 )
694
695 _id = self.db.create(self.topic, profile_create)
garciadeblas6e88d9c2024-08-15 10:55:04 +0200696 pubkey, privkey = self._generate_age_key()
697 profile_create["age_pubkey"] = self.db.encrypt(
698 pubkey, schema_version="1.11", salt=_id
699 )
700 profile_create["age_privkey"] = self.db.encrypt(
701 privkey, schema_version="1.11", salt=_id
702 )
rshri2d386cb2024-07-05 14:35:51 +0000703 rollback.append({"topic": self.topic, "_id": _id})
704 self.db.set_one(self.topic, {"_id": _id}, profile_create)
705 if op_id:
706 profile_create["op_id"] = op_id
707 self._send_msg("profile_create", {"profile_id": _id, "operation_id": op_id})
708
709 return _id, None
710 except (
711 ValidationError,
712 EngineException,
713 DbException,
714 MsgException,
715 FsException,
716 ) as e:
717 raise type(e)("{} while '{}'".format(e, step), http_code=e.http_code)
718
719 def _create_profile(self, profile_request, session):
720 profile_desc = {
721 "name": profile_request["name"],
722 "description": profile_request["description"],
723 "default": False,
724 "git_name": self.create_gitname(profile_request, session),
725 "state": "IN_CREATION",
726 "operatingState": "IN_PROGRESS",
727 "resourceState": "IN_PROGRESS.REQUEST_RECEIVED",
728 }
729 return profile_desc
730
731 def default_profile(
732 self, rollback, session, indata=None, kwargs=None, headers=None
733 ):
734 step = "validating input parameters"
735 try:
736 profile_request = self._remove_envelop(indata)
737 self._update_input_with_kwargs(profile_request, kwargs)
738 operation_params = profile_request
739
740 step = "filling profile details from input data"
741 profile_create = self._create_default_profile(profile_request, session)
742
743 step = "creating profile at database"
744 self.format_on_new(
745 profile_create, session["project_id"], make_public=session["public"]
746 )
747 profile_create["current_operation"] = None
748 self.format_on_operation(
749 profile_create,
750 "create",
751 operation_params,
752 )
753 _id = self.db.create(self.topic, profile_create)
754 rollback.append({"topic": self.topic, "_id": _id})
755 return _id
756 except (
757 ValidationError,
758 EngineException,
759 DbException,
760 MsgException,
761 FsException,
762 ) as e:
763 raise type(e)("{} while '{}'".format(e, step), http_code=e.http_code)
764
765 def _create_default_profile(self, profile_request, session):
766 profile_desc = {
767 "name": profile_request["name"],
768 "description": f"{self.topic} profile for cluster {profile_request['name']}",
769 "default": True,
770 "git_name": self.create_gitname(profile_request, session),
771 "state": "IN_CREATION",
772 "operatingState": "IN_PROGRESS",
773 "resourceState": "IN_PROGRESS.REQUEST_RECEIVED",
774 }
775 return profile_desc
776
tiernob24258a2018-10-04 18:39:49 +0200777 def delete_list(self, session, filter_q=None):
778 """
779 Delete a several entries of a topic. This is for internal usage and test only, not exposed to NBI API
tierno65ca36d2019-02-12 19:27:52 +0100780 :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
tiernob24258a2018-10-04 18:39:49 +0200781 :param filter_q: filter of data to be applied
782 :return: The deleted list, it can be empty if no one match the filter.
783 """
784 # TODO add admin to filter, validate rights
785 if not filter_q:
786 filter_q = {}
tierno1f029d82019-06-13 22:37:04 +0000787 if self.multiproject:
788 filter_q.update(self._get_project_filter(session))
tiernob24258a2018-10-04 18:39:49 +0200789 return self.db.del_list(self.topic, filter_q)
790
tiernobee3bad2019-12-05 12:26:01 +0000791 def delete_extra(self, session, _id, db_content, not_send_msg=None):
tierno65ca36d2019-02-12 19:27:52 +0100792 """
793 Delete other things apart from database entry of a item _id.
794 e.g.: other associated elements at database and other file system storage
795 :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
796 :param _id: server internal id
tiernob4844ab2019-05-23 08:42:12 +0000797 :param db_content: The database content of the _id. It is already deleted when reached this method, but the
798 content is needed in same cases
tiernobee3bad2019-12-05 12:26:01 +0000799 :param not_send_msg: To not send message (False) or store content (list) instead
tiernob4844ab2019-05-23 08:42:12 +0000800 :return: None if ok or raises EngineException with the problem
tierno65ca36d2019-02-12 19:27:52 +0100801 """
802 pass
803
tiernobee3bad2019-12-05 12:26:01 +0000804 def delete(self, session, _id, dry_run=False, not_send_msg=None):
tiernob24258a2018-10-04 18:39:49 +0200805 """
806 Delete item by its internal _id
tierno65ca36d2019-02-12 19:27:52 +0100807 :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
tiernob24258a2018-10-04 18:39:49 +0200808 :param _id: server internal id
tiernob24258a2018-10-04 18:39:49 +0200809 :param dry_run: make checking but do not delete
tiernobee3bad2019-12-05 12:26:01 +0000810 :param not_send_msg: To not send message (False) or store content (list) instead
tiernobdebce92019-07-01 15:36:49 +0000811 :return: operation id (None if there is not operation), raise exception if error or not found, conflict, ...
tiernob24258a2018-10-04 18:39:49 +0200812 """
tiernob4844ab2019-05-23 08:42:12 +0000813 # To allow addressing projects and users by name AS WELL AS by _id
tiernof5f2e3f2020-03-23 14:42:10 +0000814 if not self.multiproject:
815 filter_q = {}
816 else:
817 filter_q = self._get_project_filter(session)
818 filter_q[self.id_field(self.topic, _id)] = _id
rshri2d386cb2024-07-05 14:35:51 +0000819
tiernob4844ab2019-05-23 08:42:12 +0000820 item_content = self.db.get_one(self.topic, filter_q)
kayal2001f71c2e82024-06-25 15:26:24 +0530821 nsd_id = item_content.get("_id")
tiernob4844ab2019-05-23 08:42:12 +0000822
rshri2d386cb2024-07-05 14:35:51 +0000823 if (
824 self.topic == "k8sinfra_controller"
825 or self.topic == "k8sinfra_config"
826 or self.topic == "k8sapp"
827 or self.topic == "k8sresource"
828 or self.topic == "clusters"
829 ):
830 if "state" in item_content:
831 item_content["state"] = "IN_DELETION"
832 item_content["operatingState"] = "PROCESSING"
833 self.db.set_one(self.topic, {"_id": _id}, item_content)
834
835 item_content_1 = self.db.get_one(self.topic, filter_q)
836 item_content_1["current_operation"] = None
837 op_id = self.format_on_operation(
838 item_content_1,
839 "delete",
840 None,
841 )
842
tiernob4844ab2019-05-23 08:42:12 +0000843 self.check_conflict_on_del(session, _id, item_content)
kayal2001f71c2e82024-06-25 15:26:24 +0530844
845 # While deteling ns descriptor associated ns config template should also get deleted.
846 if self.topic == "nsds":
847 ns_config_template_content = self.db.get_list(
848 "ns_config_template", {"nsdId": _id}
849 )
850 for template_content in ns_config_template_content:
851 if template_content is not None:
852 if template_content.get("nsdId") == nsd_id:
853 ns_config_template_id = template_content.get("_id")
854 self.db.del_one("ns_config_template", {"nsdId": nsd_id})
855 self.delete_extra(
856 session,
857 ns_config_template_id,
858 template_content,
859 not_send_msg=not_send_msg,
860 )
tierno65ca36d2019-02-12 19:27:52 +0100861 if dry_run:
862 return None
863 if self.multiproject and session["project_id"]:
tiernof5f2e3f2020-03-23 14:42:10 +0000864 # remove reference from project_read if there are more projects referencing it. If it last one,
865 # do not remove reference, but delete
garciadeblas4568a372021-03-24 09:19:48 +0100866 other_projects_referencing = next(
867 (
868 p
869 for p in item_content["_admin"]["projects_read"]
870 if p not in session["project_id"] and p != "ANY"
871 ),
872 None,
873 )
tiernof5f2e3f2020-03-23 14:42:10 +0000874 # check if there are projects referencing it (apart from ANY, that means, public)....
875 if other_projects_referencing:
876 # remove references but not delete
garciadeblas4568a372021-03-24 09:19:48 +0100877 update_dict_pull = {
878 "_admin.projects_read": session["project_id"],
879 "_admin.projects_write": session["project_id"],
880 }
881 self.db.set_one(
882 self.topic, filter_q, update_dict=None, pull_list=update_dict_pull
883 )
tiernobdebce92019-07-01 15:36:49 +0000884 return None
tiernof5f2e3f2020-03-23 14:42:10 +0000885 else:
garciadeblas4568a372021-03-24 09:19:48 +0100886 can_write = next(
887 (
888 p
889 for p in item_content["_admin"]["projects_write"]
890 if p == "ANY" or p in session["project_id"]
891 ),
892 None,
893 )
tiernof5f2e3f2020-03-23 14:42:10 +0000894 if not can_write:
garciadeblas4568a372021-03-24 09:19:48 +0100895 raise EngineException(
896 "You have not write permission to delete it",
897 http_code=HTTPStatus.UNAUTHORIZED,
898 )
tiernof5f2e3f2020-03-23 14:42:10 +0000899 # delete
rshri2d386cb2024-07-05 14:35:51 +0000900 if (
901 self.topic == "k8sinfra_controller"
902 or self.topic == "k8sinfra_config"
903 or self.topic == "k8sapp"
904 or self.topic == "k8sresource"
905 ):
906 self.db.set_one(self.topic, {"_id": _id}, item_content_1)
907 self._send_msg(
908 "delete",
909 {"profile_id": _id, "operation_id": op_id},
910 not_send_msg=not_send_msg,
911 )
912 elif self.topic == "clusters":
913 self.db.set_one("clusters", {"_id": _id}, item_content_1)
914 self._send_msg(
915 "delete",
916 {"cluster_id": _id, "operation_id": op_id},
917 not_send_msg=not_send_msg,
918 )
919 else:
920 self.db.del_one(self.topic, filter_q)
921 self.delete_extra(session, _id, item_content, not_send_msg=not_send_msg)
922 self._send_msg("deleted", {"_id": _id}, not_send_msg=not_send_msg)
yshahffcac5f2024-08-19 12:49:07 +0000923 return _id
tiernob24258a2018-10-04 18:39:49 +0200924
tierno65ca36d2019-02-12 19:27:52 +0100925 def edit(self, session, _id, indata=None, kwargs=None, content=None):
926 """
927 Change the content of an item
928 :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
929 :param _id: server internal id
930 :param indata: contains the changes to apply
931 :param kwargs: modifies indata
932 :param content: original content of the item
tiernobdebce92019-07-01 15:36:49 +0000933 :return: op_id: operation id if this is processed asynchronously, None otherwise
tierno65ca36d2019-02-12 19:27:52 +0100934 """
tiernob24258a2018-10-04 18:39:49 +0200935 indata = self._remove_envelop(indata)
936
937 # Override descriptor with query string kwargs
938 if kwargs:
939 self._update_input_with_kwargs(indata, kwargs)
940 try:
rshri2d386cb2024-07-05 14:35:51 +0000941 if (
942 self.topic == "k8sinfra_controller"
943 or self.topic == "k8sinfra_config"
944 or self.topic == "k8sapp"
945 or self.topic == "k8sresource"
yshah4ad91e12024-11-18 07:05:29 +0000946 or self.topic == "clusters"
rshri2d386cb2024-07-05 14:35:51 +0000947 ):
948 check = self.db.get_one(self.topic, {"_id": _id})
yshah4ad91e12024-11-18 07:05:29 +0000949 if self.topic != "clusters":
950 if check["default"] is True:
951 raise EngineException(
952 "Cannot edit default profiles",
953 HTTPStatus.UNPROCESSABLE_ENTITY,
954 )
rshri2d386cb2024-07-05 14:35:51 +0000955 if "name" in indata:
956 if check["name"] == indata["name"]:
957 pass
958 else:
959 self.check_unique_name(session, indata["name"])
tierno65ca36d2019-02-12 19:27:52 +0100960 if indata and session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +0100961 raise EngineException(
962 "Cannot edit content and set to project (query string SET_PROJECT) at same time",
963 HTTPStatus.UNPROCESSABLE_ENTITY,
964 )
tiernob24258a2018-10-04 18:39:49 +0200965 # TODO self._check_edition(session, indata, _id, force)
966 if not content:
967 content = self.show(session, _id)
Frank Brydendeba68e2020-07-27 13:55:11 +0000968 indata = self._validate_input_edit(indata, content, force=session["force"])
tiernob24258a2018-10-04 18:39:49 +0200969 deep_update_rfc7396(content, indata)
tiernobdebce92019-07-01 15:36:49 +0000970
971 # To allow project addressing by name AS WELL AS _id. Get the _id, just in case the provided one is a name
972 _id = content.get("_id") or _id
973
bravofb995ea22021-02-10 10:57:52 -0300974 content = self.check_conflict_on_edit(session, content, indata, _id=_id)
tiernobdebce92019-07-01 15:36:49 +0000975 op_id = self.format_on_edit(content, indata)
976
977 self.db.replace(self.topic, _id, content)
tiernob24258a2018-10-04 18:39:49 +0200978
979 indata.pop("_admin", None)
tiernobdebce92019-07-01 15:36:49 +0000980 if op_id:
981 indata["op_id"] = op_id
tiernob24258a2018-10-04 18:39:49 +0200982 indata["_id"] = _id
rshri2d386cb2024-07-05 14:35:51 +0000983 if (
984 self.topic == "k8sinfra_controller"
985 or self.topic == "k8sinfra_config"
986 or self.topic == "k8sapp"
987 or self.topic == "k8sresource"
yshah4ad91e12024-11-18 07:05:29 +0000988 or self.topic == "clusters"
rshri2d386cb2024-07-05 14:35:51 +0000989 ):
990 pass
991 else:
992 self._send_msg("edited", indata)
tiernobdebce92019-07-01 15:36:49 +0000993 return op_id
tiernob24258a2018-10-04 18:39:49 +0200994 except ValidationError as e:
995 raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
rshri2d386cb2024-07-05 14:35:51 +0000996
997 def detach(self, session, _id, profile_type):
998 # To detach the profiles from every cluster
999 filter_q = {}
1000 existing_clusters = self.db.get_list("clusters", filter_q)
1001 existing_clusters_profiles = [
1002 profile["_id"]
1003 for profile in existing_clusters
1004 if profile.get("profile_type", _id)
1005 ]
1006 update_dict = None
1007 for profile in existing_clusters_profiles:
1008 filter_q = {"_id": profile}
1009 data = self.db.get_one("clusters", filter_q)
1010 if profile_type in data:
1011 profile_ids = data[profile_type]
1012 if _id in profile_ids:
1013 profile_ids.remove(_id)
1014 update_dict = {profile_type: profile_ids}
1015 self.db.set_one("clusters", filter_q, update_dict)
garciadeblas6e88d9c2024-08-15 10:55:04 +02001016
1017 def _generate_age_key(self):
1018 ident = x25519.Identity.generate()
1019 # gets the public key
garciadeblas68f62ee2024-08-19 14:04:06 +02001020 pubkey = str(ident.to_public())
garciadeblas6e88d9c2024-08-15 10:55:04 +02001021 # gets the private key
1022 privkey = str(ident)
1023 # return both public and private key
1024 return pubkey, privkey
rshri5e6017d2024-12-02 03:10:39 +00001025
1026 def add_to_old_collection(self, content, session):
1027 self.logger.info(f"content is : {content}")
1028 item = {}
1029 item["name"] = content["name"]
1030 item["credentials"] = {}
1031 # item["k8s_version"] = content["k8s_version"]
1032 if "k8s_version" in content:
1033 item["k8s_version"] = content["k8s_version"]
1034 else:
1035 item["k8s_version"] = None
1036 vim_account_details = self.db.get_one(
1037 "vim_accounts", {"name": content["vim_account"]}
1038 )
1039 item["vim_account"] = vim_account_details["_id"]
1040 item["nets"] = {"k8s_net1": None}
1041 item["deployment_methods"] = {"juju-bundle": False, "helm-chart-v3": True}
1042 # item["description"] = content["description"]
1043 if "description" in content:
1044 item["description"] = content["description"]
1045 else:
1046 item["description"] = None
1047 item["namespace"] = "kube-system"
1048 item["osm_acm"] = True
1049 item["schema_version"] = "1.11"
1050 self.logger.info(f"item is : {item}")
1051 self.format_on_new(item, session["project_id"], make_public=session["public"])
1052 _id = self.db.create("k8sclusters", item)
1053 self.logger.info(f"_id is : {_id}")
1054
1055 item_1 = self.db.get_one("k8sclusters", {"name": item["name"]})
1056
1057 item_1["_admin"]["operationalState"] = "PROCESSING"
1058 self.logger.info(f"content is : {item_1}")
1059
1060 # Create operation data
1061 now = time()
1062 operation_data = {
1063 "lcmOperationType": "create", # Assuming 'create' operation here
1064 "operationState": "PROCESSING",
1065 "startTime": now,
1066 "statusEnteredTime": now,
1067 "detailed-status": "",
1068 "operationParams": None, # Add parameters as needed
1069 }
1070
1071 # create operation
1072 item_1["_admin"]["operations"] = [operation_data]
1073 item_1["_admin"]["current_operation"] = None
1074 self.logger.info(f"content is : {item_1}")
1075 self.db.set_one("k8sclusters", {"_id": item_1["_id"]}, item_1)
1076
1077 return