blob: f723ff5d90a181b4c5bfbbd1793f0506b1892aab [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 },
garciadeblasb798f452025-08-05 18:21:26 +0200818 "appinstance": {
819 "v1": {
820 "appinstances": {
821 "METHODS": ("GET", "POST"),
822 "ROLE_PERMISSION": "appinstance:",
823 "<ID>": {
824 "METHODS": ("GET", "PATCH", "DELETE"),
825 "ROLE_PERMISSION": "appinstance:id:",
826 },
827 "update": {
828 "METHODS": ("POST",),
829 "ROLE_PERMISSION": "appinstance:",
830 },
831 },
832 }
833 },
yshah53cc9eb2024-07-05 13:06:31 +0000834 "oka": {
835 "v1": {
836 "oka_packages": {
837 "METHODS": ("GET", "POST"),
838 "ROLE_PERMISSION": "oka_pkg:",
839 "<ID>": {
840 "METHODS": ("GET", "PATCH", "DELETE", "PUT"),
841 "ROLE_PERMISSION": "oka_pkg:id:",
842 },
843 }
844 }
845 },
tierno701018c2019-06-25 11:13:14 +0000846}
847
tiernoc94c3df2018-02-09 15:38:54 +0100848
849class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100850 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
851 Exception.__init__(self, message)
852 self.http_code = http_code
853
854
855class Server(object):
856 instance = 0
857 # to decode bytes to str
858 reader = getreader("utf-8")
859
860 def __init__(self):
861 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000862 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100863 self.engine = Engine(self.authenticator)
yshah53cc9eb2024-07-05 13:06:31 +0000864 self.logger = logging.getLogger("nbi.server")
tiernoc94c3df2018-02-09 15:38:54 +0100865
tiernoc94c3df2018-02-09 15:38:54 +0100866 def _format_in(self, kwargs):
garciadeblasf2af4a12023-01-24 16:56:54 +0100867 error_text = "" # error_text must be initialized outside try
tiernoc94c3df2018-02-09 15:38:54 +0100868 try:
869 indata = None
870 if cherrypy.request.body.length:
871 error_text = "Invalid input format "
872
873 if "Content-Type" in cherrypy.request.headers:
874 if "application/json" in cherrypy.request.headers["Content-Type"]:
875 error_text = "Invalid json format "
876 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100877 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100878 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
879 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100880 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100881 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100882 elif (
883 "application/binary" in cherrypy.request.headers["Content-Type"]
884 or "application/gzip"
885 in cherrypy.request.headers["Content-Type"]
886 or "application/zip" in cherrypy.request.headers["Content-Type"]
887 or "text/plain" in cherrypy.request.headers["Content-Type"]
888 ):
tiernof27c79b2018-03-12 17:08:42 +0100889 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100890 elif (
891 "multipart/form-data"
892 in cherrypy.request.headers["Content-Type"]
893 ):
yshah53cc9eb2024-07-05 13:06:31 +0000894 if (
895 "descriptor_file" in kwargs
896 or "package" in kwargs
897 and "name" in kwargs
898 ):
899 filecontent = ""
900 if "descriptor_file" in kwargs:
901 filecontent = kwargs.pop("descriptor_file")
902 if "package" in kwargs:
903 filecontent = kwargs.pop("package")
tiernoc94c3df2018-02-09 15:38:54 +0100904 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100905 raise NbiException(
906 "empty file or content", HTTPStatus.BAD_REQUEST
907 )
yshah53cc9eb2024-07-05 13:06:31 +0000908 indata = filecontent
909 if filecontent.content_type.value:
910 cherrypy.request.headers[
911 "Content-Type"
912 ] = filecontent.content_type.value
913 elif "package" in kwargs:
914 filecontent = kwargs.pop("package")
915 if not filecontent.file:
916 raise NbiException(
917 "empty file or content", HTTPStatus.BAD_REQUEST
918 )
919 indata = filecontent
tiernoc94c3df2018-02-09 15:38:54 +0100920 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100921 cherrypy.request.headers[
922 "Content-Type"
923 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100924 else:
925 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
926 # "Only 'Content-Type' of type 'application/json' or
927 # 'application/yaml' for input format are available")
928 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100929 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100930 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100931 else:
932 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100933 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100934 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100935 if not indata:
936 indata = {}
tiernoc94c3df2018-02-09 15:38:54 +0100937 format_yaml = False
938 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
939 format_yaml = True
940
941 for k, v in kwargs.items():
942 if isinstance(v, str):
943 if v == "":
944 kwargs[k] = None
945 elif format_yaml:
946 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100947 kwargs[k] = yaml.safe_load(v)
tiernoe1281182018-05-22 12:24:36 +0200948 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100949 pass
garciadeblas4568a372021-03-24 09:19:48 +0100950 elif (
951 k.endswith(".gt")
952 or k.endswith(".lt")
953 or k.endswith(".gte")
954 or k.endswith(".lte")
955 ):
tiernoc94c3df2018-02-09 15:38:54 +0100956 try:
957 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200958 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100959 try:
960 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200961 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100962 pass
963 elif v.find(",") > 0:
964 kwargs[k] = v.split(",")
965 elif isinstance(v, (list, tuple)):
966 for index in range(0, len(v)):
967 if v[index] == "":
968 v[index] = None
969 elif format_yaml:
970 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100971 v[index] = yaml.safe_load(v[index])
tiernoe1281182018-05-22 12:24:36 +0200972 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100973 pass
974
tiernof27c79b2018-03-12 17:08:42 +0100975 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100976 except (ValueError, yaml.YAMLError) as exc:
977 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
978 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100979 raise NbiException(
980 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
981 )
tiernob92094f2018-05-11 13:44:22 +0200982 except Exception as exc:
983 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100984
985 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000986 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100987 """
988 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100989 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000990 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000991 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100992 :return: None
993 """
tierno0f98af52018-03-19 10:28:22 +0100994 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100995 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100996 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100997 return html.format(
998 data, cherrypy.request, cherrypy.response, token_info
999 )
tierno09c073e2018-04-26 13:36:48 +02001000 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +01001001 return
1002 elif hasattr(data, "read"): # file object
1003 if _format:
1004 cherrypy.response.headers["Content-Type"] = _format
1005 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +01001006 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +01001007 else:
garciadeblas4568a372021-03-24 09:19:48 +01001008 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001009 # TODO check that cherrypy close file. If not implement pending things to close per thread next
1010 return data
tierno0f98af52018-03-19 10:28:22 +01001011 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +00001012 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +01001013 return html.format(
1014 data, cherrypy.request, cherrypy.response, token_info
1015 )
Frank Brydenb5422da2020-08-10 11:44:11 +00001016 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +01001017 pass
garciadeblas4568a372021-03-24 09:19:48 +01001018 elif "application/json" in accept or (
1019 cherrypy.response.status and cherrypy.response.status >= 300
1020 ):
1021 cherrypy.response.headers[
1022 "Content-Type"
1023 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +00001024 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +01001025 return a.encode("utf8")
1026 cherrypy.response.headers["Content-Type"] = "application/yaml"
1027 return yaml.safe_dump(
1028 data,
1029 explicit_start=True,
1030 indent=4,
1031 default_flow_style=False,
1032 tags=False,
1033 encoding="utf-8",
1034 allow_unicode=True,
1035 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +01001036
1037 @cherrypy.expose
1038 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +00001039 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001040 try:
1041 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +00001042 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +01001043 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +01001044 else:
garciadeblas4568a372021-03-24 09:19:48 +01001045 raise cherrypy.HTTPError(
1046 HTTPStatus.METHOD_NOT_ALLOWED.value,
1047 "Method {} not allowed for tokens".format(cherrypy.request.method),
1048 )
tiernoc94c3df2018-02-09 15:38:54 +01001049
tierno701018c2019-06-25 11:13:14 +00001050 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001051
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001052 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +00001053 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +01001054 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +00001055 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001056
1057 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +02001058 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +02001059 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +02001060 try:
1061 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001062 raise NbiException(
1063 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
1064 )
tierno55945e72018-04-06 16:40:27 +02001065 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001066 raise NbiException(
1067 "Invalid URL or query string for version",
1068 HTTPStatus.METHOD_NOT_ALLOWED,
1069 )
tierno9c630112019-08-29 14:21:41 +00001070 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +00001071 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
1072 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +02001073 except NbiException as e:
1074 cherrypy.response.status = e.http_code.value
1075 problem_details = {
1076 "code": e.http_code.name,
1077 "status": e.http_code.value,
1078 "detail": str(e),
1079 }
1080 return self._format_out(problem_details, None)
1081
tierno12eac3c2020-03-19 23:22:08 +00001082 def domain(self):
1083 try:
1084 domains = {
garciadeblas4568a372021-03-24 09:19:48 +01001085 "user_domain_name": cherrypy.tree.apps["/osm"]
1086 .config["authentication"]
1087 .get("user_domain_name"),
1088 "project_domain_name": cherrypy.tree.apps["/osm"]
1089 .config["authentication"]
1090 .get("project_domain_name"),
1091 }
tierno12eac3c2020-03-19 23:22:08 +00001092 return self._format_out(domains)
1093 except NbiException as e:
1094 cherrypy.response.status = e.http_code.value
1095 problem_details = {
1096 "code": e.http_code.name,
1097 "status": e.http_code.value,
1098 "detail": str(e),
1099 }
1100 return self._format_out(problem_details, None)
1101
tiernoa5035702019-07-29 08:54:42 +00001102 @staticmethod
1103 def _format_login(token_info):
1104 """
1105 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
1106 log this information
1107 :param token_info: Dictionary with token content
1108 :return: None
1109 """
1110 cherrypy.request.login = token_info.get("username", "-")
1111 if token_info.get("project_name"):
1112 cherrypy.request.login += "/" + token_info["project_name"]
1113 if token_info.get("id"):
1114 cherrypy.request.login += ";session=" + token_info["id"][0:12]
1115
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001116 # NS Fault Management
1117 @cherrypy.expose
garciadeblasf2af4a12023-01-24 16:56:54 +01001118 def nsfm(
1119 self,
1120 version=None,
1121 topic=None,
1122 uuid=None,
1123 project_name=None,
1124 ns_id=None,
1125 *args,
garciadeblas57dddce2025-07-04 09:24:53 +02001126 **kwargs,
garciadeblasf2af4a12023-01-24 16:56:54 +01001127 ):
1128 if topic == "alarms":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001129 try:
1130 method = cherrypy.request.method
garciadeblasf2af4a12023-01-24 16:56:54 +01001131 role_permission = self._check_valid_url_method(
1132 method, "nsfm", version, topic, None, None, *args
1133 )
1134 query_string_operations = self._extract_query_string_operations(
1135 kwargs, method
1136 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001137
garciadeblasf2af4a12023-01-24 16:56:54 +01001138 self.authenticator.authorize(
1139 role_permission, query_string_operations, None
1140 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001141
1142 # to handle get request
garciadeblasf2af4a12023-01-24 16:56:54 +01001143 if cherrypy.request.method == "GET":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001144 # if request is on basis of uuid
garciadeblasf2af4a12023-01-24 16:56:54 +01001145 if uuid and uuid != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001146 try:
1147 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
garciadeblasf2af4a12023-01-24 16:56:54 +01001148 alarm_action = self.engine.db.get_one(
1149 "alarms_action", {"uuid": uuid}
1150 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001151 alarm.update(alarm_action)
garciadeblasf2af4a12023-01-24 16:56:54 +01001152 vnf = self.engine.db.get_one(
1153 "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}
1154 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001155 alarm["vnf-id"] = vnf["_id"]
1156 return self._format_out(str(alarm))
1157 except Exception:
1158 return self._format_out("Please provide valid alarm uuid")
garciadeblasf2af4a12023-01-24 16:56:54 +01001159 elif ns_id and ns_id != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001160 # if request is on basis of ns_id
1161 try:
garciadeblasf2af4a12023-01-24 16:56:54 +01001162 alarms = self.engine.db.get_list(
1163 "alarms", {"tags.ns_id": ns_id}
1164 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001165 for alarm in alarms:
garciadeblasf2af4a12023-01-24 16:56:54 +01001166 alarm_action = self.engine.db.get_one(
1167 "alarms_action", {"uuid": alarm["uuid"]}
1168 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001169 alarm.update(alarm_action)
1170 return self._format_out(str(alarms))
1171 except Exception:
1172 return self._format_out("Please provide valid ns id")
1173 else:
1174 # to return only alarm which are related to given project
garciadeblasf2af4a12023-01-24 16:56:54 +01001175 project = self.engine.db.get_one(
1176 "projects", {"name": project_name}
1177 )
1178 project_id = project.get("_id")
1179 ns_list = self.engine.db.get_list(
1180 "nsrs", {"_admin.projects_read": project_id}
1181 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001182 ns_ids = []
1183 for ns in ns_list:
1184 ns_ids.append(ns.get("_id"))
1185 alarms = self.engine.db.get_list("alarms")
garciadeblasf2af4a12023-01-24 16:56:54 +01001186 alarm_list = [
1187 alarm
1188 for alarm in alarms
1189 if alarm["tags"]["ns_id"] in ns_ids
1190 ]
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001191 for alrm in alarm_list:
garciadeblasf2af4a12023-01-24 16:56:54 +01001192 action = self.engine.db.get_one(
1193 "alarms_action", {"uuid": alrm.get("uuid")}
1194 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001195 alrm.update(action)
1196 return self._format_out(str(alarm_list))
1197 # to handle patch request for alarm update
garciadeblasf2af4a12023-01-24 16:56:54 +01001198 elif cherrypy.request.method == "PATCH":
garciadeblas4cd875d2023-02-14 19:05:34 +01001199 data = yaml.safe_load(cherrypy.request.body)
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001200 try:
1201 # check if uuid is valid
1202 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
1203 except Exception:
1204 return self._format_out("Please provide valid alarm uuid.")
1205 if data.get("is_enable") is not None:
1206 if data.get("is_enable"):
garciadeblasf2af4a12023-01-24 16:56:54 +01001207 alarm_status = "ok"
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001208 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001209 alarm_status = "disabled"
1210 self.engine.db.set_one(
1211 "alarms",
1212 {"uuid": data.get("uuid")},
1213 {"alarm_status": alarm_status},
1214 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001215 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001216 self.engine.db.set_one(
1217 "alarms",
1218 {"uuid": data.get("uuid")},
1219 {"threshold": data.get("threshold")},
1220 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001221 return self._format_out("Alarm updated")
1222 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01001223 if isinstance(
1224 e,
1225 (
1226 NbiException,
1227 EngineException,
1228 DbException,
1229 FsException,
1230 MsgException,
1231 AuthException,
1232 ValidationError,
1233 AuthconnException,
1234 ),
1235 ):
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001236 http_code_value = cherrypy.response.status = e.http_code.value
1237 http_code_name = e.http_code.name
1238 cherrypy.log("Exception {}".format(e))
1239 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001240 http_code_value = (
1241 cherrypy.response.status
1242 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001243 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
1244 http_code_name = HTTPStatus.BAD_REQUEST.name
1245 problem_details = {
1246 "code": http_code_name,
1247 "status": http_code_value,
1248 "detail": str(e),
1249 }
1250 return self._format_out(problem_details)
1251
tierno55945e72018-04-06 16:40:27 +02001252 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +01001253 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +00001254 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001255 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +01001256 indata = self._format_in(kwargs)
1257 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001258 raise NbiException(
1259 "Expected application/yaml or application/json Content-Type",
1260 HTTPStatus.BAD_REQUEST,
1261 )
tiernoa5035702019-07-29 08:54:42 +00001262
1263 if method == "GET":
1264 token_info = self.authenticator.authorize()
1265 # for logging
1266 self._format_login(token_info)
1267 if token_id:
1268 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +01001269 else:
tiernoa5035702019-07-29 08:54:42 +00001270 outdata = self.authenticator.get_token_list(token_info)
1271 elif method == "POST":
1272 try:
1273 token_info = self.authenticator.authorize()
1274 except Exception:
1275 token_info = None
1276 if kwargs:
1277 indata.update(kwargs)
1278 # This is needed to log the user when authentication fails
1279 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +01001280 outdata = token_info = self.authenticator.new_token(
1281 token_info, indata, cherrypy.request.remote
1282 )
jeganbe1a3df2024-06-04 12:05:19 +00001283 if outdata.get("email") or outdata.get("otp") == "invalid":
1284 return self._format_out(outdata, token_info)
garciadeblasf2af4a12023-01-24 16:56:54 +01001285 cherrypy.session["Authorization"] = outdata["_id"] # pylint: disable=E1101
tiernoa5035702019-07-29 08:54:42 +00001286 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1287 # for logging
1288 self._format_login(token_info)
jeganbe1a3df2024-06-04 12:05:19 +00001289 if outdata.get("otp") == "valid":
1290 outdata = {
1291 "id": outdata["id"],
1292 "message": "valid_otp",
1293 "user_id": outdata["user_id"],
1294 }
selvi.ja9a1fc82022-04-04 06:54:30 +00001295 # password expiry check
jeganbe1a3df2024-06-04 12:05:19 +00001296 elif self.authenticator.check_password_expiry(outdata):
garciadeblasf2af4a12023-01-24 16:56:54 +01001297 outdata = {
1298 "id": outdata["id"],
1299 "message": "change_password",
1300 "user_id": outdata["user_id"],
1301 }
tiernoa5035702019-07-29 08:54:42 +00001302 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1303 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
elumalai7802ff82023-04-24 20:38:32 +05301304 cef_event(
1305 cef_logger,
1306 {
1307 "name": "User Login",
1308 "sourceUserName": token_info.get("username"),
1309 "message": "User Logged In, Project={} Outcome=Success".format(
1310 token_info.get("project_name")
1311 ),
1312 },
1313 )
1314 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001315 elif method == "DELETE":
1316 if not token_id and "id" in kwargs:
1317 token_id = kwargs["id"]
1318 elif not token_id:
1319 token_info = self.authenticator.authorize()
1320 # for logging
1321 self._format_login(token_info)
1322 token_id = token_info["_id"]
Rahulc72bc8e2023-12-05 11:54:38 +00001323 if current_backend != "keystone":
1324 token_details = self.engine.db.get_one("tokens", {"_id": token_id})
1325 current_user = token_details.get("username")
1326 current_project = token_details.get("project_name")
1327 else:
1328 current_user = "keystone backend"
1329 current_project = "keystone backend"
tiernoa5035702019-07-29 08:54:42 +00001330 outdata = self.authenticator.del_token(token_id)
1331 token_info = None
garciadeblasf2af4a12023-01-24 16:56:54 +01001332 cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101
elumalai7802ff82023-04-24 20:38:32 +05301333 cef_event(
1334 cef_logger,
1335 {
1336 "name": "User Logout",
1337 "sourceUserName": current_user,
1338 "message": "User Logged Out, Project={} Outcome=Success".format(
1339 current_project
1340 ),
1341 },
1342 )
1343 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001344 # cherrypy.response.cookie["Authorization"] = token_id
1345 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1346 else:
garciadeblas4568a372021-03-24 09:19:48 +01001347 raise NbiException(
1348 "Method {} not allowed for token".format(method),
1349 HTTPStatus.METHOD_NOT_ALLOWED,
1350 )
tiernoa5035702019-07-29 08:54:42 +00001351 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001352
1353 @cherrypy.expose
1354 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001355 if not cherrypy.config.get("server.enable_test") or (
1356 isinstance(cherrypy.config["server.enable_test"], str)
1357 and cherrypy.config["server.enable_test"].lower() == "false"
1358 ):
tierno4836bac2020-01-15 14:41:48 +00001359 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1360 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001361 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001362 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001363 return (
1364 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1365 "sleep/<time>\nmessage/topic\n</pre></html>"
1366 )
tiernof27c79b2018-03-12 17:08:42 +01001367
1368 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001369 try:
1370 # self.engine.load_dbase(cherrypy.request.app.config)
garciadeblasf2af4a12023-01-24 16:56:54 +01001371 pid = self.authenticator.create_admin_project()
1372 self.authenticator.create_admin_user(pid)
tiernoc94c3df2018-02-09 15:38:54 +01001373 return "Done. User 'admin', password 'admin' created"
1374 except Exception:
1375 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1376 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001377 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001378 return cherrypy.lib.static.serve_file(
1379 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1380 "text/plain",
1381 "attachment",
1382 )
tiernof27c79b2018-03-12 17:08:42 +01001383 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001384 f_path = (
1385 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1386 )
tiernof27c79b2018-03-12 17:08:42 +01001387 f = open(f_path, "r")
1388 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001389 return f
tierno55945e72018-04-06 16:40:27 +02001390
tiernof27c79b2018-03-12 17:08:42 +01001391 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001392 deleted_info = self.engine.db.del_list(args[1], kwargs)
1393 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1394 elif len(args) and args[0] == "fs-clear":
1395 if len(args) >= 2:
1396 folders = (args[1],)
1397 else:
1398 folders = self.engine.fs.dir_ls(".")
1399 for folder in folders:
1400 self.engine.fs.file_delete(folder)
1401 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001402 elif args and args[0] == "login":
1403 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001404 cherrypy.response.headers[
1405 "WWW-Authenticate"
1406 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001407 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1408 elif args and args[0] == "login2":
1409 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001410 cherrypy.response.headers[
1411 "WWW-Authenticate"
1412 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001413 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1414 elif args and args[0] == "sleep":
1415 sleep_time = 5
1416 try:
1417 sleep_time = int(args[1])
1418 except Exception:
1419 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1420 return self._format_out("Database already initialized")
1421 thread_info = cherrypy.thread_data
1422 print(thread_info)
1423 time.sleep(sleep_time)
1424 # thread_info
1425 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001426 main_topic = args[1]
1427 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001428 try:
garciadeblas4568a372021-03-24 09:19:48 +01001429 if cherrypy.request.method == "POST":
garciadeblas4cd875d2023-02-14 19:05:34 +01001430 to_send = yaml.safe_load(cherrypy.request.body)
tierno55945e72018-04-06 16:40:27 +02001431 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001432 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001433 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001434 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001435 for k, v in kwargs.items():
garciadeblas4cd875d2023-02-14 19:05:34 +01001436 v_dict = yaml.safe_load(v)
tiernof1509b22020-05-12 14:32:37 +00001437 self.engine.msg.write(main_topic, k, v_dict)
1438 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001439 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001440 return_text += "Error: " + str(e)
1441 return_text += "</pre></html>\n"
1442 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001443
1444 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001445 "<html><pre>\nheaders:\n args: {}\n".format(args)
1446 + " kwargs: {}\n".format(kwargs)
1447 + " headers: {}\n".format(cherrypy.request.headers)
1448 + " path_info: {}\n".format(cherrypy.request.path_info)
1449 + " query_string: {}\n".format(cherrypy.request.query_string)
garciadeblasf2af4a12023-01-24 16:56:54 +01001450 + " session: {}\n".format(cherrypy.session) # pylint: disable=E1101
garciadeblas4568a372021-03-24 09:19:48 +01001451 + " cookie: {}\n".format(cherrypy.request.cookie)
1452 + " method: {}\n".format(cherrypy.request.method)
garciadeblasf2af4a12023-01-24 16:56:54 +01001453 + " session: {}\n".format(
1454 cherrypy.session.get("fieldname") # pylint: disable=E1101
1455 )
garciadeblas4568a372021-03-24 09:19:48 +01001456 + " body:\n"
1457 )
tiernoc94c3df2018-02-09 15:38:54 +01001458 return_text += " length: {}\n".format(cherrypy.request.body.length)
1459 if cherrypy.request.body.length:
1460 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001461 str(
1462 cherrypy.request.body.read(
1463 int(cherrypy.request.headers.get("Content-Length", 0))
1464 )
1465 )
1466 )
tiernoc94c3df2018-02-09 15:38:54 +01001467 if thread_info:
1468 return_text += "thread: {}\n".format(thread_info)
1469 return_text += "</pre></html>"
1470 return return_text
1471
tierno701018c2019-06-25 11:13:14 +00001472 @staticmethod
1473 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001474 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001475 raise NbiException(
1476 "URL must contain at least 'main_topic/version/topic'",
1477 HTTPStatus.METHOD_NOT_ALLOWED,
1478 )
tiernof27c79b2018-03-12 17:08:42 +01001479
tierno701018c2019-06-25 11:13:14 +00001480 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001481 for arg in args:
1482 if arg is None:
1483 break
1484 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001485 raise NbiException(
1486 "URL contains unexpected extra items '{}'".format(arg),
1487 HTTPStatus.METHOD_NOT_ALLOWED,
1488 )
tiernof27c79b2018-03-12 17:08:42 +01001489
1490 if arg in reference:
1491 reference = reference[arg]
1492 elif "<ID>" in reference:
1493 reference = reference["<ID>"]
1494 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001495 # if there is content
1496 if reference["*"]:
1497 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001498 break
1499 else:
garciadeblas4568a372021-03-24 09:19:48 +01001500 raise NbiException(
1501 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1502 )
tiernof27c79b2018-03-12 17:08:42 +01001503 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001504 raise NbiException(
1505 "Method {} not supported yet for this URL".format(method),
1506 HTTPStatus.NOT_IMPLEMENTED,
1507 )
tierno2236d202018-05-16 19:05:16 +02001508 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001509 raise NbiException(
1510 "Method {} not supported for this URL".format(method),
1511 HTTPStatus.METHOD_NOT_ALLOWED,
1512 )
tierno701018c2019-06-25 11:13:14 +00001513 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001514
1515 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001516 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001517 """
1518 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001519 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001520 :param version:
tiernob24258a2018-10-04 18:39:49 +02001521 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001522 :param id:
1523 :return: None
1524 """
1525 # 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 +01001526 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1527 main_topic, version, topic, id
1528 )
tiernof27c79b2018-03-12 17:08:42 +01001529 return
1530
tierno65ca36d2019-02-12 19:27:52 +01001531 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001532 def _extract_query_string_operations(kwargs, method):
1533 """
1534
1535 :param kwargs:
1536 :return:
1537 """
1538 query_string_operations = []
1539 if kwargs:
1540 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1541 if qs in kwargs and kwargs[qs].lower() != "false":
1542 query_string_operations.append(qs.lower() + ":" + method.lower())
1543 return query_string_operations
1544
1545 @staticmethod
1546 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001547 """
1548 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1549 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001550 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001551 :param kwargs: query string input.
1552 :param method: http method: GET, POSST, PUT, ...
1553 :param _id:
1554 :return: admin_query dictionary with keys:
1555 public: True, False or None
1556 force: True or False
1557 project_id: tuple with projects used for accessing an element
1558 set_project: tuple with projects that a created element will belong to
1559 method: show, list, delete, write
1560 """
garciadeblas4568a372021-03-24 09:19:48 +01001561 admin_query = {
1562 "force": False,
1563 "project_id": (token_info["project_id"],),
1564 "username": token_info["username"],
Adurti76d4b762024-05-07 06:04:37 +00001565 "user_id": token_info["user_id"],
garciadeblas4568a372021-03-24 09:19:48 +01001566 "admin": token_info["admin"],
37177091c0322024-11-01 08:55:59 +00001567 "admin_show": token_info["admin_show"],
garciadeblas4568a372021-03-24 09:19:48 +01001568 "public": None,
1569 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1570 }
tierno65ca36d2019-02-12 19:27:52 +01001571 if kwargs:
1572 # FORCE
1573 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001574 if (
1575 kwargs["FORCE"].lower() != "false"
1576 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001577 admin_query["force"] = True
1578 del kwargs["FORCE"]
1579 # PUBLIC
1580 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001581 if (
1582 kwargs["PUBLIC"].lower() != "false"
1583 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001584 admin_query["public"] = True
1585 else:
1586 admin_query["public"] = False
1587 del kwargs["PUBLIC"]
1588 # ADMIN
1589 if "ADMIN" in kwargs:
1590 behave_as = kwargs.pop("ADMIN")
1591 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001592 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001593 raise NbiException(
1594 "Only admin projects can use 'ADMIN' query string",
1595 HTTPStatus.UNAUTHORIZED,
1596 )
1597 if (
1598 not behave_as or behave_as.lower() == "true"
1599 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001600 admin_query["project_id"] = ()
1601 elif isinstance(behave_as, (list, tuple)):
1602 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001603 else: # isinstance(behave_as, str)
1604 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001605 if "SET_PROJECT" in kwargs:
1606 set_project = kwargs.pop("SET_PROJECT")
1607 if not set_project:
1608 admin_query["set_project"] = list(admin_query["project_id"])
1609 else:
1610 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001611 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001612 if admin_query["project_id"]:
1613 for p in set_project:
1614 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001615 raise NbiException(
1616 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1617 "'ADMIN='{p}'".format(p=p),
1618 HTTPStatus.UNAUTHORIZED,
1619 )
tierno65ca36d2019-02-12 19:27:52 +01001620 admin_query["set_project"] = set_project
1621
1622 # PROJECT_READ
1623 # if "PROJECT_READ" in kwargs:
1624 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001625 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001626 if method == "GET":
1627 if _id:
1628 admin_query["method"] = "show"
1629 else:
1630 admin_query["method"] = "list"
1631 elif method == "DELETE":
1632 admin_query["method"] = "delete"
1633 else:
1634 admin_query["method"] = "write"
1635 return admin_query
1636
tiernoc94c3df2018-02-09 15:38:54 +01001637 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001638 def default(
1639 self,
1640 main_topic=None,
1641 version=None,
1642 topic=None,
1643 _id=None,
1644 item=None,
1645 *args,
garciadeblas57dddce2025-07-04 09:24:53 +02001646 **kwargs,
garciadeblas4568a372021-03-24 09:19:48 +01001647 ):
tierno701018c2019-06-25 11:13:14 +00001648 token_info = None
selvi.j9919bbc2023-04-26 12:22:13 +00001649 outdata = {}
tiernof27c79b2018-03-12 17:08:42 +01001650 _format = None
tierno0f98af52018-03-19 10:28:22 +01001651 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001652 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001653 rollback = []
tierno701018c2019-06-25 11:13:14 +00001654 engine_session = None
elumalai7802ff82023-04-24 20:38:32 +05301655 url_id = ""
1656 log_mapping = {
1657 "POST": "Creating",
1658 "GET": "Fetching",
1659 "DELETE": "Deleting",
1660 "PUT": "Updating",
1661 "PATCH": "Updating",
1662 }
tiernoc94c3df2018-02-09 15:38:54 +01001663 try:
tiernob24258a2018-10-04 18:39:49 +02001664 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001665 raise NbiException(
1666 "URL must contain at least 'main_topic/version/topic'",
1667 HTTPStatus.METHOD_NOT_ALLOWED,
1668 )
1669 if main_topic not in (
1670 "admin",
1671 "vnfpkgm",
1672 "nsd",
1673 "nslcm",
1674 "pdu",
1675 "nst",
1676 "nsilcm",
1677 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001678 "vnflcm",
rshri2d386cb2024-07-05 14:35:51 +00001679 "k8scluster",
yshah53cc9eb2024-07-05 13:06:31 +00001680 "ksu",
garciadeblasb798f452025-08-05 18:21:26 +02001681 "appinstance",
yshah53cc9eb2024-07-05 13:06:31 +00001682 "oka",
garciadeblas4568a372021-03-24 09:19:48 +01001683 ):
1684 raise NbiException(
1685 "URL main_topic '{}' not supported".format(main_topic),
1686 HTTPStatus.METHOD_NOT_ALLOWED,
1687 )
1688 if version != "v1":
1689 raise NbiException(
1690 "URL version '{}' not supported".format(version),
1691 HTTPStatus.METHOD_NOT_ALLOWED,
1692 )
elumalai7802ff82023-04-24 20:38:32 +05301693 if _id is not None:
1694 url_id = _id
tiernoc94c3df2018-02-09 15:38:54 +01001695
garciadeblas4568a372021-03-24 09:19:48 +01001696 if (
1697 kwargs
1698 and "METHOD" in kwargs
1699 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1700 ):
tiernof27c79b2018-03-12 17:08:42 +01001701 method = kwargs.pop("METHOD")
1702 else:
1703 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001704
garciadeblas4568a372021-03-24 09:19:48 +01001705 role_permission = self._check_valid_url_method(
1706 method, main_topic, version, topic, _id, item, *args
1707 )
1708 query_string_operations = self._extract_query_string_operations(
1709 kwargs, method
1710 )
tiernob24258a2018-10-04 18:39:49 +02001711 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001712 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001713 token_info = self.authenticator.authorize(
1714 role_permission, query_string_operations, _id
1715 )
tierno12eac3c2020-03-19 23:22:08 +00001716 if main_topic == "admin" and topic == "domains":
1717 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001718 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001719 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001720 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301721
vijay.r35ef2f72019-04-30 17:55:49 +05301722 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001723 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001724
tiernob24258a2018-10-04 18:39:49 +02001725 if main_topic == "nsd":
1726 engine_topic = "nsds"
kayal2001f71c2e82024-06-25 15:26:24 +05301727 if topic == "ns_config_template":
1728 engine_topic = "nsconfigtemps"
tiernob24258a2018-10-04 18:39:49 +02001729 elif main_topic == "vnfpkgm":
1730 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001731 if topic == "vnfpkg_op_occs":
1732 engine_topic = "vnfpkgops"
1733 if topic == "vnf_packages" and item == "action":
1734 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001735 elif main_topic == "nslcm":
1736 engine_topic = "nsrs"
1737 if topic == "ns_lcm_op_occs":
1738 engine_topic = "nslcmops"
1739 if topic == "vnfrs" or topic == "vnf_instances":
1740 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001741 elif main_topic == "vnflcm":
1742 if topic == "vnf_lcm_op_occs":
1743 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001744 elif main_topic == "nst":
1745 engine_topic = "nsts"
1746 elif main_topic == "nsilcm":
1747 engine_topic = "nsis"
1748 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001749 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001750 elif main_topic == "pdu":
1751 engine_topic = "pdus"
rshri2d386cb2024-07-05 14:35:51 +00001752 elif main_topic == "k8scluster":
shrinithi28d887f2025-01-08 05:27:19 +00001753 engine_topic = "cluster"
rshri2d386cb2024-07-05 14:35:51 +00001754 if topic == "clusters" and _id == "register" or item == "deregister":
shrinithi28d887f2025-01-08 05:27:19 +00001755 engine_topic = "clusterops"
rshri2d386cb2024-07-05 14:35:51 +00001756 elif topic == "infra_controller_profiles":
1757 engine_topic = "infras_cont"
1758 elif topic == "infra_config_profiles":
1759 engine_topic = "infras_conf"
1760 elif topic == "resource_profiles":
1761 engine_topic = "resources"
1762 elif topic == "app_profiles":
1763 engine_topic = "apps"
garciadeblas1fbe71a2025-08-05 09:36:10 +02001764 elif topic == "clusters" and item == "nodegroup":
yshahd23c6a52025-06-13 05:49:31 +00001765 engine_topic = "node_groups"
yshah53cc9eb2024-07-05 13:06:31 +00001766 elif main_topic == "ksu" and engine_topic in ("ksus", "clone", "move"):
1767 engine_topic = "ksus"
garciadeblasb798f452025-08-05 18:21:26 +02001768 elif main_topic == "appinstance":
1769 engine_topic = "appinstances"
garciadeblas4568a372021-03-24 09:19:48 +01001770 if (
1771 engine_topic == "vims"
1772 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001773 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001774
preethika.p329b8182020-04-22 12:25:39 +05301775 if topic == "subscriptions":
1776 engine_topic = main_topic + "_" + topic
1777
tiernoc94c3df2018-02-09 15:38:54 +01001778 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001779 if item in (
1780 "nsd_content",
1781 "package_content",
1782 "artifacts",
1783 "vnfd",
1784 "nsd",
1785 "nst",
1786 "nst_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301787 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001788 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001789 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001790 path = "$DESCRIPTOR"
1791 elif args:
1792 path = args
tiernob24258a2018-10-04 18:39:49 +02001793 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001794 path = ()
1795 else:
1796 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001797 file, _format = self.engine.get_file(
1798 engine_session,
1799 engine_topic,
1800 _id,
1801 path,
1802 cherrypy.request.headers.get("Accept"),
1803 )
tiernof27c79b2018-03-12 17:08:42 +01001804 outdata = file
shrinithi28d887f2025-01-08 05:27:19 +00001805 # elif not _id and topic != "clusters":
1806 # outdata = self.engine.get_item_list(
1807 # engine_session, engine_topic, kwargs, api_req=True
1808 # )
rshri2d386cb2024-07-05 14:35:51 +00001809 elif topic == "clusters" and item in (
1810 "infra_controller_profiles",
1811 "infra_config_profiles",
1812 "app_profiles",
1813 "resource_profiles",
1814 ):
1815 profile = item
1816 filter_q = None
1817 outdata = self.engine.get_one_item(
1818 engine_session,
1819 engine_topic,
1820 _id,
1821 profile,
1822 filter_q,
1823 api_req=True,
1824 )
shahithyab9eb4142024-10-17 05:51:39 +00001825 elif (
1826 topic == "clusters"
1827 and item == "get_creds_file"
1828 or item == "get_creds"
1829 ):
1830 if item == "get_creds_file":
1831 op_id = args[0]
1832 file, _format = self.engine.get_cluster_creds_file(
1833 engine_session, engine_topic, _id, item, op_id
1834 )
1835 outdata = file
1836 if item == "get_creds":
1837 op_id = self.engine.get_cluster_creds(
1838 engine_session, engine_topic, _id, item
1839 )
1840 outdata = {"op_id": op_id}
shrinithi28d887f2025-01-08 05:27:19 +00001841 elif topic == "clusters" and not _id:
1842 outdata = self.engine.get_item_list_cluster(
1843 engine_session, engine_topic, kwargs, api_req=True
1844 )
yshahd23c6a52025-06-13 05:49:31 +00001845 elif topic == "clusters" and item == "nodegroup" and args:
1846 _id = args[0]
1847 outdata = outdata = self.engine.get_item(
1848 engine_session, engine_topic, _id, kwargs, True
1849 )
1850 elif topic == "clusters" and item == "nodegroup":
1851 kwargs["cluster_id"] = _id
1852 outdata = self.engine.get_item_list(
1853 engine_session, engine_topic, kwargs, api_req=True
1854 )
1855 elif topic == "clusters" and item == "ksus":
1856 engine_topic = "ksus"
1857 kwargs["cluster_id"] = _id
1858 outdata = self.engine.get_cluster_list_ksu(
1859 engine_session, engine_topic, kwargs, api_req=True
1860 )
shrinithi28d887f2025-01-08 05:27:19 +00001861 elif not _id:
1862 outdata = self.engine.get_item_list(
1863 engine_session, engine_topic, kwargs, api_req=True
1864 )
tiernoc94c3df2018-02-09 15:38:54 +01001865 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301866 if item == "reports":
1867 # TODO check that project_id (_id in this context) has permissions
1868 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301869 filter_q = None
1870 if "vcaStatusRefresh" in kwargs:
1871 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
garciadeblasf2af4a12023-01-24 16:56:54 +01001872 outdata = self.engine.get_item(
1873 engine_session, engine_topic, _id, filter_q, True
1874 )
delacruzramo271d2002019-12-02 21:00:37 +01001875
tiernof27c79b2018-03-12 17:08:42 +01001876 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001877 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001878 if topic in (
1879 "ns_descriptors_content",
1880 "vnf_packages_content",
1881 "netslice_templates_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301882 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001883 ):
tiernof27c79b2018-03-12 17:08:42 +01001884 _id = cherrypy.request.headers.get("Transaction-Id")
yshah53cc9eb2024-07-05 13:06:31 +00001885
tiernof27c79b2018-03-12 17:08:42 +01001886 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001887 _id, _ = self.engine.new_item(
1888 rollback,
1889 engine_session,
1890 engine_topic,
1891 {},
1892 None,
1893 cherrypy.request.headers,
1894 )
1895 completed = self.engine.upload_content(
1896 engine_session,
1897 engine_topic,
1898 _id,
1899 indata,
1900 kwargs,
1901 cherrypy.request.headers,
1902 )
tiernof27c79b2018-03-12 17:08:42 +01001903 if completed:
tiernob24258a2018-10-04 18:39:49 +02001904 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001905 else:
1906 cherrypy.response.headers["Transaction-Id"] = _id
1907 outdata = {"id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001908 elif topic == "oka_packages":
1909 _id = cherrypy.request.headers.get("Transaction-Id")
1910
1911 if not _id:
1912 _id, _ = self.engine.new_item(
1913 rollback,
1914 engine_session,
1915 engine_topic,
1916 {},
1917 kwargs,
1918 cherrypy.request.headers,
1919 )
1920 cherrypy.request.headers["method"] = cherrypy.request.method
1921 if indata:
1922 completed = self.engine.upload_content(
1923 engine_session,
1924 engine_topic,
1925 _id,
1926 indata,
1927 None,
1928 cherrypy.request.headers,
1929 )
1930 if completed:
1931 self._set_location_header(main_topic, version, topic, _id)
1932 else:
1933 cherrypy.response.headers["Transaction-Id"] = _id
yshahe08aff12024-11-07 09:32:22 +00001934 outdata = {"_id": _id, "id": _id}
tiernob24258a2018-10-04 18:39:49 +02001935 elif topic == "ns_instances_content":
1936 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001937 _id, _ = self.engine.new_item(
1938 rollback, engine_session, engine_topic, indata, kwargs
1939 )
tiernob24258a2018-10-04 18:39:49 +02001940 # creates nslcmop
1941 indata["lcmOperationType"] = "instantiate"
1942 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001943 nslcmop_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001944 rollback, engine_session, "nslcmops", indata, None
1945 )
tiernob24258a2018-10-04 18:39:49 +02001946 self._set_location_header(main_topic, version, topic, _id)
garciadeblasf53612b2024-07-12 14:44:37 +02001947 outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName}
adurti3af50952024-05-31 11:36:57 +05301948 elif topic == "ns_instances_terminate":
1949 if indata.get("ns_ids"):
1950 for ns_id in indata.get("ns_ids"):
1951 nslcmop_desc = {
1952 "lcmOperationType": "terminate",
1953 "nsInstanceId": ns_id,
shahithyab9eb4142024-10-17 05:51:39 +00001954 "autoremove": (
1955 indata.get("autoremove")
1956 if "autoremove" in indata
1957 else True
1958 ),
adurti3af50952024-05-31 11:36:57 +05301959 }
1960 op_id, _, _ = self.engine.new_item(
1961 rollback,
1962 engine_session,
1963 "nslcmops",
1964 nslcmop_desc,
1965 kwargs,
1966 )
1967 if not op_id:
1968 _ = self.engine.del_item(
1969 engine_session, engine_topic, ns_id
1970 )
1971 outdata = {"ns_ids": indata.get("ns_ids")}
1972 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001973 elif topic == "ns_instances" and item:
1974 indata["lcmOperationType"] = item
1975 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001976 _id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001977 rollback, engine_session, "nslcmops", indata, kwargs
1978 )
1979 self._set_location_header(
1980 main_topic, version, "ns_lcm_op_occs", _id
1981 )
garciadeblasf53612b2024-07-12 14:44:37 +02001982 outdata = {"id": _id, "nsName": nsName}
tierno65acb4d2018-04-06 16:42:40 +02001983 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001984 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001985 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001986 _id, _ = self.engine.new_item(
1987 rollback, engine_session, engine_topic, indata, kwargs
1988 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001989 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001990 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001991 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001992 nsilcmop_id, _ = self.engine.new_item(
1993 rollback, engine_session, "nsilcmops", indata, kwargs
1994 )
kuuse078f55e2019-05-16 19:24:21 +02001995 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001996 elif topic == "netslice_instances" and item:
1997 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001998 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001999 _id, _ = self.engine.new_item(
2000 rollback, engine_session, "nsilcmops", indata, kwargs
2001 )
2002 self._set_location_header(
2003 main_topic, version, "nsi_lcm_op_occs", _id
2004 )
garciadeblas9750c5a2018-10-15 16:20:35 +02002005 outdata = {"id": _id}
2006 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01002007 elif topic == "vnf_packages" and item == "action":
2008 indata["lcmOperationType"] = item
2009 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01002010 _id, _ = self.engine.new_item(
2011 rollback, engine_session, "vnfpkgops", indata, kwargs
2012 )
2013 self._set_location_header(
2014 main_topic, version, "vnfpkg_op_occs", _id
2015 )
delacruzramo271d2002019-12-02 21:00:37 +01002016 outdata = {"id": _id}
2017 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05302018 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01002019 _id, _ = self.engine.new_item(
2020 rollback, engine_session, engine_topic, indata, kwargs
2021 )
preethika.p329b8182020-04-22 12:25:39 +05302022 self._set_location_header(main_topic, version, topic, _id)
2023 link = {}
2024 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01002025 outdata = {
2026 "id": _id,
2027 "filter": indata["filter"],
2028 "callbackUri": indata["CallbackUri"],
2029 "_links": link,
2030 }
preethika.p329b8182020-04-22 12:25:39 +05302031 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02002032 elif topic == "vnf_instances" and item:
2033 indata["lcmOperationType"] = item
2034 indata["vnfInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02002035 _id, nsName, _ = self.engine.new_item(
garciadeblasf2af4a12023-01-24 16:56:54 +01002036 rollback, engine_session, "vnflcmops", indata, kwargs
2037 )
2038 self._set_location_header(
2039 main_topic, version, "vnf_lcm_op_occs", _id
2040 )
garciadeblasf53612b2024-07-12 14:44:37 +02002041 outdata = {"id": _id, "nsName": nsName}
almagiae47b9132022-05-17 14:12:22 +02002042 cherrypy.response.status = HTTPStatus.ACCEPTED.value
Gabriel Cuba84a60df2023-10-30 14:01:54 -05002043 elif topic == "ns_lcm_op_occs" and item == "cancel":
2044 indata["nsLcmOpOccId"] = _id
2045 self.engine.cancel_item(
2046 rollback, engine_session, "nslcmops", indata, None
2047 )
2048 self._set_location_header(main_topic, version, topic, _id)
2049 cherrypy.response.status = HTTPStatus.ACCEPTED.value
rshri2d386cb2024-07-05 14:35:51 +00002050 elif topic == "clusters" and _id == "register":
2051 # To register a cluster
2052 _id, _ = self.engine.add_item(
2053 rollback, engine_session, engine_topic, indata, kwargs
2054 )
2055 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002056 outdata = {"_id": _id, "id": _id}
rshri2d386cb2024-07-05 14:35:51 +00002057 elif (
2058 topic
2059 in (
2060 "clusters",
2061 "infra_controller_profiles",
2062 "infra_config_profiles",
2063 "app_profiles",
2064 "resource_profiles",
2065 )
2066 and item is None
2067 ):
2068 # creates cluster, infra_controller_profiles, app_profiles, infra_config_profiles, and resource_profiles
2069 _id, _ = self.engine.new_item(
2070 rollback, engine_session, engine_topic, indata, kwargs
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 elif topic == "ksus" and item:
2075 if item == "clone":
2076 _id = self.engine.clone(
2077 rollback,
2078 engine_session,
2079 engine_topic,
2080 _id,
2081 indata,
2082 kwargs,
2083 cherrypy.request.headers,
2084 )
2085 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002086 outdata = {"_id": _id, "id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00002087 if item == "move":
2088 op_id = self.engine.move_ksu(
2089 engine_session, engine_topic, _id, indata, kwargs
2090 )
2091 outdata = {"op_id": op_id}
2092 elif topic == "ksus" and _id == "delete":
2093 op_id = self.engine.delete_ksu(
2094 engine_session, engine_topic, _id, indata
2095 )
2096 outdata = {"op_id": op_id}
2097 elif topic == "ksus" and _id == "update":
2098 op_id = self.engine.edit_item(
2099 engine_session, engine_topic, _id, indata, kwargs
2100 )
2101 outdata = {"op_id": op_id}
garciadeblasb798f452025-08-05 18:21:26 +02002102 elif topic == "appinstances" and item == "update":
2103 op_id = self.engine.update_appinstance(
2104 engine_session, engine_topic, _id, indata, kwargs
2105 )
2106 outdata = {"op_id": op_id}
garciadeblasffce2a42025-03-21 11:39:39 +01002107 elif topic == "clusters" and item in ("upgrade", "scale"):
yshahd23c6a52025-06-13 05:49:31 +00002108 op_id = self.engine.update_item(
yshah53cc9eb2024-07-05 13:06:31 +00002109 engine_session, engine_topic, _id, item, indata
2110 )
2111 outdata = {"op_id": op_id}
yshahd23c6a52025-06-13 05:49:31 +00002112 elif topic == "clusters" and item == "nodegroup":
2113 indata["cluster_id"] = _id
2114 if args:
2115 _id = args[0]
2116 op_id = self.engine.update_item(
2117 engine_session, engine_topic, _id, item, indata
2118 )
2119 outdata = {"op_id": op_id}
2120 else:
2121 _id, _ = self.engine.new_item(
2122 rollback, engine_session, engine_topic, indata, kwargs
2123 )
2124 self._set_location_header(main_topic, version, topic, _id)
2125 outdata = {"_id": _id, "id": _id}
tiernof27c79b2018-03-12 17:08:42 +01002126 else:
garciadeblasbe6498e2025-11-05 11:55:37 +01002127 self.logger.debug(
2128 "Creating new item in topic {}".format(engine_topic)
2129 )
garciadeblas4568a372021-03-24 09:19:48 +01002130 _id, op_id = self.engine.new_item(
2131 rollback,
2132 engine_session,
2133 engine_topic,
2134 indata,
2135 kwargs,
2136 cherrypy.request.headers,
2137 )
tiernob24258a2018-10-04 18:39:49 +02002138 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002139 outdata = {"_id": _id, "id": _id}
tiernobdebce92019-07-01 15:36:49 +00002140 if op_id:
2141 outdata["op_id"] = op_id
2142 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02002143 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02002144
tiernoc94c3df2018-02-09 15:38:54 +01002145 elif method == "DELETE":
2146 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01002147 outdata = self.engine.del_item_list(
2148 engine_session, engine_topic, kwargs
2149 )
tierno09c073e2018-04-26 13:36:48 +02002150 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01002151 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00002152 # for NS NSI generate an operation
2153 op_id = None
tierno701018c2019-06-25 11:13:14 +00002154 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02002155 nslcmop_desc = {
2156 "lcmOperationType": "terminate",
2157 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002158 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02002159 }
garciadeblasf53612b2024-07-12 14:44:37 +02002160 op_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01002161 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
2162 )
tierno22577432020-04-08 15:16:57 +00002163 if op_id:
garciadeblasf53612b2024-07-12 14:44:37 +02002164 outdata = {"_id": op_id, "nsName": nsName}
garciadeblas4568a372021-03-24 09:19:48 +01002165 elif (
2166 topic == "netslice_instances_content"
2167 and not engine_session["force"]
2168 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02002169 nsilcmop_desc = {
2170 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02002171 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002172 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02002173 }
garciadeblas4568a372021-03-24 09:19:48 +01002174 op_id, _ = self.engine.new_item(
2175 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
2176 )
tierno22577432020-04-08 15:16:57 +00002177 if op_id:
2178 outdata = {"_id": op_id}
rshri2d386cb2024-07-05 14:35:51 +00002179 elif topic == "clusters" and item == "deregister":
2180 if not op_id:
2181 op_id = self.engine.remove(
2182 engine_session, engine_topic, _id
2183 )
2184 if op_id:
2185 outdata = {"_id": op_id}
2186 cherrypy.response.status = (
2187 HTTPStatus.ACCEPTED.value
2188 if op_id
2189 else HTTPStatus.NO_CONTENT.value
2190 )
yshahd23c6a52025-06-13 05:49:31 +00002191 elif topic == "clusters" and item == "nodegroup" and args:
2192 _id = args[0]
2193 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2194 if op_id:
2195 outdata = {"_id": op_id}
yshah53cc9eb2024-07-05 13:06:31 +00002196 elif topic == "ksus":
2197 op_id = self.engine.delete_ksu(
2198 engine_session, engine_topic, _id, indata
2199 )
2200 outdata = {"op_id": op_id}
tierno22577432020-04-08 15:16:57 +00002201 # if there is not any deletion in process, delete
rshri2d386cb2024-07-05 14:35:51 +00002202 elif not op_id:
tierno22577432020-04-08 15:16:57 +00002203 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2204 if op_id:
rshri2d386cb2024-07-05 14:35:51 +00002205 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01002206 cherrypy.response.status = (
2207 HTTPStatus.ACCEPTED.value
2208 if op_id
2209 else HTTPStatus.NO_CONTENT.value
2210 )
tierno09c073e2018-04-26 13:36:48 +02002211
tierno7ae10112018-05-18 14:36:02 +02002212 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00002213 op_id = None
tierno701018c2019-06-25 11:13:14 +00002214 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01002215 raise NbiException(
2216 "Nothing to update. Provide payload and/or query string",
2217 HTTPStatus.BAD_REQUEST,
2218 )
2219 if (
kayal2001f71c2e82024-06-25 15:26:24 +05302220 item
2221 in (
2222 "nsd_content",
2223 "package_content",
2224 "nst_content",
2225 "template_content",
2226 )
garciadeblas4568a372021-03-24 09:19:48 +01002227 and method == "PUT"
2228 ):
2229 completed = self.engine.upload_content(
2230 engine_session,
2231 engine_topic,
2232 _id,
2233 indata,
2234 kwargs,
2235 cherrypy.request.headers,
2236 )
tiernof27c79b2018-03-12 17:08:42 +01002237 if not completed:
2238 cherrypy.response.headers["Transaction-Id"] = id
rshri2d386cb2024-07-05 14:35:51 +00002239 elif item in (
2240 "app_profiles",
2241 "resource_profiles",
2242 "infra_controller_profiles",
2243 "infra_config_profiles",
2244 ):
2245 op_id = self.engine.edit(
2246 engine_session, engine_topic, _id, item, indata, kwargs
2247 )
yshah53cc9eb2024-07-05 13:06:31 +00002248 elif topic == "oka_packages" and method == "PATCH":
2249 if kwargs:
2250 op_id = self.engine.edit_item(
2251 engine_session, engine_topic, _id, None, kwargs
2252 )
2253 if indata:
yshahffcac5f2024-08-19 12:49:07 +00002254 if isinstance(indata, dict):
yshah53cc9eb2024-07-05 13:06:31 +00002255 op_id = self.engine.edit_item(
2256 engine_session, engine_topic, _id, indata, kwargs
2257 )
2258 else:
2259 cherrypy.request.headers["method"] = cherrypy.request.method
2260 completed = self.engine.upload_content(
2261 engine_session,
2262 engine_topic,
2263 _id,
2264 indata,
2265 {},
2266 cherrypy.request.headers,
2267 )
2268 if not completed:
2269 cherrypy.response.headers["Transaction-Id"] = id
2270 elif topic == "oka_packages" and method == "PUT":
2271 if indata:
2272 cherrypy.request.headers["method"] = cherrypy.request.method
2273 completed = self.engine.upload_content(
2274 engine_session,
2275 engine_topic,
2276 _id,
2277 indata,
2278 {},
2279 cherrypy.request.headers,
2280 )
2281 if not completed:
2282 cherrypy.response.headers["Transaction-Id"] = id
yshahd23c6a52025-06-13 05:49:31 +00002283 elif topic == "clusters" and item == "nodegroup" and args:
2284 _id = args[0]
2285 op_id = self.engine.edit_item(
2286 engine_session, engine_topic, _id, indata, kwargs
2287 )
tiernof27c79b2018-03-12 17:08:42 +01002288 else:
garciadeblas4568a372021-03-24 09:19:48 +01002289 op_id = self.engine.edit_item(
2290 engine_session, engine_topic, _id, indata, kwargs
2291 )
tiernobdebce92019-07-01 15:36:49 +00002292
2293 if op_id:
2294 cherrypy.response.status = HTTPStatus.ACCEPTED.value
2295 outdata = {"op_id": op_id}
2296 else:
2297 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
2298 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01002299 else:
garciadeblas4568a372021-03-24 09:19:48 +01002300 raise NbiException(
2301 "Method {} not allowed".format(method),
2302 HTTPStatus.METHOD_NOT_ALLOWED,
2303 )
tiernoa6bb45d2019-06-14 09:45:39 +00002304
2305 # if Role information changes, it is needed to reload the information of roles
2306 if topic == "roles" and method != "GET":
2307 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01002308
garciadeblas4568a372021-03-24 09:19:48 +01002309 if (
2310 topic == "projects"
2311 and method == "DELETE"
2312 or topic in ["users", "roles"]
2313 and method in ["PUT", "PATCH", "DELETE"]
2314 ):
delacruzramoad682a52019-12-10 16:26:34 +01002315 self.authenticator.remove_token_from_cache()
2316
garciadeblasf53612b2024-07-12 14:44:37 +02002317 cef_event(
2318 cef_logger,
2319 {
2320 "name": "User Operation",
2321 "sourceUserName": token_info.get("username"),
2322 },
2323 )
2324 if topic == "ns_instances_content" and url_id:
2325 nsName = (
2326 outdata.get("name") if method == "GET" else outdata.get("nsName")
2327 )
elumalai7802ff82023-04-24 20:38:32 +05302328 cef_event(
2329 cef_logger,
2330 {
garciadeblasf53612b2024-07-12 14:44:37 +02002331 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2332 log_mapping[method],
2333 topic,
2334 nsName,
2335 outdata.get("id"),
2336 token_info.get("project_name"),
2337 ),
2338 },
2339 )
2340 cherrypy.log("{}".format(cef_logger))
2341 elif topic == "ns_instances_content" and method == "POST":
2342 cef_event(
2343 cef_logger,
2344 {
2345 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2346 log_mapping[method],
2347 topic,
2348 outdata.get("nsName"),
2349 outdata.get("id"),
2350 token_info.get("project_name"),
2351 ),
2352 },
2353 )
2354 cherrypy.log("{}".format(cef_logger))
2355 elif topic in ("ns_instances", "vnf_instances") and item:
2356 cef_event(
2357 cef_logger,
2358 {
2359 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2360 log_mapping[method],
2361 topic,
2362 outdata.get("nsName"),
2363 url_id,
2364 token_info.get("project_name"),
2365 ),
2366 },
2367 )
2368 cherrypy.log("{}".format(cef_logger))
2369 elif item is not None:
2370 cef_event(
2371 cef_logger,
2372 {
elumalai7802ff82023-04-24 20:38:32 +05302373 "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format(
2374 item,
2375 topic,
2376 url_id,
2377 token_info.get("project_name"),
2378 ),
2379 },
2380 )
2381 cherrypy.log("{}".format(cef_logger))
2382 else:
2383 cef_event(
2384 cef_logger,
2385 {
elumalai7802ff82023-04-24 20:38:32 +05302386 "message": "{} {} {}, Project={} Outcome=Success".format(
2387 log_mapping[method],
2388 topic,
2389 url_id,
2390 token_info.get("project_name"),
2391 ),
2392 },
2393 )
2394 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002395 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02002396 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01002397 if isinstance(
2398 e,
2399 (
2400 NbiException,
2401 EngineException,
2402 DbException,
2403 FsException,
2404 MsgException,
2405 AuthException,
2406 ValidationError,
2407 AuthconnException,
2408 ),
2409 ):
tiernob24258a2018-10-04 18:39:49 +02002410 http_code_value = cherrypy.response.status = e.http_code.value
2411 http_code_name = e.http_code.name
2412 cherrypy.log("Exception {}".format(e))
2413 else:
garciadeblas4568a372021-03-24 09:19:48 +01002414 http_code_value = (
2415 cherrypy.response.status
2416 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01002417 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02002418 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02002419 if hasattr(outdata, "close"): # is an open file
2420 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02002421 error_text = str(e)
2422 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02002423 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02002424 try:
tiernocc103432018-10-19 14:10:35 +02002425 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01002426 self.engine.db.set_one(
2427 rollback_item["topic"],
2428 {"_id": rollback_item["_id"]},
2429 rollback_item["content"],
2430 fail_on_empty=False,
2431 )
preethika.p329b8182020-04-22 12:25:39 +05302432 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01002433 self.engine.db.del_list(
2434 rollback_item["topic"],
2435 rollback_item["filter"],
garciadeblas4568a372021-03-24 09:19:48 +01002436 )
tiernocc103432018-10-19 14:10:35 +02002437 else:
garciadeblas4568a372021-03-24 09:19:48 +01002438 self.engine.db.del_one(
2439 rollback_item["topic"],
2440 {"_id": rollback_item["_id"]},
2441 fail_on_empty=False,
2442 )
tierno3ace63c2018-05-03 17:51:43 +02002443 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01002444 rollback_error_text = "Rollback Exception {}: {}".format(
2445 rollback_item, e2
2446 )
tiernob24258a2018-10-04 18:39:49 +02002447 cherrypy.log(rollback_error_text)
2448 error_text += ". " + rollback_error_text
2449 # if isinstance(e, MsgException):
2450 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
2451 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01002452 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02002453 "code": http_code_name,
2454 "status": http_code_value,
2455 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01002456 }
elumalai7802ff82023-04-24 20:38:32 +05302457 if item is not None and token_info is not None:
2458 cef_event(
2459 cef_logger,
2460 {
2461 "name": "User Operation",
2462 "sourceUserName": token_info.get("username", None),
2463 "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format(
2464 item,
2465 topic,
2466 url_id,
2467 token_info.get("project_name", None),
2468 ),
2469 "severity": "2",
2470 },
2471 )
2472 cherrypy.log("{}".format(cef_logger))
2473 elif token_info is not None:
2474 cef_event(
2475 cef_logger,
2476 {
2477 "name": "User Operation",
2478 "sourceUserName": token_info.get("username", None),
2479 "message": "{} {} {}, Project={} Outcome=Failure".format(
2480 item,
2481 topic,
2482 url_id,
2483 token_info.get("project_name", None),
2484 ),
2485 "severity": "2",
2486 },
2487 )
2488 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002489 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01002490 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00002491 finally:
2492 if token_info:
2493 self._format_login(token_info)
2494 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
2495 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
2496 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01002497 cherrypy.request.login += ";{}={}".format(
2498 logging_id, outdata[logging_id][:36]
2499 )
tiernoc94c3df2018-02-09 15:38:54 +01002500
2501
tiernoc94c3df2018-02-09 15:38:54 +01002502def _start_service():
2503 """
2504 Callback function called when cherrypy.engine starts
2505 Override configuration with env variables
2506 Set database, storage, message configuration
2507 Init database with admin/admin user password
2508 """
tierno932499c2019-01-28 17:28:10 +00002509 global subscription_thread
elumalai7802ff82023-04-24 20:38:32 +05302510 global cef_logger
Rahulc72bc8e2023-12-05 11:54:38 +00002511 global current_backend
tiernoc94c3df2018-02-09 15:38:54 +01002512 cherrypy.log.error("Starting osm_nbi")
2513 # update general cherrypy configuration
2514 update_dict = {}
2515
garciadeblas4568a372021-03-24 09:19:48 +01002516 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01002517 for k, v in environ.items():
garciadeblas6d83f8f2023-06-19 22:34:49 +02002518 if k == "OSMNBI_USER_MANAGEMENT":
garciadeblas7a0dace2024-09-17 18:00:50 +02002519 feature_state = v.lower() == "true"
garciadeblas6d83f8f2023-06-19 22:34:49 +02002520 engine_config["authentication"]["user_management"] = feature_state
Adurtid68526d2023-11-14 11:14:53 +00002521 elif k == "OSMNBI_PWD_EXPIRE_DAYS":
2522 pwd_expire_days = int(v)
2523 engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days
2524 elif k == "OSMNBI_MAX_PWD_ATTEMPT":
2525 max_pwd_attempt = int(v)
2526 engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt
2527 elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS":
2528 account_expire_days = int(v)
2529 engine_config["authentication"]["account_expire_days"] = account_expire_days
jeganbe1a3df2024-06-04 12:05:19 +00002530 elif k == "OSMNBI_SMTP_SERVER":
2531 engine_config["authentication"]["smtp_server"] = v
2532 engine_config["authentication"]["all"] = environ
2533 elif k == "OSMNBI_SMTP_PORT":
2534 port = int(v)
2535 engine_config["authentication"]["smtp_port"] = port
2536 elif k == "OSMNBI_SENDER_EMAIL":
2537 engine_config["authentication"]["sender_email"] = v
2538 elif k == "OSMNBI_EMAIL_PASSWORD":
2539 engine_config["authentication"]["sender_password"] = v
2540 elif k == "OSMNBI_OTP_RETRY_COUNT":
2541 otp_retry_count = int(v)
2542 engine_config["authentication"]["retry_count"] = otp_retry_count
2543 elif k == "OSMNBI_OTP_EXPIRY_TIME":
2544 otp_expiry_time = int(v)
2545 engine_config["authentication"]["otp_expiry_time"] = otp_expiry_time
tiernoc94c3df2018-02-09 15:38:54 +01002546 if not k.startswith("OSMNBI_"):
2547 continue
tiernoe1281182018-05-22 12:24:36 +02002548 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01002549 if not k2:
2550 continue
2551 try:
2552 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01002553 if k == "OSMNBI_STATIC_DIR":
2554 engine_config["/static"]["tools.staticdir.dir"] = v
2555 engine_config["/static"]["tools.staticdir.on"] = True
2556 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
2557 update_dict["server.socket_port"] = int(v)
2558 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
2559 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02002560 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01002561 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002562 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02002563 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002564 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01002565 engine_config[k1][k2] = int(v)
2566 else:
2567 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002568
tiernoc94c3df2018-02-09 15:38:54 +01002569 except ValueError as e:
2570 cherrypy.log.error("Ignoring environ '{}': " + str(e))
2571 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01002572 cherrypy.log(
2573 "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
2574 )
tiernoc94c3df2018-02-09 15:38:54 +01002575
2576 if update_dict:
2577 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02002578 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01002579
2580 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01002581 log_format_simple = (
2582 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
2583 )
2584 log_formatter_simple = logging.Formatter(
2585 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
2586 )
tiernoc94c3df2018-02-09 15:38:54 +01002587 logger_server = logging.getLogger("cherrypy.error")
2588 logger_access = logging.getLogger("cherrypy.access")
2589 logger_cherry = logging.getLogger("cherrypy")
2590 logger_nbi = logging.getLogger("nbi")
2591
tiernof5298be2018-05-16 14:43:57 +02002592 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01002593 file_handler = logging.handlers.RotatingFileHandler(
2594 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
2595 )
tiernoc94c3df2018-02-09 15:38:54 +01002596 file_handler.setFormatter(log_formatter_simple)
2597 logger_cherry.addHandler(file_handler)
2598 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02002599 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01002600 for format_, logger in {
2601 "nbi.server %(filename)s:%(lineno)s": logger_server,
2602 "nbi.access %(filename)s:%(lineno)s": logger_access,
2603 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
2604 }.items():
tiernob24258a2018-10-04 18:39:49 +02002605 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01002606 log_formatter_cherry = logging.Formatter(
2607 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
2608 )
tiernob24258a2018-10-04 18:39:49 +02002609 str_handler = logging.StreamHandler()
2610 str_handler.setFormatter(log_formatter_cherry)
2611 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01002612
tiernof5298be2018-05-16 14:43:57 +02002613 if engine_config["global"].get("log.level"):
2614 logger_cherry.setLevel(engine_config["global"]["log.level"])
2615 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01002616
2617 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01002618 for k1, logname in {
2619 "message": "nbi.msg",
2620 "database": "nbi.db",
2621 "storage": "nbi.fs",
2622 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01002623 engine_config[k1]["logger_name"] = logname
2624 logger_module = logging.getLogger(logname)
2625 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01002626 file_handler = logging.handlers.RotatingFileHandler(
2627 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
2628 )
tiernoc94c3df2018-02-09 15:38:54 +01002629 file_handler.setFormatter(log_formatter_simple)
2630 logger_module.addHandler(file_handler)
2631 if "loglevel" in engine_config[k1]:
2632 logger_module.setLevel(engine_config[k1]["loglevel"])
2633 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01002634 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
2635 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
2636 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
2637 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
2638 target_version=auth_database_version
2639 )
tiernobee508e2019-01-21 11:21:49 +00002640
elumalai7802ff82023-04-24 20:38:32 +05302641 cef_logger = cef_event_builder(engine_config["authentication"])
2642
tierno932499c2019-01-28 17:28:10 +00002643 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01002644 subscription_thread = SubscriptionThread(
2645 config=engine_config, engine=nbi_server.engine
2646 )
tierno932499c2019-01-28 17:28:10 +00002647 subscription_thread.start()
2648 # Do not capture except SubscriptionException
2649
tiernob2e48bd2020-02-04 15:47:18 +00002650 backend = engine_config["authentication"]["backend"]
Rahulc72bc8e2023-12-05 11:54:38 +00002651 current_backend = backend
garciadeblas4568a372021-03-24 09:19:48 +01002652 cherrypy.log.error(
2653 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
2654 nbi_version, nbi_version_date, backend
2655 )
2656 )
tiernoc94c3df2018-02-09 15:38:54 +01002657
2658
2659def _stop_service():
2660 """
2661 Callback function called when cherrypy.engine stops
2662 TODO: Ending database connections.
2663 """
tierno932499c2019-01-28 17:28:10 +00002664 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01002665 if subscription_thread:
2666 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00002667 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01002668 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01002669 cherrypy.log.error("Stopping osm_nbi")
2670
tierno2236d202018-05-16 19:05:16 +02002671
tiernof5298be2018-05-16 14:43:57 +02002672def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00002673 global nbi_server
tierno932499c2019-01-28 17:28:10 +00002674 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01002675 cherrypy.engine.subscribe("start", _start_service)
2676 cherrypy.engine.subscribe("stop", _stop_service)
2677 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02002678
2679
2680def usage():
garciadeblas4568a372021-03-24 09:19:48 +01002681 print(
2682 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02002683 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
2684 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01002685 """.format(
2686 sys.argv[0]
2687 )
2688 )
tierno2236d202018-05-16 19:05:16 +02002689 # --log-socket-host HOST: send logs to this host")
2690 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002691
2692
garciadeblas4568a372021-03-24 09:19:48 +01002693if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002694 try:
2695 # load parameters and configuration
2696 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2697 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2698 config_file = None
2699 for o, a in opts:
2700 if o in ("-h", "--help"):
2701 usage()
2702 sys.exit()
2703 elif o in ("-c", "--config"):
2704 config_file = a
2705 # elif o == "--log-socket-port":
2706 # log_socket_port = a
2707 # elif o == "--log-socket-host":
2708 # log_socket_host = a
2709 # elif o == "--log-file":
2710 # log_file = a
2711 else:
garciadeblascffd2782025-07-03 17:52:04 +02002712 raise getopt.GetoptError(f"Unhandled option: {o}")
tiernof5298be2018-05-16 14:43:57 +02002713 if config_file:
2714 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002715 print(
2716 "configuration file '{}' that not exist".format(config_file),
2717 file=sys.stderr,
2718 )
tiernof5298be2018-05-16 14:43:57 +02002719 exit(1)
2720 else:
garciadeblas4568a372021-03-24 09:19:48 +01002721 for config_file in (
2722 __file__[: __file__.rfind(".")] + ".cfg",
2723 "./nbi.cfg",
2724 "/etc/osm/nbi.cfg",
2725 ):
tiernof5298be2018-05-16 14:43:57 +02002726 if path.isfile(config_file):
2727 break
2728 else:
garciadeblas4568a372021-03-24 09:19:48 +01002729 print(
2730 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2731 file=sys.stderr,
2732 )
tiernof5298be2018-05-16 14:43:57 +02002733 exit(1)
2734 nbi(config_file)
2735 except getopt.GetoptError as e:
2736 print(str(e), file=sys.stderr)
2737 # usage()
2738 exit(1)