blob: e67588cadf1ae8b58a8af9c10d097e75e19fc4b0 [file] [log] [blame]
tiernoc94c3df2018-02-09 15:38:54 +01001#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3
tiernod125caf2018-11-22 16:05:54 +00004# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
tiernoc94c3df2018-02-09 15:38:54 +010017import cherrypy
18import time
19import json
20import yaml
tierno9c630112019-08-29 14:21:41 +000021import osm_nbi.html_out as html
tiernoc94c3df2018-02-09 15:38:54 +010022import logging
tiernof5298be2018-05-16 14:43:57 +020023import logging.handlers
24import getopt
25import sys
Eduardo Sousa2f988212018-07-26 01:04:11 +010026
tierno9c630112019-08-29 14:21:41 +000027from osm_nbi.authconn import AuthException, AuthconnException
28from osm_nbi.auth import Authenticator
29from osm_nbi.engine import Engine, EngineException
30from osm_nbi.subscriptions import SubscriptionThread
elumalai7802ff82023-04-24 20:38:32 +053031from osm_nbi.utils import cef_event, cef_event_builder
tierno9c630112019-08-29 14:21:41 +000032from osm_nbi.validation import ValidationError
tiernoa8d63632018-05-10 13:12:32 +020033from osm_common.dbbase import DbException
34from osm_common.fsbase import FsException
35from osm_common.msgbase import MsgException
tiernoc94c3df2018-02-09 15:38:54 +010036from http import HTTPStatus
tiernoc94c3df2018-02-09 15:38:54 +010037from codecs import getreader
tiernof5298be2018-05-16 14:43:57 +020038from os import environ, path
tiernob2e48bd2020-02-04 15:47:18 +000039from osm_nbi import version as nbi_version, version_date as nbi_version_date
tiernoc94c3df2018-02-09 15:38:54 +010040
41__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
tiernodfe09572018-04-24 10:41:10 +020042
garciadeblas4568a372021-03-24 09:19:48 +010043__version__ = "0.1.3" # file version, not NBI version
tierno9c630112019-08-29 14:21:41 +000044version_date = "Aug 2019"
45
garciadeblas4568a372021-03-24 09:19:48 +010046database_version = "1.2"
47auth_database_version = "1.0"
48nbi_server = None # instance of Server class
tierno932499c2019-01-28 17:28:10 +000049subscription_thread = None # instance of SubscriptionThread class
elumalai7802ff82023-04-24 20:38:32 +053050cef_logger = None
yshah53cc9eb2024-07-05 13:06:31 +000051logger = logging.getLogger("nbi.nbi")
tiernoc94c3df2018-02-09 15:38:54 +010052
53"""
tiernof27c79b2018-03-12 17:08:42 +010054North Bound Interface (O: OSM specific; 5,X: SOL005 not implemented yet; O5: SOL005 implemented)
tiernoc94c3df2018-02-09 15:38:54 +010055URL: /osm GET POST PUT DELETE PATCH
garciadeblas9750c5a2018-10-15 16:20:35 +020056 /nsd/v1
tierno2236d202018-05-16 19:05:16 +020057 /ns_descriptors_content O O
58 /<nsdInfoId> O O O O
tiernoc94c3df2018-02-09 15:38:54 +010059 /ns_descriptors O5 O5
60 /<nsdInfoId> O5 O5 5
61 /nsd_content O5 O5
tiernof27c79b2018-03-12 17:08:42 +010062 /nsd O
63 /artifacts[/<artifactPath>] O
kayal2001f71c2e82024-06-25 15:26:24 +053064 /ns_config_template O O
65 /<nsConfigTemplateId> O O
66 /template_content O O
tiernoc94c3df2018-02-09 15:38:54 +010067 /pnf_descriptors 5 5
68 /<pnfdInfoId> 5 5 5
69 /pnfd_content 5 5
tiernof27c79b2018-03-12 17:08:42 +010070 /subscriptions 5 5
71 /<subscriptionId> 5 X
tiernoc94c3df2018-02-09 15:38:54 +010072
73 /vnfpkgm/v1
tierno55945e72018-04-06 16:40:27 +020074 /vnf_packages_content O O
tierno2236d202018-05-16 19:05:16 +020075 /<vnfPkgId> O O
tiernoc94c3df2018-02-09 15:38:54 +010076 /vnf_packages O5 O5
77 /<vnfPkgId> O5 O5 5
tiernoc94c3df2018-02-09 15:38:54 +010078 /package_content O5 O5
79 /upload_from_uri X
tiernof27c79b2018-03-12 17:08:42 +010080 /vnfd O5
81 /artifacts[/<artifactPath>] O5
82 /subscriptions X X
83 /<subscriptionId> X X
tiernoc94c3df2018-02-09 15:38:54 +010084
85 /nslcm/v1
tiernof27c79b2018-03-12 17:08:42 +010086 /ns_instances_content O O
tierno2236d202018-05-16 19:05:16 +020087 /<nsInstanceId> O O
tiernof27c79b2018-03-12 17:08:42 +010088 /ns_instances 5 5
tierno95692442018-05-24 18:05:28 +020089 /<nsInstanceId> O5 O5
tierno65acb4d2018-04-06 16:42:40 +020090 instantiate O5
91 terminate O5
92 action O
93 scale O5
elumalai8e3806c2022-04-28 17:26:24 +053094 migrate O
aticig544a2ae2022-04-05 09:00:17 +030095 update 05
garciadeblas0964edf2022-02-11 00:43:44 +010096 heal O5
tiernoc94c3df2018-02-09 15:38:54 +010097 /ns_lcm_op_occs 5 5
98 /<nsLcmOpOccId> 5 5 5
Gabriel Cuba84a60df2023-10-30 14:01:54 -050099 cancel 05
tiernof759d822018-06-11 18:54:54 +0200100 /vnf_instances (also vnfrs for compatibility) O
101 /<vnfInstanceId> O
tiernof27c79b2018-03-12 17:08:42 +0100102 /subscriptions 5 5
103 /<subscriptionId> 5 X
garciadeblas9750c5a2018-10-15 16:20:35 +0200104
tiernocb83c942018-09-24 17:28:13 +0200105 /pdu/v1
tierno032916c2019-03-22 13:27:12 +0000106 /pdu_descriptors O O
tiernocb83c942018-09-24 17:28:13 +0200107 /<id> O O O O
garciadeblas9750c5a2018-10-15 16:20:35 +0200108
tiernof27c79b2018-03-12 17:08:42 +0100109 /admin/v1
110 /tokens O O
tierno2236d202018-05-16 19:05:16 +0200111 /<id> O O
tiernof27c79b2018-03-12 17:08:42 +0100112 /users O O
tiernocd54a4a2018-09-12 16:40:35 +0200113 /<id> O O O O
tiernof27c79b2018-03-12 17:08:42 +0100114 /projects O O
tierno2236d202018-05-16 19:05:16 +0200115 /<id> O O
tierno55ba2e62018-12-11 17:22:22 +0000116 /vim_accounts (also vims for compatibility) O O
117 /<id> O O O
118 /wim_accounts O O
tierno2236d202018-05-16 19:05:16 +0200119 /<id> O O O
tierno0f98af52018-03-19 10:28:22 +0100120 /sdns O O
tierno2236d202018-05-16 19:05:16 +0200121 /<id> O O O
delacruzramofe598fe2019-10-23 18:25:11 +0200122 /k8sclusters O O
123 /<id> O O O
124 /k8srepos O O
125 /<id> O O
Felipe Vicensb66b0412020-05-06 10:11:00 +0200126 /osmrepos O O
127 /<id> O O
tiernoc94c3df2018-02-09 15:38:54 +0100128
garciadeblas9750c5a2018-10-15 16:20:35 +0200129 /nst/v1 O O
130 /netslice_templates_content O O
131 /<nstInfoId> O O O O
132 /netslice_templates O O
133 /<nstInfoId> O O O
134 /nst_content O O
135 /nst O
136 /artifacts[/<artifactPath>] O
137 /subscriptions X X
138 /<subscriptionId> X X
139
140 /nsilcm/v1
141 /netslice_instances_content O O
142 /<SliceInstanceId> O O
143 /netslice_instances O O
144 /<SliceInstanceId> O O
145 instantiate O
146 terminate O
147 action O
148 /nsi_lcm_op_occs O O
149 /<nsiLcmOpOccId> O O O
150 /subscriptions X X
151 /<subscriptionId> X X
152
rshri2d386cb2024-07-05 14:35:51 +0000153 /k8scluster/v1
154 /clusters O O
155 /<clustersId> O O
156 app_profiles O O
157 infra_controller_profiles O O
158 infra_config_profiles O O
159 resource_profiles O O
160 deregister O
161 /register O
162 /app_profiles O O
163 /<app_profilesId> O O O
164 /infra_controller_profiles O O
165 /<infra_controller_profilesId> O O O
166 /infra_config_profiles O O
167 /<infra_config_profilesId> O O O
168 /resource_profiles O O
169 /<resource_profilesID> O O O
170
tierno2236d202018-05-16 19:05:16 +0200171query string:
172 Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force.
tierno36ec8602018-11-02 17:27:11 +0100173 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
174 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
175 op := "eq" | "neq" (or "ne") | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
176 attrName := string
tierno2236d202018-05-16 19:05:16 +0200177 For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any
178 item of the array, that is, pass if any item of the array pass the filter.
179 It allows both ne and neq for not equal
180 TODO: 4.3.3 Attribute selectors
181 all_fields, fields=x,y,.., exclude_default, exclude_fields=x,y,...
tiernoc94c3df2018-02-09 15:38:54 +0100182 (none) … same as “exclude_default”
183 all_fields … all attributes.
tierno2236d202018-05-16 19:05:16 +0200184 fields=<list> … all attributes except all complex attributes with minimum cardinality of zero that are not
185 conditionally mandatory, and that are not provided in <list>.
186 exclude_fields=<list> … all attributes except those complex attributes with a minimum cardinality of zero that
187 are not conditionally mandatory, and that are provided in <list>.
188 exclude_default … all attributes except those complex attributes with a minimum cardinality of zero that are not
189 conditionally mandatory, and that are part of the "default exclude set" defined in the present specification for
190 the particular resource
191 exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality
192 of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the
193 present specification for the particular resource, but that are not part of <list>
tierno65ca36d2019-02-12 19:27:52 +0100194 Additionally it admits some administrator values:
195 FORCE: To force operations skipping dependency checkings
196 ADMIN: To act as an administrator or a different project
197 PUBLIC: To get public descriptors or set a descriptor as public
198 SET_PROJECT: To make a descriptor available for other project
beierlmbc5a5242022-05-17 21:25:29 -0400199
tiernoc94c3df2018-02-09 15:38:54 +0100200Header field name Reference Example Descriptions
201 Accept IETF RFC 7231 [19] application/json Content-Types that are acceptable for the response.
202 This header field shall be present if the response is expected to have a non-empty message body.
203 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the request.
204 This header field shall be present if the request has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200205 Authorization IETF RFC 7235 [22] Bearer mF_9.B5f-4.1JqM The authorization token for the request.
206 Details are specified in clause 4.5.3.
tiernoc94c3df2018-02-09 15:38:54 +0100207 Range IETF RFC 7233 [21] 1000-2000 Requested range of bytes from a file
208Header field name Reference Example Descriptions
209 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the response.
210 This header field shall be present if the response has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200211 Location IETF RFC 7231 [19] http://www.example.com/vnflcm/v1/vnf_instances/123 Used in redirection, or when a
212 new resource has been created.
tiernoc94c3df2018-02-09 15:38:54 +0100213 This header field shall be present if the response status code is 201 or 3xx.
tierno2236d202018-05-16 19:05:16 +0200214 In the present document this header field is also used if the response status code is 202 and a new resource was
215 created.
216 WWW-Authenticate IETF RFC 7235 [22] Bearer realm="example" Challenge if the corresponding HTTP request has not
217 provided authorization, or error details if the corresponding HTTP request has provided an invalid authorization
218 token.
219 Accept-Ranges IETF RFC 7233 [21] bytes Used by the Server to signal whether or not it supports ranges for
220 certain resources.
221 Content-Range IETF RFC 7233 [21] bytes 21010-47021/ 47022 Signals the byte range that is contained in the
222 response, and the total length of the file.
tiernoc94c3df2018-02-09 15:38:54 +0100223 Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT
tiernoc94c3df2018-02-09 15:38:54 +0100224"""
225
tierno701018c2019-06-25 11:13:14 +0000226valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC")
227# ^ Contains possible administrative query string words:
228# ADMIN=True(by default)|Project|Project-list: See all elements, or elements of a project
229# (not owned by my session project).
230# PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public
231# FORCE=True(by default)|False: Force edition/deletion operations
232# SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio
233
234valid_url_methods = {
235 # contains allowed URL and methods, and the role_permission name
236 "admin": {
237 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100238 "tokens": {
239 "METHODS": ("GET", "POST", "DELETE"),
240 "ROLE_PERMISSION": "tokens:",
241 "<ID>": {"METHODS": ("GET", "DELETE"), "ROLE_PERMISSION": "tokens:id:"},
242 },
243 "users": {
244 "METHODS": ("GET", "POST"),
245 "ROLE_PERMISSION": "users:",
246 "<ID>": {
247 "METHODS": ("GET", "DELETE", "PATCH"),
248 "ROLE_PERMISSION": "users:id:",
249 },
250 },
251 "projects": {
252 "METHODS": ("GET", "POST"),
253 "ROLE_PERMISSION": "projects:",
254 "<ID>": {
255 "METHODS": ("GET", "DELETE", "PATCH"),
256 "ROLE_PERMISSION": "projects:id:",
257 },
258 },
259 "roles": {
260 "METHODS": ("GET", "POST"),
261 "ROLE_PERMISSION": "roles:",
262 "<ID>": {
263 "METHODS": ("GET", "DELETE", "PATCH"),
264 "ROLE_PERMISSION": "roles:id:",
265 },
266 },
267 "vims": {
268 "METHODS": ("GET", "POST"),
269 "ROLE_PERMISSION": "vims:",
270 "<ID>": {
271 "METHODS": ("GET", "DELETE", "PATCH"),
272 "ROLE_PERMISSION": "vims:id:",
273 },
274 },
275 "vim_accounts": {
276 "METHODS": ("GET", "POST"),
277 "ROLE_PERMISSION": "vim_accounts:",
278 "<ID>": {
279 "METHODS": ("GET", "DELETE", "PATCH"),
280 "ROLE_PERMISSION": "vim_accounts:id:",
281 },
282 },
283 "wim_accounts": {
284 "METHODS": ("GET", "POST"),
285 "ROLE_PERMISSION": "wim_accounts:",
286 "<ID>": {
287 "METHODS": ("GET", "DELETE", "PATCH"),
288 "ROLE_PERMISSION": "wim_accounts:id:",
289 },
290 },
291 "sdns": {
292 "METHODS": ("GET", "POST"),
293 "ROLE_PERMISSION": "sdn_controllers:",
294 "<ID>": {
295 "METHODS": ("GET", "DELETE", "PATCH"),
296 "ROLE_PERMISSION": "sdn_controllers:id:",
297 },
298 },
299 "k8sclusters": {
300 "METHODS": ("GET", "POST"),
301 "ROLE_PERMISSION": "k8sclusters:",
302 "<ID>": {
303 "METHODS": ("GET", "DELETE", "PATCH"),
304 "ROLE_PERMISSION": "k8sclusters:id:",
305 },
306 },
307 "vca": {
308 "METHODS": ("GET", "POST"),
309 "ROLE_PERMISSION": "vca:",
310 "<ID>": {
311 "METHODS": ("GET", "DELETE", "PATCH"),
312 "ROLE_PERMISSION": "vca:id:",
313 },
314 },
315 "k8srepos": {
316 "METHODS": ("GET", "POST"),
317 "ROLE_PERMISSION": "k8srepos:",
318 "<ID>": {
319 "METHODS": ("GET", "DELETE"),
320 "ROLE_PERMISSION": "k8srepos:id:",
321 },
322 },
323 "osmrepos": {
324 "METHODS": ("GET", "POST"),
325 "ROLE_PERMISSION": "osmrepos:",
326 "<ID>": {
327 "METHODS": ("GET", "DELETE", "PATCH"),
328 "ROLE_PERMISSION": "osmrepos:id:",
329 },
330 },
331 "domains": {
332 "METHODS": ("GET",),
333 "ROLE_PERMISSION": "domains:",
334 },
tierno701018c2019-06-25 11:13:14 +0000335 }
336 },
337 "pdu": {
338 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100339 "pdu_descriptors": {
340 "METHODS": ("GET", "POST"),
341 "ROLE_PERMISSION": "pduds:",
342 "<ID>": {
343 "METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"),
344 "ROLE_PERMISSION": "pduds:id:",
345 },
346 },
tierno701018c2019-06-25 11:13:14 +0000347 }
348 },
349 "nsd": {
350 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100351 "ns_descriptors_content": {
352 "METHODS": ("GET", "POST"),
Adurtidb4540c2024-03-21 08:42:52 +0000353 "ROLE_PERMISSION": "nsds:content:",
garciadeblas4568a372021-03-24 09:19:48 +0100354 "<ID>": {
355 "METHODS": ("GET", "PUT", "DELETE"),
356 "ROLE_PERMISSION": "nsds:id:",
357 },
358 },
359 "ns_descriptors": {
360 "METHODS": ("GET", "POST"),
361 "ROLE_PERMISSION": "nsds:",
362 "<ID>": {
363 "METHODS": ("GET", "DELETE", "PATCH"),
364 "ROLE_PERMISSION": "nsds:id:",
365 "nsd_content": {
366 "METHODS": ("GET", "PUT"),
367 "ROLE_PERMISSION": "nsds:id:content:",
368 },
369 "nsd": {
370 "METHODS": ("GET",), # descriptor inside package
Adurtidb4540c2024-03-21 08:42:52 +0000371 "ROLE_PERMISSION": "nsds:id:nsd:",
garciadeblas4568a372021-03-24 09:19:48 +0100372 },
373 "artifacts": {
374 "METHODS": ("GET",),
375 "ROLE_PERMISSION": "nsds:id:nsd_artifact:",
376 "*": None,
377 },
378 },
379 },
kayal2001f71c2e82024-06-25 15:26:24 +0530380 "ns_config_template": {
381 "METHODS": ("GET", "POST"),
382 "ROLE_PERMISSION": "ns_config_template:content:",
383 "<ID>": {
384 "METHODS": ("GET", "DELETE"),
385 "ROLE_PERMISSION": "ns_config_template:id:",
386 "template_content": {
387 "METHODS": ("GET", "PUT"),
388 "ROLE_PERMISSION": "ns_config_template:id:content:",
389 },
390 },
391 },
garciadeblas4568a372021-03-24 09:19:48 +0100392 "pnf_descriptors": {
393 "TODO": ("GET", "POST"),
394 "<ID>": {
395 "TODO": ("GET", "DELETE", "PATCH"),
396 "pnfd_content": {"TODO": ("GET", "PUT")},
397 },
398 },
399 "subscriptions": {
400 "TODO": ("GET", "POST"),
401 "<ID>": {"TODO": ("GET", "DELETE")},
402 },
tierno701018c2019-06-25 11:13:14 +0000403 }
404 },
405 "vnfpkgm": {
406 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100407 "vnf_packages_content": {
408 "METHODS": ("GET", "POST"),
Adurtidb4540c2024-03-21 08:42:52 +0000409 "ROLE_PERMISSION": "vnfds:content:",
garciadeblas4568a372021-03-24 09:19:48 +0100410 "<ID>": {
411 "METHODS": ("GET", "PUT", "DELETE"),
412 "ROLE_PERMISSION": "vnfds:id:",
413 },
414 },
415 "vnf_packages": {
416 "METHODS": ("GET", "POST"),
417 "ROLE_PERMISSION": "vnfds:",
418 "<ID>": {
419 "METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo
420 "ROLE_PERMISSION": "vnfds:id:",
421 "package_content": {
422 "METHODS": ("GET", "PUT"), # package
Adurtidb4540c2024-03-21 08:42:52 +0000423 "ROLE_PERMISSION": "vnfds:id:content:",
garciadeblas4568a372021-03-24 09:19:48 +0100424 "upload_from_uri": {
425 "METHODS": (),
426 "TODO": ("POST",),
427 "ROLE_PERMISSION": "vnfds:id:upload:",
428 },
429 },
430 "vnfd": {
431 "METHODS": ("GET",), # descriptor inside package
Adurtidb4540c2024-03-21 08:42:52 +0000432 "ROLE_PERMISSION": "vnfds:id:vnfd:",
garciadeblas4568a372021-03-24 09:19:48 +0100433 },
434 "artifacts": {
435 "METHODS": ("GET",),
436 "ROLE_PERMISSION": "vnfds:id:vnfd_artifact:",
437 "*": None,
438 },
439 "action": {
440 "METHODS": ("POST",),
441 "ROLE_PERMISSION": "vnfds:id:action:",
442 },
443 },
444 },
445 "subscriptions": {
446 "TODO": ("GET", "POST"),
447 "<ID>": {"TODO": ("GET", "DELETE")},
448 },
449 "vnfpkg_op_occs": {
450 "METHODS": ("GET",),
451 "ROLE_PERMISSION": "vnfds:vnfpkgops:",
452 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnfds:vnfpkgops:id:"},
453 },
tierno701018c2019-06-25 11:13:14 +0000454 }
455 },
456 "nslcm": {
457 "v1": {
adurti3af50952024-05-31 11:36:57 +0530458 "ns_instances_terminate": {
459 "METHODS": ("POST"),
460 "ROLE_PERMISSION": "ns_instances:",
461 },
garciadeblas4568a372021-03-24 09:19:48 +0100462 "ns_instances_content": {
463 "METHODS": ("GET", "POST"),
Adurtidb4540c2024-03-21 08:42:52 +0000464 "ROLE_PERMISSION": "ns_instances:content:",
garciadeblas4568a372021-03-24 09:19:48 +0100465 "<ID>": {
466 "METHODS": ("GET", "DELETE"),
467 "ROLE_PERMISSION": "ns_instances:id:",
468 },
469 },
470 "ns_instances": {
471 "METHODS": ("GET", "POST"),
472 "ROLE_PERMISSION": "ns_instances:",
473 "<ID>": {
474 "METHODS": ("GET", "DELETE"),
475 "ROLE_PERMISSION": "ns_instances:id:",
garciadeblas0964edf2022-02-11 00:43:44 +0100476 "heal": {
477 "METHODS": ("POST",),
478 "ROLE_PERMISSION": "ns_instances:id:heal:",
479 },
garciadeblas4568a372021-03-24 09:19:48 +0100480 "scale": {
481 "METHODS": ("POST",),
482 "ROLE_PERMISSION": "ns_instances:id:scale:",
483 },
484 "terminate": {
485 "METHODS": ("POST",),
486 "ROLE_PERMISSION": "ns_instances:id:terminate:",
487 },
488 "instantiate": {
489 "METHODS": ("POST",),
490 "ROLE_PERMISSION": "ns_instances:id:instantiate:",
491 },
elumalai8e3806c2022-04-28 17:26:24 +0530492 "migrate": {
493 "METHODS": ("POST",),
494 "ROLE_PERMISSION": "ns_instances:id:migrate:",
495 },
garciadeblas4568a372021-03-24 09:19:48 +0100496 "action": {
497 "METHODS": ("POST",),
498 "ROLE_PERMISSION": "ns_instances:id:action:",
499 },
aticig544a2ae2022-04-05 09:00:17 +0300500 "update": {
501 "METHODS": ("POST",),
502 "ROLE_PERMISSION": "ns_instances:id:update:",
503 },
garciadeblas4568a372021-03-24 09:19:48 +0100504 },
505 },
506 "ns_lcm_op_occs": {
507 "METHODS": ("GET",),
508 "ROLE_PERMISSION": "ns_instances:opps:",
509 "<ID>": {
510 "METHODS": ("GET",),
511 "ROLE_PERMISSION": "ns_instances:opps:id:",
Gabriel Cuba84a60df2023-10-30 14:01:54 -0500512 "cancel": {
513 "METHODS": ("POST",),
514 "ROLE_PERMISSION": "ns_instances:opps:cancel:",
515 },
garciadeblas4568a372021-03-24 09:19:48 +0100516 },
517 },
518 "vnfrs": {
519 "METHODS": ("GET",),
520 "ROLE_PERMISSION": "vnf_instances:",
521 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
522 },
523 "vnf_instances": {
524 "METHODS": ("GET",),
525 "ROLE_PERMISSION": "vnf_instances:",
526 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
527 },
528 "subscriptions": {
529 "METHODS": ("GET", "POST"),
530 "ROLE_PERMISSION": "ns_subscriptions:",
531 "<ID>": {
532 "METHODS": ("GET", "DELETE"),
533 "ROLE_PERMISSION": "ns_subscriptions:id:",
534 },
535 },
tierno701018c2019-06-25 11:13:14 +0000536 }
537 },
almagiae47b9132022-05-17 14:12:22 +0200538 "vnflcm": {
539 "v1": {
garciadeblasf2af4a12023-01-24 16:56:54 +0100540 "vnf_instances": {
541 "METHODS": ("GET", "POST"),
542 "ROLE_PERMISSION": "vnflcm_instances:",
543 "<ID>": {
544 "METHODS": ("GET", "DELETE"),
545 "ROLE_PERMISSION": "vnflcm_instances:id:",
546 "scale": {
547 "METHODS": ("POST",),
548 "ROLE_PERMISSION": "vnflcm_instances:id:scale:",
549 },
550 "terminate": {
551 "METHODS": ("POST",),
552 "ROLE_PERMISSION": "vnflcm_instances:id:terminate:",
553 },
554 "instantiate": {
555 "METHODS": ("POST",),
556 "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:",
557 },
558 },
559 },
560 "vnf_lcm_op_occs": {
561 "METHODS": ("GET",),
562 "ROLE_PERMISSION": "vnf_instances:opps:",
563 "<ID>": {
564 "METHODS": ("GET",),
565 "ROLE_PERMISSION": "vnf_instances:opps:id:",
566 },
567 },
568 "subscriptions": {
569 "METHODS": ("GET", "POST"),
570 "ROLE_PERMISSION": "vnflcm_subscriptions:",
571 "<ID>": {
572 "METHODS": ("GET", "DELETE"),
573 "ROLE_PERMISSION": "vnflcm_subscriptions:id:",
574 },
575 },
almagiae47b9132022-05-17 14:12:22 +0200576 }
577 },
tierno701018c2019-06-25 11:13:14 +0000578 "nst": {
579 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100580 "netslice_templates_content": {
581 "METHODS": ("GET", "POST"),
582 "ROLE_PERMISSION": "slice_templates:",
583 "<ID>": {
584 "METHODS": ("GET", "PUT", "DELETE"),
585 "ROLE_PERMISSION": "slice_templates:id:",
586 },
587 },
588 "netslice_templates": {
589 "METHODS": ("GET", "POST"),
590 "ROLE_PERMISSION": "slice_templates:",
591 "<ID>": {
592 "METHODS": ("GET", "DELETE"),
593 "TODO": ("PATCH",),
594 "ROLE_PERMISSION": "slice_templates:id:",
595 "nst_content": {
596 "METHODS": ("GET", "PUT"),
597 "ROLE_PERMISSION": "slice_templates:id:content:",
598 },
599 "nst": {
600 "METHODS": ("GET",), # descriptor inside package
601 "ROLE_PERMISSION": "slice_templates:id:content:",
602 },
603 "artifacts": {
604 "METHODS": ("GET",),
605 "ROLE_PERMISSION": "slice_templates:id:content:",
606 "*": None,
607 },
608 },
609 },
610 "subscriptions": {
611 "TODO": ("GET", "POST"),
612 "<ID>": {"TODO": ("GET", "DELETE")},
613 },
tierno701018c2019-06-25 11:13:14 +0000614 }
615 },
616 "nsilcm": {
617 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100618 "netslice_instances_content": {
619 "METHODS": ("GET", "POST"),
620 "ROLE_PERMISSION": "slice_instances:",
621 "<ID>": {
622 "METHODS": ("GET", "DELETE"),
623 "ROLE_PERMISSION": "slice_instances:id:",
624 },
625 },
626 "netslice_instances": {
627 "METHODS": ("GET", "POST"),
628 "ROLE_PERMISSION": "slice_instances:",
629 "<ID>": {
630 "METHODS": ("GET", "DELETE"),
631 "ROLE_PERMISSION": "slice_instances:id:",
632 "terminate": {
633 "METHODS": ("POST",),
634 "ROLE_PERMISSION": "slice_instances:id:terminate:",
635 },
636 "instantiate": {
637 "METHODS": ("POST",),
638 "ROLE_PERMISSION": "slice_instances:id:instantiate:",
639 },
640 "action": {
641 "METHODS": ("POST",),
642 "ROLE_PERMISSION": "slice_instances:id:action:",
643 },
644 },
645 },
646 "nsi_lcm_op_occs": {
647 "METHODS": ("GET",),
648 "ROLE_PERMISSION": "slice_instances:opps:",
649 "<ID>": {
650 "METHODS": ("GET",),
651 "ROLE_PERMISSION": "slice_instances:opps:id:",
652 },
653 },
tierno701018c2019-06-25 11:13:14 +0000654 }
655 },
656 "nspm": {
657 "v1": {
658 "pm_jobs": {
659 "<ID>": {
660 "reports": {
garciadeblas4568a372021-03-24 09:19:48 +0100661 "<ID>": {
662 "METHODS": ("GET",),
663 "ROLE_PERMISSION": "reports:id:",
664 }
tierno701018c2019-06-25 11:13:14 +0000665 }
666 },
667 },
668 },
669 },
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000670 "nsfm": {
671 "v1": {
garciadeblasf2af4a12023-01-24 16:56:54 +0100672 "alarms": {
673 "METHODS": ("GET", "PATCH"),
674 "ROLE_PERMISSION": "alarms:",
675 "<ID>": {
676 "METHODS": ("GET", "PATCH"),
677 "ROLE_PERMISSION": "alarms:id:",
678 },
679 }
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000680 },
681 },
rshri2d386cb2024-07-05 14:35:51 +0000682 "k8scluster": {
683 "v1": {
684 "clusters": {
685 "METHODS": ("GET", "POST"),
686 "ROLE_PERMISSION": "k8scluster:",
687 "<ID>": {
yshah99122b82024-11-18 07:05:29 +0000688 "METHODS": ("GET", "PATCH", "DELETE"),
rshri2d386cb2024-07-05 14:35:51 +0000689 "ROLE_PERMISSION": "k8scluster:id:",
690 "app_profiles": {
691 "METHODS": ("PATCH", "GET"),
692 "ROLE_PERMISSION": "k8scluster:id:app_profiles:",
693 },
694 "infra_controller_profiles": {
695 "METHODS": ("PATCH", "GET"),
696 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:",
697 },
698 "infra_config_profiles": {
699 "METHODS": ("PATCH", "GET"),
700 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:",
701 },
702 "resource_profiles": {
703 "METHODS": ("PATCH", "GET"),
704 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:",
705 },
706 "deregister": {
707 "METHODS": ("DELETE",),
708 "ROLE_PERMISSION": "k8scluster:id:deregister:",
709 },
yshah53cc9eb2024-07-05 13:06:31 +0000710 "get_creds": {
711 "METHODS": ("GET",),
712 "ROLE_PERMISSION": "k8scluster:id:get_creds:",
713 },
shahithyab9eb4142024-10-17 05:51:39 +0000714 "get_creds_file": {
715 "METHODS": ("GET",),
716 "ROLE_PERMISSION": "k8scluster:id:get_creds_file:",
717 "<ID>": {
718 "METHODS": ("GET",),
719 "ROLE_PERMISSION": "k8scluster:id:get_creds_file:id",
720 },
721 },
yshah99122b82024-11-18 07:05:29 +0000722 "update": {
723 "METHODS": ("POST",),
724 "ROLE_PERMISSION": "k8scluster:id:update:",
725 },
yshah53cc9eb2024-07-05 13:06:31 +0000726 "scale": {
727 "METHODS": ("POST",),
728 "ROLE_PERMISSION": "k8scluster:id:scale:",
729 },
730 "upgrade": {
731 "METHODS": ("POST",),
732 "ROLE_PERMISSION": "k8scluster:id:upgrade:",
733 },
yshahd23c6a52025-06-13 05:49:31 +0000734 "nodegroup": {
735 "METHODS": ("POST", "GET"),
736 "ROLE_PERMISSION": "k8scluster:id:nodegroup:",
737 "<ID>": {
738 "METHODS": ("GET", "PATCH", "DELETE"),
739 "ROLE_PERMISSION": "k8scluster:id:nodegroup:id",
740 "scale": {
741 "METHODS": ("POST",),
742 "ROLE_PERMISSION": "k8scluster:id:nodegroup:id:scale:",
743 },
744 },
745 },
746 "ksus": {
747 "METHODS": ("GET",),
748 "ROLE_PERMISSION": "k8scluster:id:ksus:",
749 },
rshri2d386cb2024-07-05 14:35:51 +0000750 },
751 "register": {
752 "METHODS": ("POST",),
753 "ROLE_PERMISSION": "k8scluster:register:",
754 },
755 },
756 "app_profiles": {
757 "METHODS": ("POST", "GET"),
758 "ROLE_PERMISSION": "k8scluster:app_profiles:",
759 "<ID>": {
760 "METHODS": ("GET", "PATCH", "DELETE"),
761 "ROLE_PERMISSION": "k8scluster:app_profiles:id:",
762 },
763 },
764 "infra_controller_profiles": {
765 "METHODS": ("POST", "GET"),
766 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:",
767 "<ID>": {
768 "METHODS": ("GET", "PATCH", "DELETE"),
769 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:id:",
770 },
771 },
772 "infra_config_profiles": {
773 "METHODS": ("POST", "GET"),
774 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:",
775 "<ID>": {
776 "METHODS": ("GET", "PATCH", "DELETE"),
777 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:id:",
778 },
779 },
780 "resource_profiles": {
781 "METHODS": ("POST", "GET"),
782 "ROLE_PERMISSION": "k8scluster:resource_profiles:",
783 "<ID>": {
784 "METHODS": ("GET", "PATCH", "DELETE"),
785 "ROLE_PERMISSION": "k8scluster:resource_profiles:id:",
786 },
787 },
788 }
789 },
yshah53cc9eb2024-07-05 13:06:31 +0000790 "ksu": {
791 "v1": {
792 "ksus": {
793 "METHODS": ("GET", "POST"),
794 "ROLE_PERMISSION": "ksu:",
795 "<ID>": {
796 "METHODS": ("GET", "PATCH", "DELETE"),
797 "ROLE_PERMISSION": "ksu:id:",
798 "clone": {
799 "METHODS": ("POST",),
800 "ROLE_PERMISSION": "ksu:id:clone:",
801 },
802 "move": {
803 "METHODS": ("POST",),
804 "ROLE_PERMISSION": "ksu:id:move:",
805 },
806 },
807 "update": {
808 "METHODS": ("POST",),
809 "ROLE_PERMISSION": "ksu:",
810 },
811 "delete": {
812 "METHODS": ("POST",),
813 "ROLE_PERMISSION": "ksu:",
814 },
815 },
816 }
817 },
818 "oka": {
819 "v1": {
820 "oka_packages": {
821 "METHODS": ("GET", "POST"),
822 "ROLE_PERMISSION": "oka_pkg:",
823 "<ID>": {
824 "METHODS": ("GET", "PATCH", "DELETE", "PUT"),
825 "ROLE_PERMISSION": "oka_pkg:id:",
826 },
827 }
828 }
829 },
tierno701018c2019-06-25 11:13:14 +0000830}
831
tiernoc94c3df2018-02-09 15:38:54 +0100832
833class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100834 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
835 Exception.__init__(self, message)
836 self.http_code = http_code
837
838
839class Server(object):
840 instance = 0
841 # to decode bytes to str
842 reader = getreader("utf-8")
843
844 def __init__(self):
845 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000846 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100847 self.engine = Engine(self.authenticator)
yshah53cc9eb2024-07-05 13:06:31 +0000848 self.logger = logging.getLogger("nbi.server")
tiernoc94c3df2018-02-09 15:38:54 +0100849
tiernoc94c3df2018-02-09 15:38:54 +0100850 def _format_in(self, kwargs):
garciadeblasf2af4a12023-01-24 16:56:54 +0100851 error_text = "" # error_text must be initialized outside try
tiernoc94c3df2018-02-09 15:38:54 +0100852 try:
853 indata = None
854 if cherrypy.request.body.length:
855 error_text = "Invalid input format "
856
857 if "Content-Type" in cherrypy.request.headers:
858 if "application/json" in cherrypy.request.headers["Content-Type"]:
859 error_text = "Invalid json format "
860 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100861 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100862 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
863 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100864 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100865 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100866 elif (
867 "application/binary" in cherrypy.request.headers["Content-Type"]
868 or "application/gzip"
869 in cherrypy.request.headers["Content-Type"]
870 or "application/zip" in cherrypy.request.headers["Content-Type"]
871 or "text/plain" in cherrypy.request.headers["Content-Type"]
872 ):
tiernof27c79b2018-03-12 17:08:42 +0100873 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100874 elif (
875 "multipart/form-data"
876 in cherrypy.request.headers["Content-Type"]
877 ):
yshah53cc9eb2024-07-05 13:06:31 +0000878 if (
879 "descriptor_file" in kwargs
880 or "package" in kwargs
881 and "name" in kwargs
882 ):
883 filecontent = ""
884 if "descriptor_file" in kwargs:
885 filecontent = kwargs.pop("descriptor_file")
886 if "package" in kwargs:
887 filecontent = kwargs.pop("package")
tiernoc94c3df2018-02-09 15:38:54 +0100888 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100889 raise NbiException(
890 "empty file or content", HTTPStatus.BAD_REQUEST
891 )
yshah53cc9eb2024-07-05 13:06:31 +0000892 indata = filecontent
893 if filecontent.content_type.value:
894 cherrypy.request.headers[
895 "Content-Type"
896 ] = filecontent.content_type.value
897 elif "package" in kwargs:
898 filecontent = kwargs.pop("package")
899 if not filecontent.file:
900 raise NbiException(
901 "empty file or content", HTTPStatus.BAD_REQUEST
902 )
903 indata = filecontent
tiernoc94c3df2018-02-09 15:38:54 +0100904 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100905 cherrypy.request.headers[
906 "Content-Type"
907 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100908 else:
909 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
910 # "Only 'Content-Type' of type 'application/json' or
911 # 'application/yaml' for input format are available")
912 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100913 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100914 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100915 else:
916 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100917 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100918 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100919 if not indata:
920 indata = {}
tiernoc94c3df2018-02-09 15:38:54 +0100921 format_yaml = False
922 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
923 format_yaml = True
924
925 for k, v in kwargs.items():
926 if isinstance(v, str):
927 if v == "":
928 kwargs[k] = None
929 elif format_yaml:
930 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100931 kwargs[k] = yaml.safe_load(v)
tiernoe1281182018-05-22 12:24:36 +0200932 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100933 pass
garciadeblas4568a372021-03-24 09:19:48 +0100934 elif (
935 k.endswith(".gt")
936 or k.endswith(".lt")
937 or k.endswith(".gte")
938 or k.endswith(".lte")
939 ):
tiernoc94c3df2018-02-09 15:38:54 +0100940 try:
941 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200942 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100943 try:
944 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200945 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100946 pass
947 elif v.find(",") > 0:
948 kwargs[k] = v.split(",")
949 elif isinstance(v, (list, tuple)):
950 for index in range(0, len(v)):
951 if v[index] == "":
952 v[index] = None
953 elif format_yaml:
954 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100955 v[index] = yaml.safe_load(v[index])
tiernoe1281182018-05-22 12:24:36 +0200956 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100957 pass
958
tiernof27c79b2018-03-12 17:08:42 +0100959 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100960 except (ValueError, yaml.YAMLError) as exc:
961 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
962 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100963 raise NbiException(
964 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
965 )
tiernob92094f2018-05-11 13:44:22 +0200966 except Exception as exc:
967 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100968
969 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000970 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100971 """
972 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100973 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000974 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000975 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100976 :return: None
977 """
tierno0f98af52018-03-19 10:28:22 +0100978 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100979 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100980 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100981 return html.format(
982 data, cherrypy.request, cherrypy.response, token_info
983 )
tierno09c073e2018-04-26 13:36:48 +0200984 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100985 return
986 elif hasattr(data, "read"): # file object
987 if _format:
988 cherrypy.response.headers["Content-Type"] = _format
989 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100990 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100991 else:
garciadeblas4568a372021-03-24 09:19:48 +0100992 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100993 # TODO check that cherrypy close file. If not implement pending things to close per thread next
994 return data
tierno0f98af52018-03-19 10:28:22 +0100995 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000996 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100997 return html.format(
998 data, cherrypy.request, cherrypy.response, token_info
999 )
Frank Brydenb5422da2020-08-10 11:44:11 +00001000 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +01001001 pass
garciadeblas4568a372021-03-24 09:19:48 +01001002 elif "application/json" in accept or (
1003 cherrypy.response.status and cherrypy.response.status >= 300
1004 ):
1005 cherrypy.response.headers[
1006 "Content-Type"
1007 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +00001008 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +01001009 return a.encode("utf8")
1010 cherrypy.response.headers["Content-Type"] = "application/yaml"
1011 return yaml.safe_dump(
1012 data,
1013 explicit_start=True,
1014 indent=4,
1015 default_flow_style=False,
1016 tags=False,
1017 encoding="utf-8",
1018 allow_unicode=True,
1019 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +01001020
1021 @cherrypy.expose
1022 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +00001023 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001024 try:
1025 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +00001026 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +01001027 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +01001028 else:
garciadeblas4568a372021-03-24 09:19:48 +01001029 raise cherrypy.HTTPError(
1030 HTTPStatus.METHOD_NOT_ALLOWED.value,
1031 "Method {} not allowed for tokens".format(cherrypy.request.method),
1032 )
tiernoc94c3df2018-02-09 15:38:54 +01001033
tierno701018c2019-06-25 11:13:14 +00001034 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001035
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001036 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +00001037 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +01001038 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +00001039 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001040
1041 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +02001042 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +02001043 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +02001044 try:
1045 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001046 raise NbiException(
1047 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
1048 )
tierno55945e72018-04-06 16:40:27 +02001049 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001050 raise NbiException(
1051 "Invalid URL or query string for version",
1052 HTTPStatus.METHOD_NOT_ALLOWED,
1053 )
tierno9c630112019-08-29 14:21:41 +00001054 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +00001055 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
1056 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +02001057 except NbiException as e:
1058 cherrypy.response.status = e.http_code.value
1059 problem_details = {
1060 "code": e.http_code.name,
1061 "status": e.http_code.value,
1062 "detail": str(e),
1063 }
1064 return self._format_out(problem_details, None)
1065
tierno12eac3c2020-03-19 23:22:08 +00001066 def domain(self):
1067 try:
1068 domains = {
garciadeblas4568a372021-03-24 09:19:48 +01001069 "user_domain_name": cherrypy.tree.apps["/osm"]
1070 .config["authentication"]
1071 .get("user_domain_name"),
1072 "project_domain_name": cherrypy.tree.apps["/osm"]
1073 .config["authentication"]
1074 .get("project_domain_name"),
1075 }
tierno12eac3c2020-03-19 23:22:08 +00001076 return self._format_out(domains)
1077 except NbiException as e:
1078 cherrypy.response.status = e.http_code.value
1079 problem_details = {
1080 "code": e.http_code.name,
1081 "status": e.http_code.value,
1082 "detail": str(e),
1083 }
1084 return self._format_out(problem_details, None)
1085
tiernoa5035702019-07-29 08:54:42 +00001086 @staticmethod
1087 def _format_login(token_info):
1088 """
1089 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
1090 log this information
1091 :param token_info: Dictionary with token content
1092 :return: None
1093 """
1094 cherrypy.request.login = token_info.get("username", "-")
1095 if token_info.get("project_name"):
1096 cherrypy.request.login += "/" + token_info["project_name"]
1097 if token_info.get("id"):
1098 cherrypy.request.login += ";session=" + token_info["id"][0:12]
1099
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001100 # NS Fault Management
1101 @cherrypy.expose
garciadeblasf2af4a12023-01-24 16:56:54 +01001102 def nsfm(
1103 self,
1104 version=None,
1105 topic=None,
1106 uuid=None,
1107 project_name=None,
1108 ns_id=None,
1109 *args,
garciadeblas57dddce2025-07-04 09:24:53 +02001110 **kwargs,
garciadeblasf2af4a12023-01-24 16:56:54 +01001111 ):
1112 if topic == "alarms":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001113 try:
1114 method = cherrypy.request.method
garciadeblasf2af4a12023-01-24 16:56:54 +01001115 role_permission = self._check_valid_url_method(
1116 method, "nsfm", version, topic, None, None, *args
1117 )
1118 query_string_operations = self._extract_query_string_operations(
1119 kwargs, method
1120 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001121
garciadeblasf2af4a12023-01-24 16:56:54 +01001122 self.authenticator.authorize(
1123 role_permission, query_string_operations, None
1124 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001125
1126 # to handle get request
garciadeblasf2af4a12023-01-24 16:56:54 +01001127 if cherrypy.request.method == "GET":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001128 # if request is on basis of uuid
garciadeblasf2af4a12023-01-24 16:56:54 +01001129 if uuid and uuid != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001130 try:
1131 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
garciadeblasf2af4a12023-01-24 16:56:54 +01001132 alarm_action = self.engine.db.get_one(
1133 "alarms_action", {"uuid": uuid}
1134 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001135 alarm.update(alarm_action)
garciadeblasf2af4a12023-01-24 16:56:54 +01001136 vnf = self.engine.db.get_one(
1137 "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}
1138 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001139 alarm["vnf-id"] = vnf["_id"]
1140 return self._format_out(str(alarm))
1141 except Exception:
1142 return self._format_out("Please provide valid alarm uuid")
garciadeblasf2af4a12023-01-24 16:56:54 +01001143 elif ns_id and ns_id != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001144 # if request is on basis of ns_id
1145 try:
garciadeblasf2af4a12023-01-24 16:56:54 +01001146 alarms = self.engine.db.get_list(
1147 "alarms", {"tags.ns_id": ns_id}
1148 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001149 for alarm in alarms:
garciadeblasf2af4a12023-01-24 16:56:54 +01001150 alarm_action = self.engine.db.get_one(
1151 "alarms_action", {"uuid": alarm["uuid"]}
1152 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001153 alarm.update(alarm_action)
1154 return self._format_out(str(alarms))
1155 except Exception:
1156 return self._format_out("Please provide valid ns id")
1157 else:
1158 # to return only alarm which are related to given project
garciadeblasf2af4a12023-01-24 16:56:54 +01001159 project = self.engine.db.get_one(
1160 "projects", {"name": project_name}
1161 )
1162 project_id = project.get("_id")
1163 ns_list = self.engine.db.get_list(
1164 "nsrs", {"_admin.projects_read": project_id}
1165 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001166 ns_ids = []
1167 for ns in ns_list:
1168 ns_ids.append(ns.get("_id"))
1169 alarms = self.engine.db.get_list("alarms")
garciadeblasf2af4a12023-01-24 16:56:54 +01001170 alarm_list = [
1171 alarm
1172 for alarm in alarms
1173 if alarm["tags"]["ns_id"] in ns_ids
1174 ]
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001175 for alrm in alarm_list:
garciadeblasf2af4a12023-01-24 16:56:54 +01001176 action = self.engine.db.get_one(
1177 "alarms_action", {"uuid": alrm.get("uuid")}
1178 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001179 alrm.update(action)
1180 return self._format_out(str(alarm_list))
1181 # to handle patch request for alarm update
garciadeblasf2af4a12023-01-24 16:56:54 +01001182 elif cherrypy.request.method == "PATCH":
garciadeblas4cd875d2023-02-14 19:05:34 +01001183 data = yaml.safe_load(cherrypy.request.body)
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001184 try:
1185 # check if uuid is valid
1186 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
1187 except Exception:
1188 return self._format_out("Please provide valid alarm uuid.")
1189 if data.get("is_enable") is not None:
1190 if data.get("is_enable"):
garciadeblasf2af4a12023-01-24 16:56:54 +01001191 alarm_status = "ok"
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001192 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001193 alarm_status = "disabled"
1194 self.engine.db.set_one(
1195 "alarms",
1196 {"uuid": data.get("uuid")},
1197 {"alarm_status": alarm_status},
1198 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001199 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001200 self.engine.db.set_one(
1201 "alarms",
1202 {"uuid": data.get("uuid")},
1203 {"threshold": data.get("threshold")},
1204 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001205 return self._format_out("Alarm updated")
1206 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01001207 if isinstance(
1208 e,
1209 (
1210 NbiException,
1211 EngineException,
1212 DbException,
1213 FsException,
1214 MsgException,
1215 AuthException,
1216 ValidationError,
1217 AuthconnException,
1218 ),
1219 ):
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001220 http_code_value = cherrypy.response.status = e.http_code.value
1221 http_code_name = e.http_code.name
1222 cherrypy.log("Exception {}".format(e))
1223 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001224 http_code_value = (
1225 cherrypy.response.status
1226 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001227 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
1228 http_code_name = HTTPStatus.BAD_REQUEST.name
1229 problem_details = {
1230 "code": http_code_name,
1231 "status": http_code_value,
1232 "detail": str(e),
1233 }
1234 return self._format_out(problem_details)
1235
tierno55945e72018-04-06 16:40:27 +02001236 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +01001237 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +00001238 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001239 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +01001240 indata = self._format_in(kwargs)
1241 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001242 raise NbiException(
1243 "Expected application/yaml or application/json Content-Type",
1244 HTTPStatus.BAD_REQUEST,
1245 )
tiernoa5035702019-07-29 08:54:42 +00001246
1247 if method == "GET":
1248 token_info = self.authenticator.authorize()
1249 # for logging
1250 self._format_login(token_info)
1251 if token_id:
1252 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +01001253 else:
tiernoa5035702019-07-29 08:54:42 +00001254 outdata = self.authenticator.get_token_list(token_info)
1255 elif method == "POST":
1256 try:
1257 token_info = self.authenticator.authorize()
1258 except Exception:
1259 token_info = None
1260 if kwargs:
1261 indata.update(kwargs)
1262 # This is needed to log the user when authentication fails
1263 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +01001264 outdata = token_info = self.authenticator.new_token(
1265 token_info, indata, cherrypy.request.remote
1266 )
jeganbe1a3df2024-06-04 12:05:19 +00001267 if outdata.get("email") or outdata.get("otp") == "invalid":
1268 return self._format_out(outdata, token_info)
garciadeblasf2af4a12023-01-24 16:56:54 +01001269 cherrypy.session["Authorization"] = outdata["_id"] # pylint: disable=E1101
tiernoa5035702019-07-29 08:54:42 +00001270 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1271 # for logging
1272 self._format_login(token_info)
jeganbe1a3df2024-06-04 12:05:19 +00001273 if outdata.get("otp") == "valid":
1274 outdata = {
1275 "id": outdata["id"],
1276 "message": "valid_otp",
1277 "user_id": outdata["user_id"],
1278 }
selvi.ja9a1fc82022-04-04 06:54:30 +00001279 # password expiry check
jeganbe1a3df2024-06-04 12:05:19 +00001280 elif self.authenticator.check_password_expiry(outdata):
garciadeblasf2af4a12023-01-24 16:56:54 +01001281 outdata = {
1282 "id": outdata["id"],
1283 "message": "change_password",
1284 "user_id": outdata["user_id"],
1285 }
tiernoa5035702019-07-29 08:54:42 +00001286 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1287 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
elumalai7802ff82023-04-24 20:38:32 +05301288 cef_event(
1289 cef_logger,
1290 {
1291 "name": "User Login",
1292 "sourceUserName": token_info.get("username"),
1293 "message": "User Logged In, Project={} Outcome=Success".format(
1294 token_info.get("project_name")
1295 ),
1296 },
1297 )
1298 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001299 elif method == "DELETE":
1300 if not token_id and "id" in kwargs:
1301 token_id = kwargs["id"]
1302 elif not token_id:
1303 token_info = self.authenticator.authorize()
1304 # for logging
1305 self._format_login(token_info)
1306 token_id = token_info["_id"]
Rahulc72bc8e2023-12-05 11:54:38 +00001307 if current_backend != "keystone":
1308 token_details = self.engine.db.get_one("tokens", {"_id": token_id})
1309 current_user = token_details.get("username")
1310 current_project = token_details.get("project_name")
1311 else:
1312 current_user = "keystone backend"
1313 current_project = "keystone backend"
tiernoa5035702019-07-29 08:54:42 +00001314 outdata = self.authenticator.del_token(token_id)
1315 token_info = None
garciadeblasf2af4a12023-01-24 16:56:54 +01001316 cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101
elumalai7802ff82023-04-24 20:38:32 +05301317 cef_event(
1318 cef_logger,
1319 {
1320 "name": "User Logout",
1321 "sourceUserName": current_user,
1322 "message": "User Logged Out, Project={} Outcome=Success".format(
1323 current_project
1324 ),
1325 },
1326 )
1327 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001328 # cherrypy.response.cookie["Authorization"] = token_id
1329 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1330 else:
garciadeblas4568a372021-03-24 09:19:48 +01001331 raise NbiException(
1332 "Method {} not allowed for token".format(method),
1333 HTTPStatus.METHOD_NOT_ALLOWED,
1334 )
tiernoa5035702019-07-29 08:54:42 +00001335 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001336
1337 @cherrypy.expose
1338 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001339 if not cherrypy.config.get("server.enable_test") or (
1340 isinstance(cherrypy.config["server.enable_test"], str)
1341 and cherrypy.config["server.enable_test"].lower() == "false"
1342 ):
tierno4836bac2020-01-15 14:41:48 +00001343 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1344 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001345 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001346 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001347 return (
1348 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1349 "sleep/<time>\nmessage/topic\n</pre></html>"
1350 )
tiernof27c79b2018-03-12 17:08:42 +01001351
1352 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001353 try:
1354 # self.engine.load_dbase(cherrypy.request.app.config)
garciadeblasf2af4a12023-01-24 16:56:54 +01001355 pid = self.authenticator.create_admin_project()
1356 self.authenticator.create_admin_user(pid)
tiernoc94c3df2018-02-09 15:38:54 +01001357 return "Done. User 'admin', password 'admin' created"
1358 except Exception:
1359 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1360 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001361 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001362 return cherrypy.lib.static.serve_file(
1363 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1364 "text/plain",
1365 "attachment",
1366 )
tiernof27c79b2018-03-12 17:08:42 +01001367 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001368 f_path = (
1369 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1370 )
tiernof27c79b2018-03-12 17:08:42 +01001371 f = open(f_path, "r")
1372 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001373 return f
tierno55945e72018-04-06 16:40:27 +02001374
tiernof27c79b2018-03-12 17:08:42 +01001375 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001376 deleted_info = self.engine.db.del_list(args[1], kwargs)
1377 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1378 elif len(args) and args[0] == "fs-clear":
1379 if len(args) >= 2:
1380 folders = (args[1],)
1381 else:
1382 folders = self.engine.fs.dir_ls(".")
1383 for folder in folders:
1384 self.engine.fs.file_delete(folder)
1385 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001386 elif args and args[0] == "login":
1387 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001388 cherrypy.response.headers[
1389 "WWW-Authenticate"
1390 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001391 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1392 elif args and args[0] == "login2":
1393 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001394 cherrypy.response.headers[
1395 "WWW-Authenticate"
1396 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001397 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1398 elif args and args[0] == "sleep":
1399 sleep_time = 5
1400 try:
1401 sleep_time = int(args[1])
1402 except Exception:
1403 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1404 return self._format_out("Database already initialized")
1405 thread_info = cherrypy.thread_data
1406 print(thread_info)
1407 time.sleep(sleep_time)
1408 # thread_info
1409 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001410 main_topic = args[1]
1411 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001412 try:
garciadeblas4568a372021-03-24 09:19:48 +01001413 if cherrypy.request.method == "POST":
garciadeblas4cd875d2023-02-14 19:05:34 +01001414 to_send = yaml.safe_load(cherrypy.request.body)
tierno55945e72018-04-06 16:40:27 +02001415 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001416 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001417 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001418 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001419 for k, v in kwargs.items():
garciadeblas4cd875d2023-02-14 19:05:34 +01001420 v_dict = yaml.safe_load(v)
tiernof1509b22020-05-12 14:32:37 +00001421 self.engine.msg.write(main_topic, k, v_dict)
1422 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001423 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001424 return_text += "Error: " + str(e)
1425 return_text += "</pre></html>\n"
1426 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001427
1428 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001429 "<html><pre>\nheaders:\n args: {}\n".format(args)
1430 + " kwargs: {}\n".format(kwargs)
1431 + " headers: {}\n".format(cherrypy.request.headers)
1432 + " path_info: {}\n".format(cherrypy.request.path_info)
1433 + " query_string: {}\n".format(cherrypy.request.query_string)
garciadeblasf2af4a12023-01-24 16:56:54 +01001434 + " session: {}\n".format(cherrypy.session) # pylint: disable=E1101
garciadeblas4568a372021-03-24 09:19:48 +01001435 + " cookie: {}\n".format(cherrypy.request.cookie)
1436 + " method: {}\n".format(cherrypy.request.method)
garciadeblasf2af4a12023-01-24 16:56:54 +01001437 + " session: {}\n".format(
1438 cherrypy.session.get("fieldname") # pylint: disable=E1101
1439 )
garciadeblas4568a372021-03-24 09:19:48 +01001440 + " body:\n"
1441 )
tiernoc94c3df2018-02-09 15:38:54 +01001442 return_text += " length: {}\n".format(cherrypy.request.body.length)
1443 if cherrypy.request.body.length:
1444 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001445 str(
1446 cherrypy.request.body.read(
1447 int(cherrypy.request.headers.get("Content-Length", 0))
1448 )
1449 )
1450 )
tiernoc94c3df2018-02-09 15:38:54 +01001451 if thread_info:
1452 return_text += "thread: {}\n".format(thread_info)
1453 return_text += "</pre></html>"
1454 return return_text
1455
tierno701018c2019-06-25 11:13:14 +00001456 @staticmethod
1457 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001458 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001459 raise NbiException(
1460 "URL must contain at least 'main_topic/version/topic'",
1461 HTTPStatus.METHOD_NOT_ALLOWED,
1462 )
tiernof27c79b2018-03-12 17:08:42 +01001463
tierno701018c2019-06-25 11:13:14 +00001464 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001465 for arg in args:
1466 if arg is None:
1467 break
1468 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001469 raise NbiException(
1470 "URL contains unexpected extra items '{}'".format(arg),
1471 HTTPStatus.METHOD_NOT_ALLOWED,
1472 )
tiernof27c79b2018-03-12 17:08:42 +01001473
1474 if arg in reference:
1475 reference = reference[arg]
1476 elif "<ID>" in reference:
1477 reference = reference["<ID>"]
1478 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001479 # if there is content
1480 if reference["*"]:
1481 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001482 break
1483 else:
garciadeblas4568a372021-03-24 09:19:48 +01001484 raise NbiException(
1485 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1486 )
tiernof27c79b2018-03-12 17:08:42 +01001487 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001488 raise NbiException(
1489 "Method {} not supported yet for this URL".format(method),
1490 HTTPStatus.NOT_IMPLEMENTED,
1491 )
tierno2236d202018-05-16 19:05:16 +02001492 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001493 raise NbiException(
1494 "Method {} not supported for this URL".format(method),
1495 HTTPStatus.METHOD_NOT_ALLOWED,
1496 )
tierno701018c2019-06-25 11:13:14 +00001497 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001498
1499 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001500 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001501 """
1502 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001503 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001504 :param version:
tiernob24258a2018-10-04 18:39:49 +02001505 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001506 :param id:
1507 :return: None
1508 """
1509 # Use cherrypy.request.base for absoluted path and make use of request.header HOST just in case behind aNAT
garciadeblas4568a372021-03-24 09:19:48 +01001510 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1511 main_topic, version, topic, id
1512 )
tiernof27c79b2018-03-12 17:08:42 +01001513 return
1514
tierno65ca36d2019-02-12 19:27:52 +01001515 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001516 def _extract_query_string_operations(kwargs, method):
1517 """
1518
1519 :param kwargs:
1520 :return:
1521 """
1522 query_string_operations = []
1523 if kwargs:
1524 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1525 if qs in kwargs and kwargs[qs].lower() != "false":
1526 query_string_operations.append(qs.lower() + ":" + method.lower())
1527 return query_string_operations
1528
1529 @staticmethod
1530 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001531 """
1532 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1533 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001534 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001535 :param kwargs: query string input.
1536 :param method: http method: GET, POSST, PUT, ...
1537 :param _id:
1538 :return: admin_query dictionary with keys:
1539 public: True, False or None
1540 force: True or False
1541 project_id: tuple with projects used for accessing an element
1542 set_project: tuple with projects that a created element will belong to
1543 method: show, list, delete, write
1544 """
garciadeblas4568a372021-03-24 09:19:48 +01001545 admin_query = {
1546 "force": False,
1547 "project_id": (token_info["project_id"],),
1548 "username": token_info["username"],
Adurti76d4b762024-05-07 06:04:37 +00001549 "user_id": token_info["user_id"],
garciadeblas4568a372021-03-24 09:19:48 +01001550 "admin": token_info["admin"],
37177091c0322024-11-01 08:55:59 +00001551 "admin_show": token_info["admin_show"],
garciadeblas4568a372021-03-24 09:19:48 +01001552 "public": None,
1553 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1554 }
tierno65ca36d2019-02-12 19:27:52 +01001555 if kwargs:
1556 # FORCE
1557 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001558 if (
1559 kwargs["FORCE"].lower() != "false"
1560 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001561 admin_query["force"] = True
1562 del kwargs["FORCE"]
1563 # PUBLIC
1564 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001565 if (
1566 kwargs["PUBLIC"].lower() != "false"
1567 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001568 admin_query["public"] = True
1569 else:
1570 admin_query["public"] = False
1571 del kwargs["PUBLIC"]
1572 # ADMIN
1573 if "ADMIN" in kwargs:
1574 behave_as = kwargs.pop("ADMIN")
1575 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001576 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001577 raise NbiException(
1578 "Only admin projects can use 'ADMIN' query string",
1579 HTTPStatus.UNAUTHORIZED,
1580 )
1581 if (
1582 not behave_as or behave_as.lower() == "true"
1583 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001584 admin_query["project_id"] = ()
1585 elif isinstance(behave_as, (list, tuple)):
1586 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001587 else: # isinstance(behave_as, str)
1588 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001589 if "SET_PROJECT" in kwargs:
1590 set_project = kwargs.pop("SET_PROJECT")
1591 if not set_project:
1592 admin_query["set_project"] = list(admin_query["project_id"])
1593 else:
1594 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001595 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001596 if admin_query["project_id"]:
1597 for p in set_project:
1598 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001599 raise NbiException(
1600 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1601 "'ADMIN='{p}'".format(p=p),
1602 HTTPStatus.UNAUTHORIZED,
1603 )
tierno65ca36d2019-02-12 19:27:52 +01001604 admin_query["set_project"] = set_project
1605
1606 # PROJECT_READ
1607 # if "PROJECT_READ" in kwargs:
1608 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001609 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001610 if method == "GET":
1611 if _id:
1612 admin_query["method"] = "show"
1613 else:
1614 admin_query["method"] = "list"
1615 elif method == "DELETE":
1616 admin_query["method"] = "delete"
1617 else:
1618 admin_query["method"] = "write"
1619 return admin_query
1620
tiernoc94c3df2018-02-09 15:38:54 +01001621 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001622 def default(
1623 self,
1624 main_topic=None,
1625 version=None,
1626 topic=None,
1627 _id=None,
1628 item=None,
1629 *args,
garciadeblas57dddce2025-07-04 09:24:53 +02001630 **kwargs,
garciadeblas4568a372021-03-24 09:19:48 +01001631 ):
tierno701018c2019-06-25 11:13:14 +00001632 token_info = None
selvi.j9919bbc2023-04-26 12:22:13 +00001633 outdata = {}
tiernof27c79b2018-03-12 17:08:42 +01001634 _format = None
tierno0f98af52018-03-19 10:28:22 +01001635 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001636 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001637 rollback = []
tierno701018c2019-06-25 11:13:14 +00001638 engine_session = None
elumalai7802ff82023-04-24 20:38:32 +05301639 url_id = ""
1640 log_mapping = {
1641 "POST": "Creating",
1642 "GET": "Fetching",
1643 "DELETE": "Deleting",
1644 "PUT": "Updating",
1645 "PATCH": "Updating",
1646 }
tiernoc94c3df2018-02-09 15:38:54 +01001647 try:
tiernob24258a2018-10-04 18:39:49 +02001648 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001649 raise NbiException(
1650 "URL must contain at least 'main_topic/version/topic'",
1651 HTTPStatus.METHOD_NOT_ALLOWED,
1652 )
1653 if main_topic not in (
1654 "admin",
1655 "vnfpkgm",
1656 "nsd",
1657 "nslcm",
1658 "pdu",
1659 "nst",
1660 "nsilcm",
1661 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001662 "vnflcm",
rshri2d386cb2024-07-05 14:35:51 +00001663 "k8scluster",
yshah53cc9eb2024-07-05 13:06:31 +00001664 "ksu",
1665 "oka",
garciadeblas4568a372021-03-24 09:19:48 +01001666 ):
1667 raise NbiException(
1668 "URL main_topic '{}' not supported".format(main_topic),
1669 HTTPStatus.METHOD_NOT_ALLOWED,
1670 )
1671 if version != "v1":
1672 raise NbiException(
1673 "URL version '{}' not supported".format(version),
1674 HTTPStatus.METHOD_NOT_ALLOWED,
1675 )
elumalai7802ff82023-04-24 20:38:32 +05301676 if _id is not None:
1677 url_id = _id
tiernoc94c3df2018-02-09 15:38:54 +01001678
garciadeblas4568a372021-03-24 09:19:48 +01001679 if (
1680 kwargs
1681 and "METHOD" in kwargs
1682 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1683 ):
tiernof27c79b2018-03-12 17:08:42 +01001684 method = kwargs.pop("METHOD")
1685 else:
1686 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001687
garciadeblas4568a372021-03-24 09:19:48 +01001688 role_permission = self._check_valid_url_method(
1689 method, main_topic, version, topic, _id, item, *args
1690 )
1691 query_string_operations = self._extract_query_string_operations(
1692 kwargs, method
1693 )
tiernob24258a2018-10-04 18:39:49 +02001694 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001695 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001696 token_info = self.authenticator.authorize(
1697 role_permission, query_string_operations, _id
1698 )
tierno12eac3c2020-03-19 23:22:08 +00001699 if main_topic == "admin" and topic == "domains":
1700 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001701 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001702 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001703 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301704
vijay.r35ef2f72019-04-30 17:55:49 +05301705 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001706 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001707
tiernob24258a2018-10-04 18:39:49 +02001708 if main_topic == "nsd":
1709 engine_topic = "nsds"
kayal2001f71c2e82024-06-25 15:26:24 +05301710 if topic == "ns_config_template":
1711 engine_topic = "nsconfigtemps"
tiernob24258a2018-10-04 18:39:49 +02001712 elif main_topic == "vnfpkgm":
1713 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001714 if topic == "vnfpkg_op_occs":
1715 engine_topic = "vnfpkgops"
1716 if topic == "vnf_packages" and item == "action":
1717 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001718 elif main_topic == "nslcm":
1719 engine_topic = "nsrs"
1720 if topic == "ns_lcm_op_occs":
1721 engine_topic = "nslcmops"
1722 if topic == "vnfrs" or topic == "vnf_instances":
1723 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001724 elif main_topic == "vnflcm":
1725 if topic == "vnf_lcm_op_occs":
1726 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001727 elif main_topic == "nst":
1728 engine_topic = "nsts"
1729 elif main_topic == "nsilcm":
1730 engine_topic = "nsis"
1731 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001732 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001733 elif main_topic == "pdu":
1734 engine_topic = "pdus"
rshri2d386cb2024-07-05 14:35:51 +00001735 elif main_topic == "k8scluster":
shrinithi28d887f2025-01-08 05:27:19 +00001736 engine_topic = "cluster"
rshri2d386cb2024-07-05 14:35:51 +00001737 if topic == "clusters" and _id == "register" or item == "deregister":
shrinithi28d887f2025-01-08 05:27:19 +00001738 engine_topic = "clusterops"
rshri2d386cb2024-07-05 14:35:51 +00001739 elif topic == "infra_controller_profiles":
1740 engine_topic = "infras_cont"
1741 elif topic == "infra_config_profiles":
1742 engine_topic = "infras_conf"
1743 elif topic == "resource_profiles":
1744 engine_topic = "resources"
1745 elif topic == "app_profiles":
1746 engine_topic = "apps"
yshahd23c6a52025-06-13 05:49:31 +00001747 if topic == "clusters" and item == "nodegroup":
1748 engine_topic = "node_groups"
yshah53cc9eb2024-07-05 13:06:31 +00001749 elif main_topic == "k8scluster" and item in (
1750 "upgrade",
1751 "get_creds",
1752 "scale",
1753 ):
shrinithi28d887f2025-01-08 05:27:19 +00001754 engine_topic = "cluster"
yshah53cc9eb2024-07-05 13:06:31 +00001755 elif main_topic == "ksu" and engine_topic in ("ksus", "clone", "move"):
1756 engine_topic = "ksus"
garciadeblas4568a372021-03-24 09:19:48 +01001757 if (
1758 engine_topic == "vims"
1759 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001760 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001761
preethika.p329b8182020-04-22 12:25:39 +05301762 if topic == "subscriptions":
1763 engine_topic = main_topic + "_" + topic
1764
tiernoc94c3df2018-02-09 15:38:54 +01001765 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001766 if item in (
1767 "nsd_content",
1768 "package_content",
1769 "artifacts",
1770 "vnfd",
1771 "nsd",
1772 "nst",
1773 "nst_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301774 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001775 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001776 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001777 path = "$DESCRIPTOR"
1778 elif args:
1779 path = args
tiernob24258a2018-10-04 18:39:49 +02001780 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001781 path = ()
1782 else:
1783 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001784 file, _format = self.engine.get_file(
1785 engine_session,
1786 engine_topic,
1787 _id,
1788 path,
1789 cherrypy.request.headers.get("Accept"),
1790 )
tiernof27c79b2018-03-12 17:08:42 +01001791 outdata = file
shrinithi28d887f2025-01-08 05:27:19 +00001792 # elif not _id and topic != "clusters":
1793 # outdata = self.engine.get_item_list(
1794 # engine_session, engine_topic, kwargs, api_req=True
1795 # )
rshri2d386cb2024-07-05 14:35:51 +00001796 elif topic == "clusters" and item in (
1797 "infra_controller_profiles",
1798 "infra_config_profiles",
1799 "app_profiles",
1800 "resource_profiles",
1801 ):
1802 profile = item
1803 filter_q = None
1804 outdata = self.engine.get_one_item(
1805 engine_session,
1806 engine_topic,
1807 _id,
1808 profile,
1809 filter_q,
1810 api_req=True,
1811 )
shahithyab9eb4142024-10-17 05:51:39 +00001812 elif (
1813 topic == "clusters"
1814 and item == "get_creds_file"
1815 or item == "get_creds"
1816 ):
1817 if item == "get_creds_file":
1818 op_id = args[0]
1819 file, _format = self.engine.get_cluster_creds_file(
1820 engine_session, engine_topic, _id, item, op_id
1821 )
1822 outdata = file
1823 if item == "get_creds":
1824 op_id = self.engine.get_cluster_creds(
1825 engine_session, engine_topic, _id, item
1826 )
1827 outdata = {"op_id": op_id}
shrinithi28d887f2025-01-08 05:27:19 +00001828 elif topic == "clusters" and not _id:
1829 outdata = self.engine.get_item_list_cluster(
1830 engine_session, engine_topic, kwargs, api_req=True
1831 )
yshahd23c6a52025-06-13 05:49:31 +00001832 elif topic == "clusters" and item == "nodegroup" and args:
1833 _id = args[0]
1834 outdata = outdata = self.engine.get_item(
1835 engine_session, engine_topic, _id, kwargs, True
1836 )
1837 elif topic == "clusters" and item == "nodegroup":
1838 kwargs["cluster_id"] = _id
1839 outdata = self.engine.get_item_list(
1840 engine_session, engine_topic, kwargs, api_req=True
1841 )
1842 elif topic == "clusters" and item == "ksus":
1843 engine_topic = "ksus"
1844 kwargs["cluster_id"] = _id
1845 outdata = self.engine.get_cluster_list_ksu(
1846 engine_session, engine_topic, kwargs, api_req=True
1847 )
shrinithi28d887f2025-01-08 05:27:19 +00001848 elif not _id:
1849 outdata = self.engine.get_item_list(
1850 engine_session, engine_topic, kwargs, api_req=True
1851 )
tiernoc94c3df2018-02-09 15:38:54 +01001852 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301853 if item == "reports":
1854 # TODO check that project_id (_id in this context) has permissions
1855 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301856 filter_q = None
1857 if "vcaStatusRefresh" in kwargs:
1858 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
garciadeblasf2af4a12023-01-24 16:56:54 +01001859 outdata = self.engine.get_item(
1860 engine_session, engine_topic, _id, filter_q, True
1861 )
delacruzramo271d2002019-12-02 21:00:37 +01001862
tiernof27c79b2018-03-12 17:08:42 +01001863 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001864 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001865 if topic in (
1866 "ns_descriptors_content",
1867 "vnf_packages_content",
1868 "netslice_templates_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301869 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001870 ):
tiernof27c79b2018-03-12 17:08:42 +01001871 _id = cherrypy.request.headers.get("Transaction-Id")
yshah53cc9eb2024-07-05 13:06:31 +00001872
tiernof27c79b2018-03-12 17:08:42 +01001873 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001874 _id, _ = self.engine.new_item(
1875 rollback,
1876 engine_session,
1877 engine_topic,
1878 {},
1879 None,
1880 cherrypy.request.headers,
1881 )
1882 completed = self.engine.upload_content(
1883 engine_session,
1884 engine_topic,
1885 _id,
1886 indata,
1887 kwargs,
1888 cherrypy.request.headers,
1889 )
tiernof27c79b2018-03-12 17:08:42 +01001890 if completed:
tiernob24258a2018-10-04 18:39:49 +02001891 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001892 else:
1893 cherrypy.response.headers["Transaction-Id"] = _id
1894 outdata = {"id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001895 elif topic == "oka_packages":
1896 _id = cherrypy.request.headers.get("Transaction-Id")
1897
1898 if not _id:
1899 _id, _ = self.engine.new_item(
1900 rollback,
1901 engine_session,
1902 engine_topic,
1903 {},
1904 kwargs,
1905 cherrypy.request.headers,
1906 )
1907 cherrypy.request.headers["method"] = cherrypy.request.method
1908 if indata:
1909 completed = self.engine.upload_content(
1910 engine_session,
1911 engine_topic,
1912 _id,
1913 indata,
1914 None,
1915 cherrypy.request.headers,
1916 )
1917 if completed:
1918 self._set_location_header(main_topic, version, topic, _id)
1919 else:
1920 cherrypy.response.headers["Transaction-Id"] = _id
yshahe08aff12024-11-07 09:32:22 +00001921 outdata = {"_id": _id, "id": _id}
tiernob24258a2018-10-04 18:39:49 +02001922 elif topic == "ns_instances_content":
1923 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001924 _id, _ = self.engine.new_item(
1925 rollback, engine_session, engine_topic, indata, kwargs
1926 )
tiernob24258a2018-10-04 18:39:49 +02001927 # creates nslcmop
1928 indata["lcmOperationType"] = "instantiate"
1929 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001930 nslcmop_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001931 rollback, engine_session, "nslcmops", indata, None
1932 )
tiernob24258a2018-10-04 18:39:49 +02001933 self._set_location_header(main_topic, version, topic, _id)
garciadeblasf53612b2024-07-12 14:44:37 +02001934 outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName}
adurti3af50952024-05-31 11:36:57 +05301935 elif topic == "ns_instances_terminate":
1936 if indata.get("ns_ids"):
1937 for ns_id in indata.get("ns_ids"):
1938 nslcmop_desc = {
1939 "lcmOperationType": "terminate",
1940 "nsInstanceId": ns_id,
shahithyab9eb4142024-10-17 05:51:39 +00001941 "autoremove": (
1942 indata.get("autoremove")
1943 if "autoremove" in indata
1944 else True
1945 ),
adurti3af50952024-05-31 11:36:57 +05301946 }
1947 op_id, _, _ = self.engine.new_item(
1948 rollback,
1949 engine_session,
1950 "nslcmops",
1951 nslcmop_desc,
1952 kwargs,
1953 )
1954 if not op_id:
1955 _ = self.engine.del_item(
1956 engine_session, engine_topic, ns_id
1957 )
1958 outdata = {"ns_ids": indata.get("ns_ids")}
1959 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001960 elif topic == "ns_instances" and item:
1961 indata["lcmOperationType"] = item
1962 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001963 _id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001964 rollback, engine_session, "nslcmops", indata, kwargs
1965 )
1966 self._set_location_header(
1967 main_topic, version, "ns_lcm_op_occs", _id
1968 )
garciadeblasf53612b2024-07-12 14:44:37 +02001969 outdata = {"id": _id, "nsName": nsName}
tierno65acb4d2018-04-06 16:42:40 +02001970 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001971 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001972 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001973 _id, _ = self.engine.new_item(
1974 rollback, engine_session, engine_topic, indata, kwargs
1975 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001976 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001977 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001978 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001979 nsilcmop_id, _ = self.engine.new_item(
1980 rollback, engine_session, "nsilcmops", indata, kwargs
1981 )
kuuse078f55e2019-05-16 19:24:21 +02001982 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001983 elif topic == "netslice_instances" and item:
1984 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001985 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001986 _id, _ = self.engine.new_item(
1987 rollback, engine_session, "nsilcmops", indata, kwargs
1988 )
1989 self._set_location_header(
1990 main_topic, version, "nsi_lcm_op_occs", _id
1991 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001992 outdata = {"id": _id}
1993 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001994 elif topic == "vnf_packages" and item == "action":
1995 indata["lcmOperationType"] = item
1996 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001997 _id, _ = self.engine.new_item(
1998 rollback, engine_session, "vnfpkgops", indata, kwargs
1999 )
2000 self._set_location_header(
2001 main_topic, version, "vnfpkg_op_occs", _id
2002 )
delacruzramo271d2002019-12-02 21:00:37 +01002003 outdata = {"id": _id}
2004 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05302005 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01002006 _id, _ = self.engine.new_item(
2007 rollback, engine_session, engine_topic, indata, kwargs
2008 )
preethika.p329b8182020-04-22 12:25:39 +05302009 self._set_location_header(main_topic, version, topic, _id)
2010 link = {}
2011 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01002012 outdata = {
2013 "id": _id,
2014 "filter": indata["filter"],
2015 "callbackUri": indata["CallbackUri"],
2016 "_links": link,
2017 }
preethika.p329b8182020-04-22 12:25:39 +05302018 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02002019 elif topic == "vnf_instances" and item:
2020 indata["lcmOperationType"] = item
2021 indata["vnfInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02002022 _id, nsName, _ = self.engine.new_item(
garciadeblasf2af4a12023-01-24 16:56:54 +01002023 rollback, engine_session, "vnflcmops", indata, kwargs
2024 )
2025 self._set_location_header(
2026 main_topic, version, "vnf_lcm_op_occs", _id
2027 )
garciadeblasf53612b2024-07-12 14:44:37 +02002028 outdata = {"id": _id, "nsName": nsName}
almagiae47b9132022-05-17 14:12:22 +02002029 cherrypy.response.status = HTTPStatus.ACCEPTED.value
Gabriel Cuba84a60df2023-10-30 14:01:54 -05002030 elif topic == "ns_lcm_op_occs" and item == "cancel":
2031 indata["nsLcmOpOccId"] = _id
2032 self.engine.cancel_item(
2033 rollback, engine_session, "nslcmops", indata, None
2034 )
2035 self._set_location_header(main_topic, version, topic, _id)
2036 cherrypy.response.status = HTTPStatus.ACCEPTED.value
rshri2d386cb2024-07-05 14:35:51 +00002037 elif topic == "clusters" and _id == "register":
2038 # To register a cluster
2039 _id, _ = self.engine.add_item(
2040 rollback, engine_session, engine_topic, indata, kwargs
2041 )
2042 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002043 outdata = {"_id": _id, "id": _id}
rshri2d386cb2024-07-05 14:35:51 +00002044 elif (
2045 topic
2046 in (
2047 "clusters",
2048 "infra_controller_profiles",
2049 "infra_config_profiles",
2050 "app_profiles",
2051 "resource_profiles",
2052 )
2053 and item is None
2054 ):
2055 # creates cluster, infra_controller_profiles, app_profiles, infra_config_profiles, and resource_profiles
2056 _id, _ = self.engine.new_item(
2057 rollback, engine_session, engine_topic, indata, kwargs
2058 )
2059 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002060 outdata = {"_id": _id, "id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00002061 elif topic == "ksus" and item:
2062 if item == "clone":
2063 _id = self.engine.clone(
2064 rollback,
2065 engine_session,
2066 engine_topic,
2067 _id,
2068 indata,
2069 kwargs,
2070 cherrypy.request.headers,
2071 )
2072 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002073 outdata = {"_id": _id, "id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00002074 if item == "move":
2075 op_id = self.engine.move_ksu(
2076 engine_session, engine_topic, _id, indata, kwargs
2077 )
2078 outdata = {"op_id": op_id}
2079 elif topic == "ksus" and _id == "delete":
2080 op_id = self.engine.delete_ksu(
2081 engine_session, engine_topic, _id, indata
2082 )
2083 outdata = {"op_id": op_id}
2084 elif topic == "ksus" and _id == "update":
2085 op_id = self.engine.edit_item(
2086 engine_session, engine_topic, _id, indata, kwargs
2087 )
2088 outdata = {"op_id": op_id}
garciadeblasffce2a42025-03-21 11:39:39 +01002089 elif topic == "clusters" and item in ("upgrade", "scale"):
yshahd23c6a52025-06-13 05:49:31 +00002090 op_id = self.engine.update_item(
yshah53cc9eb2024-07-05 13:06:31 +00002091 engine_session, engine_topic, _id, item, indata
2092 )
2093 outdata = {"op_id": op_id}
yshahd23c6a52025-06-13 05:49:31 +00002094 elif topic == "clusters" and item == "nodegroup":
2095 indata["cluster_id"] = _id
2096 if args:
2097 _id = args[0]
2098 op_id = self.engine.update_item(
2099 engine_session, engine_topic, _id, item, indata
2100 )
2101 outdata = {"op_id": op_id}
2102 else:
2103 _id, _ = self.engine.new_item(
2104 rollback, engine_session, engine_topic, indata, kwargs
2105 )
2106 self._set_location_header(main_topic, version, topic, _id)
2107 outdata = {"_id": _id, "id": _id}
tiernof27c79b2018-03-12 17:08:42 +01002108 else:
garciadeblas4568a372021-03-24 09:19:48 +01002109 _id, op_id = self.engine.new_item(
2110 rollback,
2111 engine_session,
2112 engine_topic,
2113 indata,
2114 kwargs,
2115 cherrypy.request.headers,
2116 )
tiernob24258a2018-10-04 18:39:49 +02002117 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002118 outdata = {"_id": _id, "id": _id}
tiernobdebce92019-07-01 15:36:49 +00002119 if op_id:
2120 outdata["op_id"] = op_id
2121 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02002122 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02002123
tiernoc94c3df2018-02-09 15:38:54 +01002124 elif method == "DELETE":
2125 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01002126 outdata = self.engine.del_item_list(
2127 engine_session, engine_topic, kwargs
2128 )
tierno09c073e2018-04-26 13:36:48 +02002129 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01002130 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00002131 # for NS NSI generate an operation
2132 op_id = None
tierno701018c2019-06-25 11:13:14 +00002133 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02002134 nslcmop_desc = {
2135 "lcmOperationType": "terminate",
2136 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002137 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02002138 }
garciadeblasf53612b2024-07-12 14:44:37 +02002139 op_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01002140 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
2141 )
tierno22577432020-04-08 15:16:57 +00002142 if op_id:
garciadeblasf53612b2024-07-12 14:44:37 +02002143 outdata = {"_id": op_id, "nsName": nsName}
garciadeblas4568a372021-03-24 09:19:48 +01002144 elif (
2145 topic == "netslice_instances_content"
2146 and not engine_session["force"]
2147 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02002148 nsilcmop_desc = {
2149 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02002150 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002151 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02002152 }
garciadeblas4568a372021-03-24 09:19:48 +01002153 op_id, _ = self.engine.new_item(
2154 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
2155 )
tierno22577432020-04-08 15:16:57 +00002156 if op_id:
2157 outdata = {"_id": op_id}
rshri2d386cb2024-07-05 14:35:51 +00002158 elif topic == "clusters" and item == "deregister":
2159 if not op_id:
2160 op_id = self.engine.remove(
2161 engine_session, engine_topic, _id
2162 )
2163 if op_id:
2164 outdata = {"_id": op_id}
2165 cherrypy.response.status = (
2166 HTTPStatus.ACCEPTED.value
2167 if op_id
2168 else HTTPStatus.NO_CONTENT.value
2169 )
yshahd23c6a52025-06-13 05:49:31 +00002170 elif topic == "clusters" and item == "nodegroup" and args:
2171 _id = args[0]
2172 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2173 if op_id:
2174 outdata = {"_id": op_id}
yshah53cc9eb2024-07-05 13:06:31 +00002175 elif topic == "ksus":
2176 op_id = self.engine.delete_ksu(
2177 engine_session, engine_topic, _id, indata
2178 )
2179 outdata = {"op_id": op_id}
tierno22577432020-04-08 15:16:57 +00002180 # if there is not any deletion in process, delete
rshri2d386cb2024-07-05 14:35:51 +00002181 elif not op_id:
tierno22577432020-04-08 15:16:57 +00002182 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2183 if op_id:
rshri2d386cb2024-07-05 14:35:51 +00002184 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01002185 cherrypy.response.status = (
2186 HTTPStatus.ACCEPTED.value
2187 if op_id
2188 else HTTPStatus.NO_CONTENT.value
2189 )
tierno09c073e2018-04-26 13:36:48 +02002190
tierno7ae10112018-05-18 14:36:02 +02002191 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00002192 op_id = None
tierno701018c2019-06-25 11:13:14 +00002193 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01002194 raise NbiException(
2195 "Nothing to update. Provide payload and/or query string",
2196 HTTPStatus.BAD_REQUEST,
2197 )
2198 if (
kayal2001f71c2e82024-06-25 15:26:24 +05302199 item
2200 in (
2201 "nsd_content",
2202 "package_content",
2203 "nst_content",
2204 "template_content",
2205 )
garciadeblas4568a372021-03-24 09:19:48 +01002206 and method == "PUT"
2207 ):
2208 completed = self.engine.upload_content(
2209 engine_session,
2210 engine_topic,
2211 _id,
2212 indata,
2213 kwargs,
2214 cherrypy.request.headers,
2215 )
tiernof27c79b2018-03-12 17:08:42 +01002216 if not completed:
2217 cherrypy.response.headers["Transaction-Id"] = id
rshri2d386cb2024-07-05 14:35:51 +00002218 elif item in (
2219 "app_profiles",
2220 "resource_profiles",
2221 "infra_controller_profiles",
2222 "infra_config_profiles",
2223 ):
2224 op_id = self.engine.edit(
2225 engine_session, engine_topic, _id, item, indata, kwargs
2226 )
yshah53cc9eb2024-07-05 13:06:31 +00002227 elif topic == "oka_packages" and method == "PATCH":
2228 if kwargs:
2229 op_id = self.engine.edit_item(
2230 engine_session, engine_topic, _id, None, kwargs
2231 )
2232 if indata:
yshahffcac5f2024-08-19 12:49:07 +00002233 if isinstance(indata, dict):
yshah53cc9eb2024-07-05 13:06:31 +00002234 op_id = self.engine.edit_item(
2235 engine_session, engine_topic, _id, indata, kwargs
2236 )
2237 else:
2238 cherrypy.request.headers["method"] = cherrypy.request.method
2239 completed = self.engine.upload_content(
2240 engine_session,
2241 engine_topic,
2242 _id,
2243 indata,
2244 {},
2245 cherrypy.request.headers,
2246 )
2247 if not completed:
2248 cherrypy.response.headers["Transaction-Id"] = id
2249 elif topic == "oka_packages" and method == "PUT":
2250 if indata:
2251 cherrypy.request.headers["method"] = cherrypy.request.method
2252 completed = self.engine.upload_content(
2253 engine_session,
2254 engine_topic,
2255 _id,
2256 indata,
2257 {},
2258 cherrypy.request.headers,
2259 )
2260 if not completed:
2261 cherrypy.response.headers["Transaction-Id"] = id
yshahd23c6a52025-06-13 05:49:31 +00002262 elif topic == "clusters" and item == "nodegroup" and args:
2263 _id = args[0]
2264 op_id = self.engine.edit_item(
2265 engine_session, engine_topic, _id, indata, kwargs
2266 )
tiernof27c79b2018-03-12 17:08:42 +01002267 else:
garciadeblas4568a372021-03-24 09:19:48 +01002268 op_id = self.engine.edit_item(
2269 engine_session, engine_topic, _id, indata, kwargs
2270 )
tiernobdebce92019-07-01 15:36:49 +00002271
2272 if op_id:
2273 cherrypy.response.status = HTTPStatus.ACCEPTED.value
2274 outdata = {"op_id": op_id}
2275 else:
2276 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
2277 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01002278 else:
garciadeblas4568a372021-03-24 09:19:48 +01002279 raise NbiException(
2280 "Method {} not allowed".format(method),
2281 HTTPStatus.METHOD_NOT_ALLOWED,
2282 )
tiernoa6bb45d2019-06-14 09:45:39 +00002283
2284 # if Role information changes, it is needed to reload the information of roles
2285 if topic == "roles" and method != "GET":
2286 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01002287
garciadeblas4568a372021-03-24 09:19:48 +01002288 if (
2289 topic == "projects"
2290 and method == "DELETE"
2291 or topic in ["users", "roles"]
2292 and method in ["PUT", "PATCH", "DELETE"]
2293 ):
delacruzramoad682a52019-12-10 16:26:34 +01002294 self.authenticator.remove_token_from_cache()
2295
garciadeblasf53612b2024-07-12 14:44:37 +02002296 cef_event(
2297 cef_logger,
2298 {
2299 "name": "User Operation",
2300 "sourceUserName": token_info.get("username"),
2301 },
2302 )
2303 if topic == "ns_instances_content" and url_id:
2304 nsName = (
2305 outdata.get("name") if method == "GET" else outdata.get("nsName")
2306 )
elumalai7802ff82023-04-24 20:38:32 +05302307 cef_event(
2308 cef_logger,
2309 {
garciadeblasf53612b2024-07-12 14:44:37 +02002310 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2311 log_mapping[method],
2312 topic,
2313 nsName,
2314 outdata.get("id"),
2315 token_info.get("project_name"),
2316 ),
2317 },
2318 )
2319 cherrypy.log("{}".format(cef_logger))
2320 elif topic == "ns_instances_content" and method == "POST":
2321 cef_event(
2322 cef_logger,
2323 {
2324 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2325 log_mapping[method],
2326 topic,
2327 outdata.get("nsName"),
2328 outdata.get("id"),
2329 token_info.get("project_name"),
2330 ),
2331 },
2332 )
2333 cherrypy.log("{}".format(cef_logger))
2334 elif topic in ("ns_instances", "vnf_instances") and item:
2335 cef_event(
2336 cef_logger,
2337 {
2338 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2339 log_mapping[method],
2340 topic,
2341 outdata.get("nsName"),
2342 url_id,
2343 token_info.get("project_name"),
2344 ),
2345 },
2346 )
2347 cherrypy.log("{}".format(cef_logger))
2348 elif item is not None:
2349 cef_event(
2350 cef_logger,
2351 {
elumalai7802ff82023-04-24 20:38:32 +05302352 "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format(
2353 item,
2354 topic,
2355 url_id,
2356 token_info.get("project_name"),
2357 ),
2358 },
2359 )
2360 cherrypy.log("{}".format(cef_logger))
2361 else:
2362 cef_event(
2363 cef_logger,
2364 {
elumalai7802ff82023-04-24 20:38:32 +05302365 "message": "{} {} {}, Project={} Outcome=Success".format(
2366 log_mapping[method],
2367 topic,
2368 url_id,
2369 token_info.get("project_name"),
2370 ),
2371 },
2372 )
2373 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002374 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02002375 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01002376 if isinstance(
2377 e,
2378 (
2379 NbiException,
2380 EngineException,
2381 DbException,
2382 FsException,
2383 MsgException,
2384 AuthException,
2385 ValidationError,
2386 AuthconnException,
2387 ),
2388 ):
tiernob24258a2018-10-04 18:39:49 +02002389 http_code_value = cherrypy.response.status = e.http_code.value
2390 http_code_name = e.http_code.name
2391 cherrypy.log("Exception {}".format(e))
2392 else:
garciadeblas4568a372021-03-24 09:19:48 +01002393 http_code_value = (
2394 cherrypy.response.status
2395 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01002396 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02002397 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02002398 if hasattr(outdata, "close"): # is an open file
2399 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02002400 error_text = str(e)
2401 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02002402 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02002403 try:
tiernocc103432018-10-19 14:10:35 +02002404 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01002405 self.engine.db.set_one(
2406 rollback_item["topic"],
2407 {"_id": rollback_item["_id"]},
2408 rollback_item["content"],
2409 fail_on_empty=False,
2410 )
preethika.p329b8182020-04-22 12:25:39 +05302411 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01002412 self.engine.db.del_list(
2413 rollback_item["topic"],
2414 rollback_item["filter"],
garciadeblas4568a372021-03-24 09:19:48 +01002415 )
tiernocc103432018-10-19 14:10:35 +02002416 else:
garciadeblas4568a372021-03-24 09:19:48 +01002417 self.engine.db.del_one(
2418 rollback_item["topic"],
2419 {"_id": rollback_item["_id"]},
2420 fail_on_empty=False,
2421 )
tierno3ace63c2018-05-03 17:51:43 +02002422 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01002423 rollback_error_text = "Rollback Exception {}: {}".format(
2424 rollback_item, e2
2425 )
tiernob24258a2018-10-04 18:39:49 +02002426 cherrypy.log(rollback_error_text)
2427 error_text += ". " + rollback_error_text
2428 # if isinstance(e, MsgException):
2429 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
2430 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01002431 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02002432 "code": http_code_name,
2433 "status": http_code_value,
2434 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01002435 }
elumalai7802ff82023-04-24 20:38:32 +05302436 if item is not None and token_info is not None:
2437 cef_event(
2438 cef_logger,
2439 {
2440 "name": "User Operation",
2441 "sourceUserName": token_info.get("username", None),
2442 "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format(
2443 item,
2444 topic,
2445 url_id,
2446 token_info.get("project_name", None),
2447 ),
2448 "severity": "2",
2449 },
2450 )
2451 cherrypy.log("{}".format(cef_logger))
2452 elif token_info is not None:
2453 cef_event(
2454 cef_logger,
2455 {
2456 "name": "User Operation",
2457 "sourceUserName": token_info.get("username", None),
2458 "message": "{} {} {}, Project={} Outcome=Failure".format(
2459 item,
2460 topic,
2461 url_id,
2462 token_info.get("project_name", None),
2463 ),
2464 "severity": "2",
2465 },
2466 )
2467 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002468 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01002469 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00002470 finally:
2471 if token_info:
2472 self._format_login(token_info)
2473 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
2474 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
2475 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01002476 cherrypy.request.login += ";{}={}".format(
2477 logging_id, outdata[logging_id][:36]
2478 )
tiernoc94c3df2018-02-09 15:38:54 +01002479
2480
tiernoc94c3df2018-02-09 15:38:54 +01002481def _start_service():
2482 """
2483 Callback function called when cherrypy.engine starts
2484 Override configuration with env variables
2485 Set database, storage, message configuration
2486 Init database with admin/admin user password
2487 """
tierno932499c2019-01-28 17:28:10 +00002488 global subscription_thread
elumalai7802ff82023-04-24 20:38:32 +05302489 global cef_logger
Rahulc72bc8e2023-12-05 11:54:38 +00002490 global current_backend
tiernoc94c3df2018-02-09 15:38:54 +01002491 cherrypy.log.error("Starting osm_nbi")
2492 # update general cherrypy configuration
2493 update_dict = {}
2494
garciadeblas4568a372021-03-24 09:19:48 +01002495 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01002496 for k, v in environ.items():
garciadeblas6d83f8f2023-06-19 22:34:49 +02002497 if k == "OSMNBI_USER_MANAGEMENT":
garciadeblas7a0dace2024-09-17 18:00:50 +02002498 feature_state = v.lower() == "true"
garciadeblas6d83f8f2023-06-19 22:34:49 +02002499 engine_config["authentication"]["user_management"] = feature_state
Adurtid68526d2023-11-14 11:14:53 +00002500 elif k == "OSMNBI_PWD_EXPIRE_DAYS":
2501 pwd_expire_days = int(v)
2502 engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days
2503 elif k == "OSMNBI_MAX_PWD_ATTEMPT":
2504 max_pwd_attempt = int(v)
2505 engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt
2506 elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS":
2507 account_expire_days = int(v)
2508 engine_config["authentication"]["account_expire_days"] = account_expire_days
jeganbe1a3df2024-06-04 12:05:19 +00002509 elif k == "OSMNBI_SMTP_SERVER":
2510 engine_config["authentication"]["smtp_server"] = v
2511 engine_config["authentication"]["all"] = environ
2512 elif k == "OSMNBI_SMTP_PORT":
2513 port = int(v)
2514 engine_config["authentication"]["smtp_port"] = port
2515 elif k == "OSMNBI_SENDER_EMAIL":
2516 engine_config["authentication"]["sender_email"] = v
2517 elif k == "OSMNBI_EMAIL_PASSWORD":
2518 engine_config["authentication"]["sender_password"] = v
2519 elif k == "OSMNBI_OTP_RETRY_COUNT":
2520 otp_retry_count = int(v)
2521 engine_config["authentication"]["retry_count"] = otp_retry_count
2522 elif k == "OSMNBI_OTP_EXPIRY_TIME":
2523 otp_expiry_time = int(v)
2524 engine_config["authentication"]["otp_expiry_time"] = otp_expiry_time
tiernoc94c3df2018-02-09 15:38:54 +01002525 if not k.startswith("OSMNBI_"):
2526 continue
tiernoe1281182018-05-22 12:24:36 +02002527 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01002528 if not k2:
2529 continue
2530 try:
2531 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01002532 if k == "OSMNBI_STATIC_DIR":
2533 engine_config["/static"]["tools.staticdir.dir"] = v
2534 engine_config["/static"]["tools.staticdir.on"] = True
2535 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
2536 update_dict["server.socket_port"] = int(v)
2537 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
2538 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02002539 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01002540 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002541 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02002542 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002543 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01002544 engine_config[k1][k2] = int(v)
2545 else:
2546 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002547
tiernoc94c3df2018-02-09 15:38:54 +01002548 except ValueError as e:
2549 cherrypy.log.error("Ignoring environ '{}': " + str(e))
2550 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01002551 cherrypy.log(
2552 "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
2553 )
tiernoc94c3df2018-02-09 15:38:54 +01002554
2555 if update_dict:
2556 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02002557 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01002558
2559 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01002560 log_format_simple = (
2561 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
2562 )
2563 log_formatter_simple = logging.Formatter(
2564 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
2565 )
tiernoc94c3df2018-02-09 15:38:54 +01002566 logger_server = logging.getLogger("cherrypy.error")
2567 logger_access = logging.getLogger("cherrypy.access")
2568 logger_cherry = logging.getLogger("cherrypy")
2569 logger_nbi = logging.getLogger("nbi")
2570
tiernof5298be2018-05-16 14:43:57 +02002571 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01002572 file_handler = logging.handlers.RotatingFileHandler(
2573 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
2574 )
tiernoc94c3df2018-02-09 15:38:54 +01002575 file_handler.setFormatter(log_formatter_simple)
2576 logger_cherry.addHandler(file_handler)
2577 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02002578 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01002579 for format_, logger in {
2580 "nbi.server %(filename)s:%(lineno)s": logger_server,
2581 "nbi.access %(filename)s:%(lineno)s": logger_access,
2582 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
2583 }.items():
tiernob24258a2018-10-04 18:39:49 +02002584 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01002585 log_formatter_cherry = logging.Formatter(
2586 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
2587 )
tiernob24258a2018-10-04 18:39:49 +02002588 str_handler = logging.StreamHandler()
2589 str_handler.setFormatter(log_formatter_cherry)
2590 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01002591
tiernof5298be2018-05-16 14:43:57 +02002592 if engine_config["global"].get("log.level"):
2593 logger_cherry.setLevel(engine_config["global"]["log.level"])
2594 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01002595
2596 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01002597 for k1, logname in {
2598 "message": "nbi.msg",
2599 "database": "nbi.db",
2600 "storage": "nbi.fs",
2601 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01002602 engine_config[k1]["logger_name"] = logname
2603 logger_module = logging.getLogger(logname)
2604 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01002605 file_handler = logging.handlers.RotatingFileHandler(
2606 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
2607 )
tiernoc94c3df2018-02-09 15:38:54 +01002608 file_handler.setFormatter(log_formatter_simple)
2609 logger_module.addHandler(file_handler)
2610 if "loglevel" in engine_config[k1]:
2611 logger_module.setLevel(engine_config[k1]["loglevel"])
2612 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01002613 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
2614 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
2615 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
2616 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
2617 target_version=auth_database_version
2618 )
tiernobee508e2019-01-21 11:21:49 +00002619
elumalai7802ff82023-04-24 20:38:32 +05302620 cef_logger = cef_event_builder(engine_config["authentication"])
2621
tierno932499c2019-01-28 17:28:10 +00002622 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01002623 subscription_thread = SubscriptionThread(
2624 config=engine_config, engine=nbi_server.engine
2625 )
tierno932499c2019-01-28 17:28:10 +00002626 subscription_thread.start()
2627 # Do not capture except SubscriptionException
2628
tiernob2e48bd2020-02-04 15:47:18 +00002629 backend = engine_config["authentication"]["backend"]
Rahulc72bc8e2023-12-05 11:54:38 +00002630 current_backend = backend
garciadeblas4568a372021-03-24 09:19:48 +01002631 cherrypy.log.error(
2632 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
2633 nbi_version, nbi_version_date, backend
2634 )
2635 )
tiernoc94c3df2018-02-09 15:38:54 +01002636
2637
2638def _stop_service():
2639 """
2640 Callback function called when cherrypy.engine stops
2641 TODO: Ending database connections.
2642 """
tierno932499c2019-01-28 17:28:10 +00002643 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01002644 if subscription_thread:
2645 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00002646 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01002647 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01002648 cherrypy.log.error("Stopping osm_nbi")
2649
tierno2236d202018-05-16 19:05:16 +02002650
tiernof5298be2018-05-16 14:43:57 +02002651def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00002652 global nbi_server
tierno932499c2019-01-28 17:28:10 +00002653 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01002654 cherrypy.engine.subscribe("start", _start_service)
2655 cherrypy.engine.subscribe("stop", _stop_service)
2656 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02002657
2658
2659def usage():
garciadeblas4568a372021-03-24 09:19:48 +01002660 print(
2661 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02002662 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
2663 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01002664 """.format(
2665 sys.argv[0]
2666 )
2667 )
tierno2236d202018-05-16 19:05:16 +02002668 # --log-socket-host HOST: send logs to this host")
2669 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002670
2671
garciadeblas4568a372021-03-24 09:19:48 +01002672if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002673 try:
2674 # load parameters and configuration
2675 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2676 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2677 config_file = None
2678 for o, a in opts:
2679 if o in ("-h", "--help"):
2680 usage()
2681 sys.exit()
2682 elif o in ("-c", "--config"):
2683 config_file = a
2684 # elif o == "--log-socket-port":
2685 # log_socket_port = a
2686 # elif o == "--log-socket-host":
2687 # log_socket_host = a
2688 # elif o == "--log-file":
2689 # log_file = a
2690 else:
garciadeblascffd2782025-07-03 17:52:04 +02002691 raise getopt.GetoptError(f"Unhandled option: {o}")
tiernof5298be2018-05-16 14:43:57 +02002692 if config_file:
2693 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002694 print(
2695 "configuration file '{}' that not exist".format(config_file),
2696 file=sys.stderr,
2697 )
tiernof5298be2018-05-16 14:43:57 +02002698 exit(1)
2699 else:
garciadeblas4568a372021-03-24 09:19:48 +01002700 for config_file in (
2701 __file__[: __file__.rfind(".")] + ".cfg",
2702 "./nbi.cfg",
2703 "/etc/osm/nbi.cfg",
2704 ):
tiernof5298be2018-05-16 14:43:57 +02002705 if path.isfile(config_file):
2706 break
2707 else:
garciadeblas4568a372021-03-24 09:19:48 +01002708 print(
2709 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2710 file=sys.stderr,
2711 )
tiernof5298be2018-05-16 14:43:57 +02002712 exit(1)
2713 nbi(config_file)
2714 except getopt.GetoptError as e:
2715 print(str(e), file=sys.stderr)
2716 # usage()
2717 exit(1)