blob: 14a08991a3f3abb63d3132644829cbdd47c5afc1 [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
tiernoc94c3df2018-02-09 15:38:54 +010051
52"""
tiernof27c79b2018-03-12 17:08:42 +010053North Bound Interface (O: OSM specific; 5,X: SOL005 not implemented yet; O5: SOL005 implemented)
tiernoc94c3df2018-02-09 15:38:54 +010054URL: /osm GET POST PUT DELETE PATCH
garciadeblas9750c5a2018-10-15 16:20:35 +020055 /nsd/v1
tierno2236d202018-05-16 19:05:16 +020056 /ns_descriptors_content O O
57 /<nsdInfoId> O O O O
tiernoc94c3df2018-02-09 15:38:54 +010058 /ns_descriptors O5 O5
59 /<nsdInfoId> O5 O5 5
60 /nsd_content O5 O5
tiernof27c79b2018-03-12 17:08:42 +010061 /nsd O
62 /artifacts[/<artifactPath>] O
kayal2001f71c2e82024-06-25 15:26:24 +053063 /ns_config_template O O
64 /<nsConfigTemplateId> O O
65 /template_content O O
tiernoc94c3df2018-02-09 15:38:54 +010066 /pnf_descriptors 5 5
67 /<pnfdInfoId> 5 5 5
68 /pnfd_content 5 5
tiernof27c79b2018-03-12 17:08:42 +010069 /subscriptions 5 5
70 /<subscriptionId> 5 X
tiernoc94c3df2018-02-09 15:38:54 +010071
72 /vnfpkgm/v1
tierno55945e72018-04-06 16:40:27 +020073 /vnf_packages_content O O
tierno2236d202018-05-16 19:05:16 +020074 /<vnfPkgId> O O
tiernoc94c3df2018-02-09 15:38:54 +010075 /vnf_packages O5 O5
76 /<vnfPkgId> O5 O5 5
tiernoc94c3df2018-02-09 15:38:54 +010077 /package_content O5 O5
78 /upload_from_uri X
tiernof27c79b2018-03-12 17:08:42 +010079 /vnfd O5
80 /artifacts[/<artifactPath>] O5
81 /subscriptions X X
82 /<subscriptionId> X X
tiernoc94c3df2018-02-09 15:38:54 +010083
84 /nslcm/v1
tiernof27c79b2018-03-12 17:08:42 +010085 /ns_instances_content O O
tierno2236d202018-05-16 19:05:16 +020086 /<nsInstanceId> O O
tiernof27c79b2018-03-12 17:08:42 +010087 /ns_instances 5 5
tierno95692442018-05-24 18:05:28 +020088 /<nsInstanceId> O5 O5
tierno65acb4d2018-04-06 16:42:40 +020089 instantiate O5
90 terminate O5
91 action O
92 scale O5
elumalai8e3806c2022-04-28 17:26:24 +053093 migrate O
aticig544a2ae2022-04-05 09:00:17 +030094 update 05
garciadeblas0964edf2022-02-11 00:43:44 +010095 heal O5
tiernoc94c3df2018-02-09 15:38:54 +010096 /ns_lcm_op_occs 5 5
97 /<nsLcmOpOccId> 5 5 5
Gabriel Cuba84a60df2023-10-30 14:01:54 -050098 cancel 05
tiernof759d822018-06-11 18:54:54 +020099 /vnf_instances (also vnfrs for compatibility) O
100 /<vnfInstanceId> O
tiernof27c79b2018-03-12 17:08:42 +0100101 /subscriptions 5 5
102 /<subscriptionId> 5 X
garciadeblas9750c5a2018-10-15 16:20:35 +0200103
tiernocb83c942018-09-24 17:28:13 +0200104 /pdu/v1
tierno032916c2019-03-22 13:27:12 +0000105 /pdu_descriptors O O
tiernocb83c942018-09-24 17:28:13 +0200106 /<id> O O O O
garciadeblas9750c5a2018-10-15 16:20:35 +0200107
tiernof27c79b2018-03-12 17:08:42 +0100108 /admin/v1
109 /tokens O O
tierno2236d202018-05-16 19:05:16 +0200110 /<id> O O
tiernof27c79b2018-03-12 17:08:42 +0100111 /users O O
tiernocd54a4a2018-09-12 16:40:35 +0200112 /<id> O O O O
tiernof27c79b2018-03-12 17:08:42 +0100113 /projects O O
tierno2236d202018-05-16 19:05:16 +0200114 /<id> O O
tierno55ba2e62018-12-11 17:22:22 +0000115 /vim_accounts (also vims for compatibility) O O
116 /<id> O O O
117 /wim_accounts O O
tierno2236d202018-05-16 19:05:16 +0200118 /<id> O O O
tierno0f98af52018-03-19 10:28:22 +0100119 /sdns O O
tierno2236d202018-05-16 19:05:16 +0200120 /<id> O O O
delacruzramofe598fe2019-10-23 18:25:11 +0200121 /k8sclusters O O
122 /<id> O O O
123 /k8srepos O O
124 /<id> O O
Felipe Vicensb66b0412020-05-06 10:11:00 +0200125 /osmrepos O O
126 /<id> O O
tiernoc94c3df2018-02-09 15:38:54 +0100127
garciadeblas9750c5a2018-10-15 16:20:35 +0200128 /nst/v1 O O
129 /netslice_templates_content O O
130 /<nstInfoId> O O O O
131 /netslice_templates O O
132 /<nstInfoId> O O O
133 /nst_content O O
134 /nst O
135 /artifacts[/<artifactPath>] O
136 /subscriptions X X
137 /<subscriptionId> X X
138
139 /nsilcm/v1
140 /netslice_instances_content O O
141 /<SliceInstanceId> O O
142 /netslice_instances O O
143 /<SliceInstanceId> O O
144 instantiate O
145 terminate O
146 action O
147 /nsi_lcm_op_occs O O
148 /<nsiLcmOpOccId> O O O
149 /subscriptions X X
150 /<subscriptionId> X X
151
tierno2236d202018-05-16 19:05:16 +0200152query string:
153 Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force.
tierno36ec8602018-11-02 17:27:11 +0100154 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
155 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
156 op := "eq" | "neq" (or "ne") | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
157 attrName := string
tierno2236d202018-05-16 19:05:16 +0200158 For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any
159 item of the array, that is, pass if any item of the array pass the filter.
160 It allows both ne and neq for not equal
161 TODO: 4.3.3 Attribute selectors
162 all_fields, fields=x,y,.., exclude_default, exclude_fields=x,y,...
tiernoc94c3df2018-02-09 15:38:54 +0100163 (none) … same as “exclude_default”
164 all_fields … all attributes.
tierno2236d202018-05-16 19:05:16 +0200165 fields=<list> … all attributes except all complex attributes with minimum cardinality of zero that are not
166 conditionally mandatory, and that are not provided in <list>.
167 exclude_fields=<list> … all attributes except those complex attributes with a minimum cardinality of zero that
168 are not conditionally mandatory, and that are provided in <list>.
169 exclude_default … all attributes except those complex attributes with a minimum cardinality of zero that are not
170 conditionally mandatory, and that are part of the "default exclude set" defined in the present specification for
171 the particular resource
172 exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality
173 of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the
174 present specification for the particular resource, but that are not part of <list>
tierno65ca36d2019-02-12 19:27:52 +0100175 Additionally it admits some administrator values:
176 FORCE: To force operations skipping dependency checkings
177 ADMIN: To act as an administrator or a different project
178 PUBLIC: To get public descriptors or set a descriptor as public
179 SET_PROJECT: To make a descriptor available for other project
beierlmbc5a5242022-05-17 21:25:29 -0400180
tiernoc94c3df2018-02-09 15:38:54 +0100181Header field name Reference Example Descriptions
182 Accept IETF RFC 7231 [19] application/json Content-Types that are acceptable for the response.
183 This header field shall be present if the response is expected to have a non-empty message body.
184 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the request.
185 This header field shall be present if the request has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200186 Authorization IETF RFC 7235 [22] Bearer mF_9.B5f-4.1JqM The authorization token for the request.
187 Details are specified in clause 4.5.3.
tiernoc94c3df2018-02-09 15:38:54 +0100188 Range IETF RFC 7233 [21] 1000-2000 Requested range of bytes from a file
189Header field name Reference Example Descriptions
190 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the response.
191 This header field shall be present if the response has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200192 Location IETF RFC 7231 [19] http://www.example.com/vnflcm/v1/vnf_instances/123 Used in redirection, or when a
193 new resource has been created.
tiernoc94c3df2018-02-09 15:38:54 +0100194 This header field shall be present if the response status code is 201 or 3xx.
tierno2236d202018-05-16 19:05:16 +0200195 In the present document this header field is also used if the response status code is 202 and a new resource was
196 created.
197 WWW-Authenticate IETF RFC 7235 [22] Bearer realm="example" Challenge if the corresponding HTTP request has not
198 provided authorization, or error details if the corresponding HTTP request has provided an invalid authorization
199 token.
200 Accept-Ranges IETF RFC 7233 [21] bytes Used by the Server to signal whether or not it supports ranges for
201 certain resources.
202 Content-Range IETF RFC 7233 [21] bytes 21010-47021/ 47022 Signals the byte range that is contained in the
203 response, and the total length of the file.
tiernoc94c3df2018-02-09 15:38:54 +0100204 Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT
tiernoc94c3df2018-02-09 15:38:54 +0100205"""
206
tierno701018c2019-06-25 11:13:14 +0000207valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC")
208# ^ Contains possible administrative query string words:
209# ADMIN=True(by default)|Project|Project-list: See all elements, or elements of a project
210# (not owned by my session project).
211# PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public
212# FORCE=True(by default)|False: Force edition/deletion operations
213# SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio
214
215valid_url_methods = {
216 # contains allowed URL and methods, and the role_permission name
217 "admin": {
218 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100219 "tokens": {
220 "METHODS": ("GET", "POST", "DELETE"),
221 "ROLE_PERMISSION": "tokens:",
222 "<ID>": {"METHODS": ("GET", "DELETE"), "ROLE_PERMISSION": "tokens:id:"},
223 },
224 "users": {
225 "METHODS": ("GET", "POST"),
226 "ROLE_PERMISSION": "users:",
227 "<ID>": {
228 "METHODS": ("GET", "DELETE", "PATCH"),
229 "ROLE_PERMISSION": "users:id:",
230 },
231 },
232 "projects": {
233 "METHODS": ("GET", "POST"),
234 "ROLE_PERMISSION": "projects:",
235 "<ID>": {
236 "METHODS": ("GET", "DELETE", "PATCH"),
237 "ROLE_PERMISSION": "projects:id:",
238 },
239 },
240 "roles": {
241 "METHODS": ("GET", "POST"),
242 "ROLE_PERMISSION": "roles:",
243 "<ID>": {
244 "METHODS": ("GET", "DELETE", "PATCH"),
245 "ROLE_PERMISSION": "roles:id:",
246 },
247 },
248 "vims": {
249 "METHODS": ("GET", "POST"),
250 "ROLE_PERMISSION": "vims:",
251 "<ID>": {
252 "METHODS": ("GET", "DELETE", "PATCH"),
253 "ROLE_PERMISSION": "vims:id:",
254 },
255 },
256 "vim_accounts": {
257 "METHODS": ("GET", "POST"),
258 "ROLE_PERMISSION": "vim_accounts:",
259 "<ID>": {
260 "METHODS": ("GET", "DELETE", "PATCH"),
261 "ROLE_PERMISSION": "vim_accounts:id:",
262 },
263 },
264 "wim_accounts": {
265 "METHODS": ("GET", "POST"),
266 "ROLE_PERMISSION": "wim_accounts:",
267 "<ID>": {
268 "METHODS": ("GET", "DELETE", "PATCH"),
269 "ROLE_PERMISSION": "wim_accounts:id:",
270 },
271 },
272 "sdns": {
273 "METHODS": ("GET", "POST"),
274 "ROLE_PERMISSION": "sdn_controllers:",
275 "<ID>": {
276 "METHODS": ("GET", "DELETE", "PATCH"),
277 "ROLE_PERMISSION": "sdn_controllers:id:",
278 },
279 },
280 "k8sclusters": {
281 "METHODS": ("GET", "POST"),
282 "ROLE_PERMISSION": "k8sclusters:",
283 "<ID>": {
284 "METHODS": ("GET", "DELETE", "PATCH"),
285 "ROLE_PERMISSION": "k8sclusters:id:",
286 },
287 },
288 "vca": {
289 "METHODS": ("GET", "POST"),
290 "ROLE_PERMISSION": "vca:",
291 "<ID>": {
292 "METHODS": ("GET", "DELETE", "PATCH"),
293 "ROLE_PERMISSION": "vca:id:",
294 },
295 },
296 "k8srepos": {
297 "METHODS": ("GET", "POST"),
298 "ROLE_PERMISSION": "k8srepos:",
299 "<ID>": {
300 "METHODS": ("GET", "DELETE"),
301 "ROLE_PERMISSION": "k8srepos:id:",
302 },
303 },
304 "osmrepos": {
305 "METHODS": ("GET", "POST"),
306 "ROLE_PERMISSION": "osmrepos:",
307 "<ID>": {
308 "METHODS": ("GET", "DELETE", "PATCH"),
309 "ROLE_PERMISSION": "osmrepos:id:",
310 },
311 },
312 "domains": {
313 "METHODS": ("GET",),
314 "ROLE_PERMISSION": "domains:",
315 },
tierno701018c2019-06-25 11:13:14 +0000316 }
317 },
318 "pdu": {
319 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100320 "pdu_descriptors": {
321 "METHODS": ("GET", "POST"),
322 "ROLE_PERMISSION": "pduds:",
323 "<ID>": {
324 "METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"),
325 "ROLE_PERMISSION": "pduds:id:",
326 },
327 },
tierno701018c2019-06-25 11:13:14 +0000328 }
329 },
330 "nsd": {
331 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100332 "ns_descriptors_content": {
333 "METHODS": ("GET", "POST"),
334 "ROLE_PERMISSION": "nsds:",
335 "<ID>": {
336 "METHODS": ("GET", "PUT", "DELETE"),
337 "ROLE_PERMISSION": "nsds:id:",
338 },
339 },
340 "ns_descriptors": {
341 "METHODS": ("GET", "POST"),
342 "ROLE_PERMISSION": "nsds:",
343 "<ID>": {
344 "METHODS": ("GET", "DELETE", "PATCH"),
345 "ROLE_PERMISSION": "nsds:id:",
346 "nsd_content": {
347 "METHODS": ("GET", "PUT"),
348 "ROLE_PERMISSION": "nsds:id:content:",
349 },
350 "nsd": {
351 "METHODS": ("GET",), # descriptor inside package
352 "ROLE_PERMISSION": "nsds:id:content:",
353 },
354 "artifacts": {
355 "METHODS": ("GET",),
356 "ROLE_PERMISSION": "nsds:id:nsd_artifact:",
357 "*": None,
358 },
359 },
360 },
kayal2001f71c2e82024-06-25 15:26:24 +0530361 "ns_config_template": {
362 "METHODS": ("GET", "POST"),
363 "ROLE_PERMISSION": "ns_config_template:content:",
364 "<ID>": {
365 "METHODS": ("GET", "DELETE"),
366 "ROLE_PERMISSION": "ns_config_template:id:",
367 "template_content": {
368 "METHODS": ("GET", "PUT"),
369 "ROLE_PERMISSION": "ns_config_template:id:content:",
370 },
371 },
372 },
garciadeblas4568a372021-03-24 09:19:48 +0100373 "pnf_descriptors": {
374 "TODO": ("GET", "POST"),
375 "<ID>": {
376 "TODO": ("GET", "DELETE", "PATCH"),
377 "pnfd_content": {"TODO": ("GET", "PUT")},
378 },
379 },
380 "subscriptions": {
381 "TODO": ("GET", "POST"),
382 "<ID>": {"TODO": ("GET", "DELETE")},
383 },
tierno701018c2019-06-25 11:13:14 +0000384 }
385 },
386 "vnfpkgm": {
387 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100388 "vnf_packages_content": {
389 "METHODS": ("GET", "POST"),
390 "ROLE_PERMISSION": "vnfds:",
391 "<ID>": {
392 "METHODS": ("GET", "PUT", "DELETE"),
393 "ROLE_PERMISSION": "vnfds:id:",
394 },
395 },
396 "vnf_packages": {
397 "METHODS": ("GET", "POST"),
398 "ROLE_PERMISSION": "vnfds:",
399 "<ID>": {
400 "METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo
401 "ROLE_PERMISSION": "vnfds:id:",
402 "package_content": {
403 "METHODS": ("GET", "PUT"), # package
404 "ROLE_PERMISSION": "vnfds:id:",
405 "upload_from_uri": {
406 "METHODS": (),
407 "TODO": ("POST",),
408 "ROLE_PERMISSION": "vnfds:id:upload:",
409 },
410 },
411 "vnfd": {
412 "METHODS": ("GET",), # descriptor inside package
413 "ROLE_PERMISSION": "vnfds:id:content:",
414 },
415 "artifacts": {
416 "METHODS": ("GET",),
417 "ROLE_PERMISSION": "vnfds:id:vnfd_artifact:",
418 "*": None,
419 },
420 "action": {
421 "METHODS": ("POST",),
422 "ROLE_PERMISSION": "vnfds:id:action:",
423 },
424 },
425 },
426 "subscriptions": {
427 "TODO": ("GET", "POST"),
428 "<ID>": {"TODO": ("GET", "DELETE")},
429 },
430 "vnfpkg_op_occs": {
431 "METHODS": ("GET",),
432 "ROLE_PERMISSION": "vnfds:vnfpkgops:",
433 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnfds:vnfpkgops:id:"},
434 },
tierno701018c2019-06-25 11:13:14 +0000435 }
436 },
437 "nslcm": {
438 "v1": {
adurti3af50952024-05-31 11:36:57 +0530439 "ns_instances_terminate": {
440 "METHODS": ("POST"),
441 "ROLE_PERMISSION": "ns_instances:",
442 },
garciadeblas4568a372021-03-24 09:19:48 +0100443 "ns_instances_content": {
444 "METHODS": ("GET", "POST"),
445 "ROLE_PERMISSION": "ns_instances:",
446 "<ID>": {
447 "METHODS": ("GET", "DELETE"),
448 "ROLE_PERMISSION": "ns_instances:id:",
449 },
450 },
451 "ns_instances": {
452 "METHODS": ("GET", "POST"),
453 "ROLE_PERMISSION": "ns_instances:",
454 "<ID>": {
455 "METHODS": ("GET", "DELETE"),
456 "ROLE_PERMISSION": "ns_instances:id:",
garciadeblas0964edf2022-02-11 00:43:44 +0100457 "heal": {
458 "METHODS": ("POST",),
459 "ROLE_PERMISSION": "ns_instances:id:heal:",
460 },
garciadeblas4568a372021-03-24 09:19:48 +0100461 "scale": {
462 "METHODS": ("POST",),
463 "ROLE_PERMISSION": "ns_instances:id:scale:",
464 },
465 "terminate": {
466 "METHODS": ("POST",),
467 "ROLE_PERMISSION": "ns_instances:id:terminate:",
468 },
469 "instantiate": {
470 "METHODS": ("POST",),
471 "ROLE_PERMISSION": "ns_instances:id:instantiate:",
472 },
elumalai8e3806c2022-04-28 17:26:24 +0530473 "migrate": {
474 "METHODS": ("POST",),
475 "ROLE_PERMISSION": "ns_instances:id:migrate:",
476 },
garciadeblas4568a372021-03-24 09:19:48 +0100477 "action": {
478 "METHODS": ("POST",),
479 "ROLE_PERMISSION": "ns_instances:id:action:",
480 },
aticig544a2ae2022-04-05 09:00:17 +0300481 "update": {
482 "METHODS": ("POST",),
483 "ROLE_PERMISSION": "ns_instances:id:update:",
484 },
garciadeblas4568a372021-03-24 09:19:48 +0100485 },
486 },
487 "ns_lcm_op_occs": {
488 "METHODS": ("GET",),
489 "ROLE_PERMISSION": "ns_instances:opps:",
490 "<ID>": {
491 "METHODS": ("GET",),
492 "ROLE_PERMISSION": "ns_instances:opps:id:",
Gabriel Cuba84a60df2023-10-30 14:01:54 -0500493 "cancel": {
494 "METHODS": ("POST",),
495 "ROLE_PERMISSION": "ns_instances:opps:cancel:",
496 },
garciadeblas4568a372021-03-24 09:19:48 +0100497 },
498 },
499 "vnfrs": {
500 "METHODS": ("GET",),
501 "ROLE_PERMISSION": "vnf_instances:",
502 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
503 },
504 "vnf_instances": {
505 "METHODS": ("GET",),
506 "ROLE_PERMISSION": "vnf_instances:",
507 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
508 },
509 "subscriptions": {
510 "METHODS": ("GET", "POST"),
511 "ROLE_PERMISSION": "ns_subscriptions:",
512 "<ID>": {
513 "METHODS": ("GET", "DELETE"),
514 "ROLE_PERMISSION": "ns_subscriptions:id:",
515 },
516 },
tierno701018c2019-06-25 11:13:14 +0000517 }
518 },
almagiae47b9132022-05-17 14:12:22 +0200519 "vnflcm": {
520 "v1": {
garciadeblasf2af4a12023-01-24 16:56:54 +0100521 "vnf_instances": {
522 "METHODS": ("GET", "POST"),
523 "ROLE_PERMISSION": "vnflcm_instances:",
524 "<ID>": {
525 "METHODS": ("GET", "DELETE"),
526 "ROLE_PERMISSION": "vnflcm_instances:id:",
527 "scale": {
528 "METHODS": ("POST",),
529 "ROLE_PERMISSION": "vnflcm_instances:id:scale:",
530 },
531 "terminate": {
532 "METHODS": ("POST",),
533 "ROLE_PERMISSION": "vnflcm_instances:id:terminate:",
534 },
535 "instantiate": {
536 "METHODS": ("POST",),
537 "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:",
538 },
539 },
540 },
541 "vnf_lcm_op_occs": {
542 "METHODS": ("GET",),
543 "ROLE_PERMISSION": "vnf_instances:opps:",
544 "<ID>": {
545 "METHODS": ("GET",),
546 "ROLE_PERMISSION": "vnf_instances:opps:id:",
547 },
548 },
549 "subscriptions": {
550 "METHODS": ("GET", "POST"),
551 "ROLE_PERMISSION": "vnflcm_subscriptions:",
552 "<ID>": {
553 "METHODS": ("GET", "DELETE"),
554 "ROLE_PERMISSION": "vnflcm_subscriptions:id:",
555 },
556 },
almagiae47b9132022-05-17 14:12:22 +0200557 }
558 },
tierno701018c2019-06-25 11:13:14 +0000559 "nst": {
560 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100561 "netslice_templates_content": {
562 "METHODS": ("GET", "POST"),
563 "ROLE_PERMISSION": "slice_templates:",
564 "<ID>": {
565 "METHODS": ("GET", "PUT", "DELETE"),
566 "ROLE_PERMISSION": "slice_templates:id:",
567 },
568 },
569 "netslice_templates": {
570 "METHODS": ("GET", "POST"),
571 "ROLE_PERMISSION": "slice_templates:",
572 "<ID>": {
573 "METHODS": ("GET", "DELETE"),
574 "TODO": ("PATCH",),
575 "ROLE_PERMISSION": "slice_templates:id:",
576 "nst_content": {
577 "METHODS": ("GET", "PUT"),
578 "ROLE_PERMISSION": "slice_templates:id:content:",
579 },
580 "nst": {
581 "METHODS": ("GET",), # descriptor inside package
582 "ROLE_PERMISSION": "slice_templates:id:content:",
583 },
584 "artifacts": {
585 "METHODS": ("GET",),
586 "ROLE_PERMISSION": "slice_templates:id:content:",
587 "*": None,
588 },
589 },
590 },
591 "subscriptions": {
592 "TODO": ("GET", "POST"),
593 "<ID>": {"TODO": ("GET", "DELETE")},
594 },
tierno701018c2019-06-25 11:13:14 +0000595 }
596 },
597 "nsilcm": {
598 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100599 "netslice_instances_content": {
600 "METHODS": ("GET", "POST"),
601 "ROLE_PERMISSION": "slice_instances:",
602 "<ID>": {
603 "METHODS": ("GET", "DELETE"),
604 "ROLE_PERMISSION": "slice_instances:id:",
605 },
606 },
607 "netslice_instances": {
608 "METHODS": ("GET", "POST"),
609 "ROLE_PERMISSION": "slice_instances:",
610 "<ID>": {
611 "METHODS": ("GET", "DELETE"),
612 "ROLE_PERMISSION": "slice_instances:id:",
613 "terminate": {
614 "METHODS": ("POST",),
615 "ROLE_PERMISSION": "slice_instances:id:terminate:",
616 },
617 "instantiate": {
618 "METHODS": ("POST",),
619 "ROLE_PERMISSION": "slice_instances:id:instantiate:",
620 },
621 "action": {
622 "METHODS": ("POST",),
623 "ROLE_PERMISSION": "slice_instances:id:action:",
624 },
625 },
626 },
627 "nsi_lcm_op_occs": {
628 "METHODS": ("GET",),
629 "ROLE_PERMISSION": "slice_instances:opps:",
630 "<ID>": {
631 "METHODS": ("GET",),
632 "ROLE_PERMISSION": "slice_instances:opps:id:",
633 },
634 },
tierno701018c2019-06-25 11:13:14 +0000635 }
636 },
637 "nspm": {
638 "v1": {
639 "pm_jobs": {
640 "<ID>": {
641 "reports": {
garciadeblas4568a372021-03-24 09:19:48 +0100642 "<ID>": {
643 "METHODS": ("GET",),
644 "ROLE_PERMISSION": "reports:id:",
645 }
tierno701018c2019-06-25 11:13:14 +0000646 }
647 },
648 },
649 },
650 },
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000651 "nsfm": {
652 "v1": {
garciadeblasf2af4a12023-01-24 16:56:54 +0100653 "alarms": {
654 "METHODS": ("GET", "PATCH"),
655 "ROLE_PERMISSION": "alarms:",
656 "<ID>": {
657 "METHODS": ("GET", "PATCH"),
658 "ROLE_PERMISSION": "alarms:id:",
659 },
660 }
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000661 },
662 },
tierno701018c2019-06-25 11:13:14 +0000663}
664
tiernoc94c3df2018-02-09 15:38:54 +0100665
666class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100667 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
668 Exception.__init__(self, message)
669 self.http_code = http_code
670
671
672class Server(object):
673 instance = 0
674 # to decode bytes to str
675 reader = getreader("utf-8")
676
677 def __init__(self):
678 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000679 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100680 self.engine = Engine(self.authenticator)
tiernoc94c3df2018-02-09 15:38:54 +0100681
tiernoc94c3df2018-02-09 15:38:54 +0100682 def _format_in(self, kwargs):
garciadeblasf2af4a12023-01-24 16:56:54 +0100683 error_text = "" # error_text must be initialized outside try
tiernoc94c3df2018-02-09 15:38:54 +0100684 try:
685 indata = None
686 if cherrypy.request.body.length:
687 error_text = "Invalid input format "
688
689 if "Content-Type" in cherrypy.request.headers:
690 if "application/json" in cherrypy.request.headers["Content-Type"]:
691 error_text = "Invalid json format "
692 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100693 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100694 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
695 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100696 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100697 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100698 elif (
699 "application/binary" in cherrypy.request.headers["Content-Type"]
700 or "application/gzip"
701 in cherrypy.request.headers["Content-Type"]
702 or "application/zip" in cherrypy.request.headers["Content-Type"]
703 or "text/plain" in cherrypy.request.headers["Content-Type"]
704 ):
tiernof27c79b2018-03-12 17:08:42 +0100705 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100706 elif (
707 "multipart/form-data"
708 in cherrypy.request.headers["Content-Type"]
709 ):
tiernoc94c3df2018-02-09 15:38:54 +0100710 if "descriptor_file" in kwargs:
711 filecontent = kwargs.pop("descriptor_file")
712 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100713 raise NbiException(
714 "empty file or content", HTTPStatus.BAD_REQUEST
715 )
tiernof27c79b2018-03-12 17:08:42 +0100716 indata = filecontent.file # .read()
tiernoc94c3df2018-02-09 15:38:54 +0100717 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100718 cherrypy.request.headers[
719 "Content-Type"
720 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100721 else:
722 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
723 # "Only 'Content-Type' of type 'application/json' or
724 # 'application/yaml' for input format are available")
725 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100726 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100727 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100728 else:
729 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100730 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100731 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100732 if not indata:
733 indata = {}
734
tiernoc94c3df2018-02-09 15:38:54 +0100735 format_yaml = False
736 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
737 format_yaml = True
738
739 for k, v in kwargs.items():
740 if isinstance(v, str):
741 if v == "":
742 kwargs[k] = None
743 elif format_yaml:
744 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100745 kwargs[k] = yaml.safe_load(v)
tiernoe1281182018-05-22 12:24:36 +0200746 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100747 pass
garciadeblas4568a372021-03-24 09:19:48 +0100748 elif (
749 k.endswith(".gt")
750 or k.endswith(".lt")
751 or k.endswith(".gte")
752 or k.endswith(".lte")
753 ):
tiernoc94c3df2018-02-09 15:38:54 +0100754 try:
755 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200756 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100757 try:
758 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200759 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100760 pass
761 elif v.find(",") > 0:
762 kwargs[k] = v.split(",")
763 elif isinstance(v, (list, tuple)):
764 for index in range(0, len(v)):
765 if v[index] == "":
766 v[index] = None
767 elif format_yaml:
768 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100769 v[index] = yaml.safe_load(v[index])
tiernoe1281182018-05-22 12:24:36 +0200770 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100771 pass
772
tiernof27c79b2018-03-12 17:08:42 +0100773 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100774 except (ValueError, yaml.YAMLError) as exc:
775 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
776 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100777 raise NbiException(
778 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
779 )
tiernob92094f2018-05-11 13:44:22 +0200780 except Exception as exc:
781 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100782
783 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000784 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100785 """
786 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100787 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000788 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000789 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100790 :return: None
791 """
tierno0f98af52018-03-19 10:28:22 +0100792 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100793 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100794 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100795 return html.format(
796 data, cherrypy.request, cherrypy.response, token_info
797 )
tierno09c073e2018-04-26 13:36:48 +0200798 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100799 return
800 elif hasattr(data, "read"): # file object
801 if _format:
802 cherrypy.response.headers["Content-Type"] = _format
803 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100804 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100805 else:
garciadeblas4568a372021-03-24 09:19:48 +0100806 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100807 # TODO check that cherrypy close file. If not implement pending things to close per thread next
808 return data
tierno0f98af52018-03-19 10:28:22 +0100809 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000810 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100811 return html.format(
812 data, cherrypy.request, cherrypy.response, token_info
813 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000814 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100815 pass
garciadeblas4568a372021-03-24 09:19:48 +0100816 elif "application/json" in accept or (
817 cherrypy.response.status and cherrypy.response.status >= 300
818 ):
819 cherrypy.response.headers[
820 "Content-Type"
821 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000822 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100823 return a.encode("utf8")
824 cherrypy.response.headers["Content-Type"] = "application/yaml"
825 return yaml.safe_dump(
826 data,
827 explicit_start=True,
828 indent=4,
829 default_flow_style=False,
830 tags=False,
831 encoding="utf-8",
832 allow_unicode=True,
833 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +0100834
835 @cherrypy.expose
836 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +0000837 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100838 try:
839 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +0000840 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +0100841 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +0100842 else:
garciadeblas4568a372021-03-24 09:19:48 +0100843 raise cherrypy.HTTPError(
844 HTTPStatus.METHOD_NOT_ALLOWED.value,
845 "Method {} not allowed for tokens".format(cherrypy.request.method),
846 )
tiernoc94c3df2018-02-09 15:38:54 +0100847
tierno701018c2019-06-25 11:13:14 +0000848 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100849
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100850 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +0000851 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +0100852 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +0000853 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100854
855 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +0200856 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +0200857 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +0200858 try:
859 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +0100860 raise NbiException(
861 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
862 )
tierno55945e72018-04-06 16:40:27 +0200863 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +0100864 raise NbiException(
865 "Invalid URL or query string for version",
866 HTTPStatus.METHOD_NOT_ALLOWED,
867 )
tierno9c630112019-08-29 14:21:41 +0000868 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +0000869 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
870 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +0200871 except NbiException as e:
872 cherrypy.response.status = e.http_code.value
873 problem_details = {
874 "code": e.http_code.name,
875 "status": e.http_code.value,
876 "detail": str(e),
877 }
878 return self._format_out(problem_details, None)
879
tierno12eac3c2020-03-19 23:22:08 +0000880 def domain(self):
881 try:
882 domains = {
garciadeblas4568a372021-03-24 09:19:48 +0100883 "user_domain_name": cherrypy.tree.apps["/osm"]
884 .config["authentication"]
885 .get("user_domain_name"),
886 "project_domain_name": cherrypy.tree.apps["/osm"]
887 .config["authentication"]
888 .get("project_domain_name"),
889 }
tierno12eac3c2020-03-19 23:22:08 +0000890 return self._format_out(domains)
891 except NbiException as e:
892 cherrypy.response.status = e.http_code.value
893 problem_details = {
894 "code": e.http_code.name,
895 "status": e.http_code.value,
896 "detail": str(e),
897 }
898 return self._format_out(problem_details, None)
899
tiernoa5035702019-07-29 08:54:42 +0000900 @staticmethod
901 def _format_login(token_info):
902 """
903 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
904 log this information
905 :param token_info: Dictionary with token content
906 :return: None
907 """
908 cherrypy.request.login = token_info.get("username", "-")
909 if token_info.get("project_name"):
910 cherrypy.request.login += "/" + token_info["project_name"]
911 if token_info.get("id"):
912 cherrypy.request.login += ";session=" + token_info["id"][0:12]
913
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000914 # NS Fault Management
915 @cherrypy.expose
garciadeblasf2af4a12023-01-24 16:56:54 +0100916 def nsfm(
917 self,
918 version=None,
919 topic=None,
920 uuid=None,
921 project_name=None,
922 ns_id=None,
923 *args,
924 **kwargs
925 ):
926 if topic == "alarms":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000927 try:
928 method = cherrypy.request.method
garciadeblasf2af4a12023-01-24 16:56:54 +0100929 role_permission = self._check_valid_url_method(
930 method, "nsfm", version, topic, None, None, *args
931 )
932 query_string_operations = self._extract_query_string_operations(
933 kwargs, method
934 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000935
garciadeblasf2af4a12023-01-24 16:56:54 +0100936 self.authenticator.authorize(
937 role_permission, query_string_operations, None
938 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000939
940 # to handle get request
garciadeblasf2af4a12023-01-24 16:56:54 +0100941 if cherrypy.request.method == "GET":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000942 # if request is on basis of uuid
garciadeblasf2af4a12023-01-24 16:56:54 +0100943 if uuid and uuid != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000944 try:
945 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
garciadeblasf2af4a12023-01-24 16:56:54 +0100946 alarm_action = self.engine.db.get_one(
947 "alarms_action", {"uuid": uuid}
948 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000949 alarm.update(alarm_action)
garciadeblasf2af4a12023-01-24 16:56:54 +0100950 vnf = self.engine.db.get_one(
951 "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}
952 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000953 alarm["vnf-id"] = vnf["_id"]
954 return self._format_out(str(alarm))
955 except Exception:
956 return self._format_out("Please provide valid alarm uuid")
garciadeblasf2af4a12023-01-24 16:56:54 +0100957 elif ns_id and ns_id != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000958 # if request is on basis of ns_id
959 try:
garciadeblasf2af4a12023-01-24 16:56:54 +0100960 alarms = self.engine.db.get_list(
961 "alarms", {"tags.ns_id": ns_id}
962 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000963 for alarm in alarms:
garciadeblasf2af4a12023-01-24 16:56:54 +0100964 alarm_action = self.engine.db.get_one(
965 "alarms_action", {"uuid": alarm["uuid"]}
966 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000967 alarm.update(alarm_action)
968 return self._format_out(str(alarms))
969 except Exception:
970 return self._format_out("Please provide valid ns id")
971 else:
972 # to return only alarm which are related to given project
garciadeblasf2af4a12023-01-24 16:56:54 +0100973 project = self.engine.db.get_one(
974 "projects", {"name": project_name}
975 )
976 project_id = project.get("_id")
977 ns_list = self.engine.db.get_list(
978 "nsrs", {"_admin.projects_read": project_id}
979 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000980 ns_ids = []
981 for ns in ns_list:
982 ns_ids.append(ns.get("_id"))
983 alarms = self.engine.db.get_list("alarms")
garciadeblasf2af4a12023-01-24 16:56:54 +0100984 alarm_list = [
985 alarm
986 for alarm in alarms
987 if alarm["tags"]["ns_id"] in ns_ids
988 ]
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000989 for alrm in alarm_list:
garciadeblasf2af4a12023-01-24 16:56:54 +0100990 action = self.engine.db.get_one(
991 "alarms_action", {"uuid": alrm.get("uuid")}
992 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000993 alrm.update(action)
994 return self._format_out(str(alarm_list))
995 # to handle patch request for alarm update
garciadeblasf2af4a12023-01-24 16:56:54 +0100996 elif cherrypy.request.method == "PATCH":
garciadeblas4cd875d2023-02-14 19:05:34 +0100997 data = yaml.safe_load(cherrypy.request.body)
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000998 try:
999 # check if uuid is valid
1000 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
1001 except Exception:
1002 return self._format_out("Please provide valid alarm uuid.")
1003 if data.get("is_enable") is not None:
1004 if data.get("is_enable"):
garciadeblasf2af4a12023-01-24 16:56:54 +01001005 alarm_status = "ok"
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001006 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001007 alarm_status = "disabled"
1008 self.engine.db.set_one(
1009 "alarms",
1010 {"uuid": data.get("uuid")},
1011 {"alarm_status": alarm_status},
1012 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001013 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001014 self.engine.db.set_one(
1015 "alarms",
1016 {"uuid": data.get("uuid")},
1017 {"threshold": data.get("threshold")},
1018 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001019 return self._format_out("Alarm updated")
1020 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01001021 if isinstance(
1022 e,
1023 (
1024 NbiException,
1025 EngineException,
1026 DbException,
1027 FsException,
1028 MsgException,
1029 AuthException,
1030 ValidationError,
1031 AuthconnException,
1032 ),
1033 ):
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001034 http_code_value = cherrypy.response.status = e.http_code.value
1035 http_code_name = e.http_code.name
1036 cherrypy.log("Exception {}".format(e))
1037 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001038 http_code_value = (
1039 cherrypy.response.status
1040 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001041 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
1042 http_code_name = HTTPStatus.BAD_REQUEST.name
1043 problem_details = {
1044 "code": http_code_name,
1045 "status": http_code_value,
1046 "detail": str(e),
1047 }
1048 return self._format_out(problem_details)
1049
tierno55945e72018-04-06 16:40:27 +02001050 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +01001051 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +00001052 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001053 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +01001054 indata = self._format_in(kwargs)
1055 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001056 raise NbiException(
1057 "Expected application/yaml or application/json Content-Type",
1058 HTTPStatus.BAD_REQUEST,
1059 )
tiernoa5035702019-07-29 08:54:42 +00001060
1061 if method == "GET":
1062 token_info = self.authenticator.authorize()
1063 # for logging
1064 self._format_login(token_info)
1065 if token_id:
1066 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +01001067 else:
tiernoa5035702019-07-29 08:54:42 +00001068 outdata = self.authenticator.get_token_list(token_info)
1069 elif method == "POST":
1070 try:
1071 token_info = self.authenticator.authorize()
1072 except Exception:
1073 token_info = None
1074 if kwargs:
1075 indata.update(kwargs)
1076 # This is needed to log the user when authentication fails
1077 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +01001078 outdata = token_info = self.authenticator.new_token(
1079 token_info, indata, cherrypy.request.remote
1080 )
garciadeblasf2af4a12023-01-24 16:56:54 +01001081 cherrypy.session["Authorization"] = outdata["_id"] # pylint: disable=E1101
tiernoa5035702019-07-29 08:54:42 +00001082 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1083 # for logging
1084 self._format_login(token_info)
selvi.ja9a1fc82022-04-04 06:54:30 +00001085 # password expiry check
1086 if self.authenticator.check_password_expiry(outdata):
garciadeblasf2af4a12023-01-24 16:56:54 +01001087 outdata = {
1088 "id": outdata["id"],
1089 "message": "change_password",
1090 "user_id": outdata["user_id"],
1091 }
tiernoa5035702019-07-29 08:54:42 +00001092 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1093 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
elumalai7802ff82023-04-24 20:38:32 +05301094 cef_event(
1095 cef_logger,
1096 {
1097 "name": "User Login",
1098 "sourceUserName": token_info.get("username"),
1099 "message": "User Logged In, Project={} Outcome=Success".format(
1100 token_info.get("project_name")
1101 ),
1102 },
1103 )
1104 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001105 elif method == "DELETE":
1106 if not token_id and "id" in kwargs:
1107 token_id = kwargs["id"]
1108 elif not token_id:
1109 token_info = self.authenticator.authorize()
1110 # for logging
1111 self._format_login(token_info)
1112 token_id = token_info["_id"]
Rahulc72bc8e2023-12-05 11:54:38 +00001113 if current_backend != "keystone":
1114 token_details = self.engine.db.get_one("tokens", {"_id": token_id})
1115 current_user = token_details.get("username")
1116 current_project = token_details.get("project_name")
1117 else:
1118 current_user = "keystone backend"
1119 current_project = "keystone backend"
tiernoa5035702019-07-29 08:54:42 +00001120 outdata = self.authenticator.del_token(token_id)
1121 token_info = None
garciadeblasf2af4a12023-01-24 16:56:54 +01001122 cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101
elumalai7802ff82023-04-24 20:38:32 +05301123 cef_event(
1124 cef_logger,
1125 {
1126 "name": "User Logout",
1127 "sourceUserName": current_user,
1128 "message": "User Logged Out, Project={} Outcome=Success".format(
1129 current_project
1130 ),
1131 },
1132 )
1133 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001134 # cherrypy.response.cookie["Authorization"] = token_id
1135 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1136 else:
garciadeblas4568a372021-03-24 09:19:48 +01001137 raise NbiException(
1138 "Method {} not allowed for token".format(method),
1139 HTTPStatus.METHOD_NOT_ALLOWED,
1140 )
tiernoa5035702019-07-29 08:54:42 +00001141 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001142
1143 @cherrypy.expose
1144 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001145 if not cherrypy.config.get("server.enable_test") or (
1146 isinstance(cherrypy.config["server.enable_test"], str)
1147 and cherrypy.config["server.enable_test"].lower() == "false"
1148 ):
tierno4836bac2020-01-15 14:41:48 +00001149 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1150 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001151 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001152 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001153 return (
1154 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1155 "sleep/<time>\nmessage/topic\n</pre></html>"
1156 )
tiernof27c79b2018-03-12 17:08:42 +01001157
1158 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001159 try:
1160 # self.engine.load_dbase(cherrypy.request.app.config)
garciadeblasf2af4a12023-01-24 16:56:54 +01001161 pid = self.authenticator.create_admin_project()
1162 self.authenticator.create_admin_user(pid)
tiernoc94c3df2018-02-09 15:38:54 +01001163 return "Done. User 'admin', password 'admin' created"
1164 except Exception:
1165 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1166 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001167 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001168 return cherrypy.lib.static.serve_file(
1169 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1170 "text/plain",
1171 "attachment",
1172 )
tiernof27c79b2018-03-12 17:08:42 +01001173 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001174 f_path = (
1175 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1176 )
tiernof27c79b2018-03-12 17:08:42 +01001177 f = open(f_path, "r")
1178 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001179 return f
tierno55945e72018-04-06 16:40:27 +02001180
tiernof27c79b2018-03-12 17:08:42 +01001181 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001182 deleted_info = self.engine.db.del_list(args[1], kwargs)
1183 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1184 elif len(args) and args[0] == "fs-clear":
1185 if len(args) >= 2:
1186 folders = (args[1],)
1187 else:
1188 folders = self.engine.fs.dir_ls(".")
1189 for folder in folders:
1190 self.engine.fs.file_delete(folder)
1191 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001192 elif args and args[0] == "login":
1193 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001194 cherrypy.response.headers[
1195 "WWW-Authenticate"
1196 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001197 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1198 elif args and args[0] == "login2":
1199 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001200 cherrypy.response.headers[
1201 "WWW-Authenticate"
1202 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001203 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1204 elif args and args[0] == "sleep":
1205 sleep_time = 5
1206 try:
1207 sleep_time = int(args[1])
1208 except Exception:
1209 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1210 return self._format_out("Database already initialized")
1211 thread_info = cherrypy.thread_data
1212 print(thread_info)
1213 time.sleep(sleep_time)
1214 # thread_info
1215 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001216 main_topic = args[1]
1217 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001218 try:
garciadeblas4568a372021-03-24 09:19:48 +01001219 if cherrypy.request.method == "POST":
garciadeblas4cd875d2023-02-14 19:05:34 +01001220 to_send = yaml.safe_load(cherrypy.request.body)
tierno55945e72018-04-06 16:40:27 +02001221 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001222 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001223 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001224 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001225 for k, v in kwargs.items():
garciadeblas4cd875d2023-02-14 19:05:34 +01001226 v_dict = yaml.safe_load(v)
tiernof1509b22020-05-12 14:32:37 +00001227 self.engine.msg.write(main_topic, k, v_dict)
1228 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001229 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001230 return_text += "Error: " + str(e)
1231 return_text += "</pre></html>\n"
1232 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001233
1234 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001235 "<html><pre>\nheaders:\n args: {}\n".format(args)
1236 + " kwargs: {}\n".format(kwargs)
1237 + " headers: {}\n".format(cherrypy.request.headers)
1238 + " path_info: {}\n".format(cherrypy.request.path_info)
1239 + " query_string: {}\n".format(cherrypy.request.query_string)
garciadeblasf2af4a12023-01-24 16:56:54 +01001240 + " session: {}\n".format(cherrypy.session) # pylint: disable=E1101
garciadeblas4568a372021-03-24 09:19:48 +01001241 + " cookie: {}\n".format(cherrypy.request.cookie)
1242 + " method: {}\n".format(cherrypy.request.method)
garciadeblasf2af4a12023-01-24 16:56:54 +01001243 + " session: {}\n".format(
1244 cherrypy.session.get("fieldname") # pylint: disable=E1101
1245 )
garciadeblas4568a372021-03-24 09:19:48 +01001246 + " body:\n"
1247 )
tiernoc94c3df2018-02-09 15:38:54 +01001248 return_text += " length: {}\n".format(cherrypy.request.body.length)
1249 if cherrypy.request.body.length:
1250 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001251 str(
1252 cherrypy.request.body.read(
1253 int(cherrypy.request.headers.get("Content-Length", 0))
1254 )
1255 )
1256 )
tiernoc94c3df2018-02-09 15:38:54 +01001257 if thread_info:
1258 return_text += "thread: {}\n".format(thread_info)
1259 return_text += "</pre></html>"
1260 return return_text
1261
tierno701018c2019-06-25 11:13:14 +00001262 @staticmethod
1263 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001264 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001265 raise NbiException(
1266 "URL must contain at least 'main_topic/version/topic'",
1267 HTTPStatus.METHOD_NOT_ALLOWED,
1268 )
tiernof27c79b2018-03-12 17:08:42 +01001269
tierno701018c2019-06-25 11:13:14 +00001270 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001271 for arg in args:
1272 if arg is None:
1273 break
1274 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001275 raise NbiException(
1276 "URL contains unexpected extra items '{}'".format(arg),
1277 HTTPStatus.METHOD_NOT_ALLOWED,
1278 )
tiernof27c79b2018-03-12 17:08:42 +01001279
1280 if arg in reference:
1281 reference = reference[arg]
1282 elif "<ID>" in reference:
1283 reference = reference["<ID>"]
1284 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001285 # if there is content
1286 if reference["*"]:
1287 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001288 break
1289 else:
garciadeblas4568a372021-03-24 09:19:48 +01001290 raise NbiException(
1291 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1292 )
tiernof27c79b2018-03-12 17:08:42 +01001293 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001294 raise NbiException(
1295 "Method {} not supported yet for this URL".format(method),
1296 HTTPStatus.NOT_IMPLEMENTED,
1297 )
tierno2236d202018-05-16 19:05:16 +02001298 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001299 raise NbiException(
1300 "Method {} not supported for this URL".format(method),
1301 HTTPStatus.METHOD_NOT_ALLOWED,
1302 )
tierno701018c2019-06-25 11:13:14 +00001303 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001304
1305 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001306 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001307 """
1308 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001309 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001310 :param version:
tiernob24258a2018-10-04 18:39:49 +02001311 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001312 :param id:
1313 :return: None
1314 """
1315 # 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 +01001316 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1317 main_topic, version, topic, id
1318 )
tiernof27c79b2018-03-12 17:08:42 +01001319 return
1320
tierno65ca36d2019-02-12 19:27:52 +01001321 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001322 def _extract_query_string_operations(kwargs, method):
1323 """
1324
1325 :param kwargs:
1326 :return:
1327 """
1328 query_string_operations = []
1329 if kwargs:
1330 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1331 if qs in kwargs and kwargs[qs].lower() != "false":
1332 query_string_operations.append(qs.lower() + ":" + method.lower())
1333 return query_string_operations
1334
1335 @staticmethod
1336 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001337 """
1338 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1339 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001340 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001341 :param kwargs: query string input.
1342 :param method: http method: GET, POSST, PUT, ...
1343 :param _id:
1344 :return: admin_query dictionary with keys:
1345 public: True, False or None
1346 force: True or False
1347 project_id: tuple with projects used for accessing an element
1348 set_project: tuple with projects that a created element will belong to
1349 method: show, list, delete, write
1350 """
garciadeblas4568a372021-03-24 09:19:48 +01001351 admin_query = {
1352 "force": False,
1353 "project_id": (token_info["project_id"],),
1354 "username": token_info["username"],
1355 "admin": token_info["admin"],
1356 "public": None,
1357 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1358 }
tierno65ca36d2019-02-12 19:27:52 +01001359 if kwargs:
1360 # FORCE
1361 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001362 if (
1363 kwargs["FORCE"].lower() != "false"
1364 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001365 admin_query["force"] = True
1366 del kwargs["FORCE"]
1367 # PUBLIC
1368 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001369 if (
1370 kwargs["PUBLIC"].lower() != "false"
1371 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001372 admin_query["public"] = True
1373 else:
1374 admin_query["public"] = False
1375 del kwargs["PUBLIC"]
1376 # ADMIN
1377 if "ADMIN" in kwargs:
1378 behave_as = kwargs.pop("ADMIN")
1379 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001380 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001381 raise NbiException(
1382 "Only admin projects can use 'ADMIN' query string",
1383 HTTPStatus.UNAUTHORIZED,
1384 )
1385 if (
1386 not behave_as or behave_as.lower() == "true"
1387 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001388 admin_query["project_id"] = ()
1389 elif isinstance(behave_as, (list, tuple)):
1390 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001391 else: # isinstance(behave_as, str)
1392 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001393 if "SET_PROJECT" in kwargs:
1394 set_project = kwargs.pop("SET_PROJECT")
1395 if not set_project:
1396 admin_query["set_project"] = list(admin_query["project_id"])
1397 else:
1398 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001399 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001400 if admin_query["project_id"]:
1401 for p in set_project:
1402 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001403 raise NbiException(
1404 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1405 "'ADMIN='{p}'".format(p=p),
1406 HTTPStatus.UNAUTHORIZED,
1407 )
tierno65ca36d2019-02-12 19:27:52 +01001408 admin_query["set_project"] = set_project
1409
1410 # PROJECT_READ
1411 # if "PROJECT_READ" in kwargs:
1412 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001413 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001414 if method == "GET":
1415 if _id:
1416 admin_query["method"] = "show"
1417 else:
1418 admin_query["method"] = "list"
1419 elif method == "DELETE":
1420 admin_query["method"] = "delete"
1421 else:
1422 admin_query["method"] = "write"
1423 return admin_query
1424
tiernoc94c3df2018-02-09 15:38:54 +01001425 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001426 def default(
1427 self,
1428 main_topic=None,
1429 version=None,
1430 topic=None,
1431 _id=None,
1432 item=None,
1433 *args,
1434 **kwargs
1435 ):
tierno701018c2019-06-25 11:13:14 +00001436 token_info = None
selvi.j9919bbc2023-04-26 12:22:13 +00001437 outdata = {}
tiernof27c79b2018-03-12 17:08:42 +01001438 _format = None
tierno0f98af52018-03-19 10:28:22 +01001439 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001440 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001441 rollback = []
tierno701018c2019-06-25 11:13:14 +00001442 engine_session = None
elumalai7802ff82023-04-24 20:38:32 +05301443 url_id = ""
1444 log_mapping = {
1445 "POST": "Creating",
1446 "GET": "Fetching",
1447 "DELETE": "Deleting",
1448 "PUT": "Updating",
1449 "PATCH": "Updating",
1450 }
tiernoc94c3df2018-02-09 15:38:54 +01001451 try:
tiernob24258a2018-10-04 18:39:49 +02001452 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001453 raise NbiException(
1454 "URL must contain at least 'main_topic/version/topic'",
1455 HTTPStatus.METHOD_NOT_ALLOWED,
1456 )
1457 if main_topic not in (
1458 "admin",
1459 "vnfpkgm",
1460 "nsd",
1461 "nslcm",
1462 "pdu",
1463 "nst",
1464 "nsilcm",
1465 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001466 "vnflcm",
garciadeblas4568a372021-03-24 09:19:48 +01001467 ):
1468 raise NbiException(
1469 "URL main_topic '{}' not supported".format(main_topic),
1470 HTTPStatus.METHOD_NOT_ALLOWED,
1471 )
1472 if version != "v1":
1473 raise NbiException(
1474 "URL version '{}' not supported".format(version),
1475 HTTPStatus.METHOD_NOT_ALLOWED,
1476 )
elumalai7802ff82023-04-24 20:38:32 +05301477 if _id is not None:
1478 url_id = _id
tiernoc94c3df2018-02-09 15:38:54 +01001479
garciadeblas4568a372021-03-24 09:19:48 +01001480 if (
1481 kwargs
1482 and "METHOD" in kwargs
1483 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1484 ):
tiernof27c79b2018-03-12 17:08:42 +01001485 method = kwargs.pop("METHOD")
1486 else:
1487 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001488
garciadeblas4568a372021-03-24 09:19:48 +01001489 role_permission = self._check_valid_url_method(
1490 method, main_topic, version, topic, _id, item, *args
1491 )
1492 query_string_operations = self._extract_query_string_operations(
1493 kwargs, method
1494 )
tiernob24258a2018-10-04 18:39:49 +02001495 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001496 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001497 token_info = self.authenticator.authorize(
1498 role_permission, query_string_operations, _id
1499 )
tierno12eac3c2020-03-19 23:22:08 +00001500 if main_topic == "admin" and topic == "domains":
1501 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001502 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001503 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001504 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301505
vijay.r35ef2f72019-04-30 17:55:49 +05301506 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001507 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001508
tiernob24258a2018-10-04 18:39:49 +02001509 if main_topic == "nsd":
1510 engine_topic = "nsds"
kayal2001f71c2e82024-06-25 15:26:24 +05301511 if topic == "ns_config_template":
1512 engine_topic = "nsconfigtemps"
tiernob24258a2018-10-04 18:39:49 +02001513 elif main_topic == "vnfpkgm":
1514 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001515 if topic == "vnfpkg_op_occs":
1516 engine_topic = "vnfpkgops"
1517 if topic == "vnf_packages" and item == "action":
1518 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001519 elif main_topic == "nslcm":
1520 engine_topic = "nsrs"
1521 if topic == "ns_lcm_op_occs":
1522 engine_topic = "nslcmops"
1523 if topic == "vnfrs" or topic == "vnf_instances":
1524 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001525 elif main_topic == "vnflcm":
1526 if topic == "vnf_lcm_op_occs":
1527 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001528 elif main_topic == "nst":
1529 engine_topic = "nsts"
1530 elif main_topic == "nsilcm":
1531 engine_topic = "nsis"
1532 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001533 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001534 elif main_topic == "pdu":
1535 engine_topic = "pdus"
garciadeblas4568a372021-03-24 09:19:48 +01001536 if (
1537 engine_topic == "vims"
1538 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001539 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001540
preethika.p329b8182020-04-22 12:25:39 +05301541 if topic == "subscriptions":
1542 engine_topic = main_topic + "_" + topic
1543
tiernoc94c3df2018-02-09 15:38:54 +01001544 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001545 if item in (
1546 "nsd_content",
1547 "package_content",
1548 "artifacts",
1549 "vnfd",
1550 "nsd",
1551 "nst",
1552 "nst_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301553 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001554 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001555 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001556 path = "$DESCRIPTOR"
1557 elif args:
1558 path = args
tiernob24258a2018-10-04 18:39:49 +02001559 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001560 path = ()
1561 else:
1562 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001563 file, _format = self.engine.get_file(
1564 engine_session,
1565 engine_topic,
1566 _id,
1567 path,
1568 cherrypy.request.headers.get("Accept"),
1569 )
tiernof27c79b2018-03-12 17:08:42 +01001570 outdata = file
1571 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001572 outdata = self.engine.get_item_list(
1573 engine_session, engine_topic, kwargs, api_req=True
1574 )
tiernoc94c3df2018-02-09 15:38:54 +01001575 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301576 if item == "reports":
1577 # TODO check that project_id (_id in this context) has permissions
1578 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301579 filter_q = None
1580 if "vcaStatusRefresh" in kwargs:
1581 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
garciadeblasf2af4a12023-01-24 16:56:54 +01001582 outdata = self.engine.get_item(
1583 engine_session, engine_topic, _id, filter_q, True
1584 )
delacruzramo271d2002019-12-02 21:00:37 +01001585
tiernof27c79b2018-03-12 17:08:42 +01001586 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001587 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001588 if topic in (
1589 "ns_descriptors_content",
1590 "vnf_packages_content",
1591 "netslice_templates_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301592 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001593 ):
tiernof27c79b2018-03-12 17:08:42 +01001594 _id = cherrypy.request.headers.get("Transaction-Id")
1595 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001596 _id, _ = self.engine.new_item(
1597 rollback,
1598 engine_session,
1599 engine_topic,
1600 {},
1601 None,
1602 cherrypy.request.headers,
1603 )
1604 completed = self.engine.upload_content(
1605 engine_session,
1606 engine_topic,
1607 _id,
1608 indata,
1609 kwargs,
1610 cherrypy.request.headers,
1611 )
tiernof27c79b2018-03-12 17:08:42 +01001612 if completed:
tiernob24258a2018-10-04 18:39:49 +02001613 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001614 else:
1615 cherrypy.response.headers["Transaction-Id"] = _id
1616 outdata = {"id": _id}
tiernob24258a2018-10-04 18:39:49 +02001617 elif topic == "ns_instances_content":
1618 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001619 _id, _ = self.engine.new_item(
1620 rollback, engine_session, engine_topic, indata, kwargs
1621 )
tiernob24258a2018-10-04 18:39:49 +02001622 # creates nslcmop
1623 indata["lcmOperationType"] = "instantiate"
1624 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001625 nslcmop_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001626 rollback, engine_session, "nslcmops", indata, None
1627 )
tiernob24258a2018-10-04 18:39:49 +02001628 self._set_location_header(main_topic, version, topic, _id)
garciadeblasf53612b2024-07-12 14:44:37 +02001629 outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName}
adurti3af50952024-05-31 11:36:57 +05301630 elif topic == "ns_instances_terminate":
1631 if indata.get("ns_ids"):
1632 for ns_id in indata.get("ns_ids"):
1633 nslcmop_desc = {
1634 "lcmOperationType": "terminate",
1635 "nsInstanceId": ns_id,
1636 "autoremove": indata.get("autoremove")
1637 if "autoremove" in indata
1638 else True,
1639 }
1640 op_id, _, _ = self.engine.new_item(
1641 rollback,
1642 engine_session,
1643 "nslcmops",
1644 nslcmop_desc,
1645 kwargs,
1646 )
1647 if not op_id:
1648 _ = self.engine.del_item(
1649 engine_session, engine_topic, ns_id
1650 )
1651 outdata = {"ns_ids": indata.get("ns_ids")}
1652 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001653 elif topic == "ns_instances" and item:
1654 indata["lcmOperationType"] = item
1655 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001656 _id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001657 rollback, engine_session, "nslcmops", indata, kwargs
1658 )
1659 self._set_location_header(
1660 main_topic, version, "ns_lcm_op_occs", _id
1661 )
garciadeblasf53612b2024-07-12 14:44:37 +02001662 outdata = {"id": _id, "nsName": nsName}
tierno65acb4d2018-04-06 16:42:40 +02001663 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001664 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001665 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001666 _id, _ = self.engine.new_item(
1667 rollback, engine_session, engine_topic, indata, kwargs
1668 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001669 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001670 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001671 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001672 nsilcmop_id, _ = self.engine.new_item(
1673 rollback, engine_session, "nsilcmops", indata, kwargs
1674 )
kuuse078f55e2019-05-16 19:24:21 +02001675 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001676 elif topic == "netslice_instances" and item:
1677 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001678 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001679 _id, _ = self.engine.new_item(
1680 rollback, engine_session, "nsilcmops", indata, kwargs
1681 )
1682 self._set_location_header(
1683 main_topic, version, "nsi_lcm_op_occs", _id
1684 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001685 outdata = {"id": _id}
1686 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001687 elif topic == "vnf_packages" and item == "action":
1688 indata["lcmOperationType"] = item
1689 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001690 _id, _ = self.engine.new_item(
1691 rollback, engine_session, "vnfpkgops", indata, kwargs
1692 )
1693 self._set_location_header(
1694 main_topic, version, "vnfpkg_op_occs", _id
1695 )
delacruzramo271d2002019-12-02 21:00:37 +01001696 outdata = {"id": _id}
1697 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301698 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001699 _id, _ = self.engine.new_item(
1700 rollback, engine_session, engine_topic, indata, kwargs
1701 )
preethika.p329b8182020-04-22 12:25:39 +05301702 self._set_location_header(main_topic, version, topic, _id)
1703 link = {}
1704 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001705 outdata = {
1706 "id": _id,
1707 "filter": indata["filter"],
1708 "callbackUri": indata["CallbackUri"],
1709 "_links": link,
1710 }
preethika.p329b8182020-04-22 12:25:39 +05301711 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001712 elif topic == "vnf_instances" and item:
1713 indata["lcmOperationType"] = item
1714 indata["vnfInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001715 _id, nsName, _ = self.engine.new_item(
garciadeblasf2af4a12023-01-24 16:56:54 +01001716 rollback, engine_session, "vnflcmops", indata, kwargs
1717 )
1718 self._set_location_header(
1719 main_topic, version, "vnf_lcm_op_occs", _id
1720 )
garciadeblasf53612b2024-07-12 14:44:37 +02001721 outdata = {"id": _id, "nsName": nsName}
almagiae47b9132022-05-17 14:12:22 +02001722 cherrypy.response.status = HTTPStatus.ACCEPTED.value
Gabriel Cuba84a60df2023-10-30 14:01:54 -05001723 elif topic == "ns_lcm_op_occs" and item == "cancel":
1724 indata["nsLcmOpOccId"] = _id
1725 self.engine.cancel_item(
1726 rollback, engine_session, "nslcmops", indata, None
1727 )
1728 self._set_location_header(main_topic, version, topic, _id)
1729 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernof27c79b2018-03-12 17:08:42 +01001730 else:
garciadeblas4568a372021-03-24 09:19:48 +01001731 _id, op_id = self.engine.new_item(
1732 rollback,
1733 engine_session,
1734 engine_topic,
1735 indata,
1736 kwargs,
1737 cherrypy.request.headers,
1738 )
tiernob24258a2018-10-04 18:39:49 +02001739 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001740 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00001741 if op_id:
1742 outdata["op_id"] = op_id
1743 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001744 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02001745
tiernoc94c3df2018-02-09 15:38:54 +01001746 elif method == "DELETE":
1747 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001748 outdata = self.engine.del_item_list(
1749 engine_session, engine_topic, kwargs
1750 )
tierno09c073e2018-04-26 13:36:48 +02001751 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01001752 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00001753 # for NS NSI generate an operation
1754 op_id = None
tierno701018c2019-06-25 11:13:14 +00001755 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02001756 nslcmop_desc = {
1757 "lcmOperationType": "terminate",
1758 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001759 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02001760 }
garciadeblasf53612b2024-07-12 14:44:37 +02001761 op_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001762 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
1763 )
tierno22577432020-04-08 15:16:57 +00001764 if op_id:
garciadeblasf53612b2024-07-12 14:44:37 +02001765 outdata = {"_id": op_id, "nsName": nsName}
garciadeblas4568a372021-03-24 09:19:48 +01001766 elif (
1767 topic == "netslice_instances_content"
1768 and not engine_session["force"]
1769 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001770 nsilcmop_desc = {
1771 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02001772 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001773 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02001774 }
garciadeblas4568a372021-03-24 09:19:48 +01001775 op_id, _ = self.engine.new_item(
1776 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
1777 )
tierno22577432020-04-08 15:16:57 +00001778 if op_id:
1779 outdata = {"_id": op_id}
1780 # if there is not any deletion in process, delete
1781 if not op_id:
1782 op_id = self.engine.del_item(engine_session, engine_topic, _id)
1783 if op_id:
1784 outdata = {"op_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001785 cherrypy.response.status = (
1786 HTTPStatus.ACCEPTED.value
1787 if op_id
1788 else HTTPStatus.NO_CONTENT.value
1789 )
tierno09c073e2018-04-26 13:36:48 +02001790
tierno7ae10112018-05-18 14:36:02 +02001791 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00001792 op_id = None
tierno701018c2019-06-25 11:13:14 +00001793 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01001794 raise NbiException(
1795 "Nothing to update. Provide payload and/or query string",
1796 HTTPStatus.BAD_REQUEST,
1797 )
1798 if (
kayal2001f71c2e82024-06-25 15:26:24 +05301799 item
1800 in (
1801 "nsd_content",
1802 "package_content",
1803 "nst_content",
1804 "template_content",
1805 )
garciadeblas4568a372021-03-24 09:19:48 +01001806 and method == "PUT"
1807 ):
1808 completed = self.engine.upload_content(
1809 engine_session,
1810 engine_topic,
1811 _id,
1812 indata,
1813 kwargs,
1814 cherrypy.request.headers,
1815 )
tiernof27c79b2018-03-12 17:08:42 +01001816 if not completed:
1817 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01001818 else:
garciadeblas4568a372021-03-24 09:19:48 +01001819 op_id = self.engine.edit_item(
1820 engine_session, engine_topic, _id, indata, kwargs
1821 )
tiernobdebce92019-07-01 15:36:49 +00001822
1823 if op_id:
1824 cherrypy.response.status = HTTPStatus.ACCEPTED.value
1825 outdata = {"op_id": op_id}
1826 else:
1827 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
1828 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01001829 else:
garciadeblas4568a372021-03-24 09:19:48 +01001830 raise NbiException(
1831 "Method {} not allowed".format(method),
1832 HTTPStatus.METHOD_NOT_ALLOWED,
1833 )
tiernoa6bb45d2019-06-14 09:45:39 +00001834
1835 # if Role information changes, it is needed to reload the information of roles
1836 if topic == "roles" and method != "GET":
1837 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01001838
garciadeblas4568a372021-03-24 09:19:48 +01001839 if (
1840 topic == "projects"
1841 and method == "DELETE"
1842 or topic in ["users", "roles"]
1843 and method in ["PUT", "PATCH", "DELETE"]
1844 ):
delacruzramoad682a52019-12-10 16:26:34 +01001845 self.authenticator.remove_token_from_cache()
1846
garciadeblasf53612b2024-07-12 14:44:37 +02001847 cef_event(
1848 cef_logger,
1849 {
1850 "name": "User Operation",
1851 "sourceUserName": token_info.get("username"),
1852 },
1853 )
1854 if topic == "ns_instances_content" and url_id:
1855 nsName = (
1856 outdata.get("name") if method == "GET" else outdata.get("nsName")
1857 )
elumalai7802ff82023-04-24 20:38:32 +05301858 cef_event(
1859 cef_logger,
1860 {
garciadeblasf53612b2024-07-12 14:44:37 +02001861 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
1862 log_mapping[method],
1863 topic,
1864 nsName,
1865 outdata.get("id"),
1866 token_info.get("project_name"),
1867 ),
1868 },
1869 )
1870 cherrypy.log("{}".format(cef_logger))
1871 elif topic == "ns_instances_content" and method == "POST":
1872 cef_event(
1873 cef_logger,
1874 {
1875 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
1876 log_mapping[method],
1877 topic,
1878 outdata.get("nsName"),
1879 outdata.get("id"),
1880 token_info.get("project_name"),
1881 ),
1882 },
1883 )
1884 cherrypy.log("{}".format(cef_logger))
1885 elif topic in ("ns_instances", "vnf_instances") and item:
1886 cef_event(
1887 cef_logger,
1888 {
1889 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
1890 log_mapping[method],
1891 topic,
1892 outdata.get("nsName"),
1893 url_id,
1894 token_info.get("project_name"),
1895 ),
1896 },
1897 )
1898 cherrypy.log("{}".format(cef_logger))
1899 elif item is not None:
1900 cef_event(
1901 cef_logger,
1902 {
elumalai7802ff82023-04-24 20:38:32 +05301903 "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format(
1904 item,
1905 topic,
1906 url_id,
1907 token_info.get("project_name"),
1908 ),
1909 },
1910 )
1911 cherrypy.log("{}".format(cef_logger))
1912 else:
1913 cef_event(
1914 cef_logger,
1915 {
elumalai7802ff82023-04-24 20:38:32 +05301916 "message": "{} {} {}, Project={} Outcome=Success".format(
1917 log_mapping[method],
1918 topic,
1919 url_id,
1920 token_info.get("project_name"),
1921 ),
1922 },
1923 )
1924 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00001925 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02001926 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01001927 if isinstance(
1928 e,
1929 (
1930 NbiException,
1931 EngineException,
1932 DbException,
1933 FsException,
1934 MsgException,
1935 AuthException,
1936 ValidationError,
1937 AuthconnException,
1938 ),
1939 ):
tiernob24258a2018-10-04 18:39:49 +02001940 http_code_value = cherrypy.response.status = e.http_code.value
1941 http_code_name = e.http_code.name
1942 cherrypy.log("Exception {}".format(e))
1943 else:
garciadeblas4568a372021-03-24 09:19:48 +01001944 http_code_value = (
1945 cherrypy.response.status
1946 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01001947 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02001948 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02001949 if hasattr(outdata, "close"): # is an open file
1950 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02001951 error_text = str(e)
1952 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02001953 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02001954 try:
tiernocc103432018-10-19 14:10:35 +02001955 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01001956 self.engine.db.set_one(
1957 rollback_item["topic"],
1958 {"_id": rollback_item["_id"]},
1959 rollback_item["content"],
1960 fail_on_empty=False,
1961 )
preethika.p329b8182020-04-22 12:25:39 +05301962 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01001963 self.engine.db.del_list(
1964 rollback_item["topic"],
1965 rollback_item["filter"],
garciadeblas4568a372021-03-24 09:19:48 +01001966 )
tiernocc103432018-10-19 14:10:35 +02001967 else:
garciadeblas4568a372021-03-24 09:19:48 +01001968 self.engine.db.del_one(
1969 rollback_item["topic"],
1970 {"_id": rollback_item["_id"]},
1971 fail_on_empty=False,
1972 )
tierno3ace63c2018-05-03 17:51:43 +02001973 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01001974 rollback_error_text = "Rollback Exception {}: {}".format(
1975 rollback_item, e2
1976 )
tiernob24258a2018-10-04 18:39:49 +02001977 cherrypy.log(rollback_error_text)
1978 error_text += ". " + rollback_error_text
1979 # if isinstance(e, MsgException):
1980 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
1981 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01001982 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02001983 "code": http_code_name,
1984 "status": http_code_value,
1985 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01001986 }
elumalai7802ff82023-04-24 20:38:32 +05301987 if item is not None and token_info is not None:
1988 cef_event(
1989 cef_logger,
1990 {
1991 "name": "User Operation",
1992 "sourceUserName": token_info.get("username", None),
1993 "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format(
1994 item,
1995 topic,
1996 url_id,
1997 token_info.get("project_name", None),
1998 ),
1999 "severity": "2",
2000 },
2001 )
2002 cherrypy.log("{}".format(cef_logger))
2003 elif token_info is not None:
2004 cef_event(
2005 cef_logger,
2006 {
2007 "name": "User Operation",
2008 "sourceUserName": token_info.get("username", None),
2009 "message": "{} {} {}, Project={} Outcome=Failure".format(
2010 item,
2011 topic,
2012 url_id,
2013 token_info.get("project_name", None),
2014 ),
2015 "severity": "2",
2016 },
2017 )
2018 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002019 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01002020 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00002021 finally:
2022 if token_info:
2023 self._format_login(token_info)
2024 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
2025 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
2026 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01002027 cherrypy.request.login += ";{}={}".format(
2028 logging_id, outdata[logging_id][:36]
2029 )
tiernoc94c3df2018-02-09 15:38:54 +01002030
2031
tiernoc94c3df2018-02-09 15:38:54 +01002032def _start_service():
2033 """
2034 Callback function called when cherrypy.engine starts
2035 Override configuration with env variables
2036 Set database, storage, message configuration
2037 Init database with admin/admin user password
2038 """
tierno932499c2019-01-28 17:28:10 +00002039 global nbi_server
2040 global subscription_thread
elumalai7802ff82023-04-24 20:38:32 +05302041 global cef_logger
Rahulc72bc8e2023-12-05 11:54:38 +00002042 global current_backend
tiernoc94c3df2018-02-09 15:38:54 +01002043 cherrypy.log.error("Starting osm_nbi")
2044 # update general cherrypy configuration
2045 update_dict = {}
2046
garciadeblas4568a372021-03-24 09:19:48 +01002047 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01002048 for k, v in environ.items():
garciadeblas6d83f8f2023-06-19 22:34:49 +02002049 if k == "OSMNBI_USER_MANAGEMENT":
2050 feature_state = eval(v.title())
2051 engine_config["authentication"]["user_management"] = feature_state
Adurtid68526d2023-11-14 11:14:53 +00002052 elif k == "OSMNBI_PWD_EXPIRE_DAYS":
2053 pwd_expire_days = int(v)
2054 engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days
2055 elif k == "OSMNBI_MAX_PWD_ATTEMPT":
2056 max_pwd_attempt = int(v)
2057 engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt
2058 elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS":
2059 account_expire_days = int(v)
2060 engine_config["authentication"]["account_expire_days"] = account_expire_days
tiernoc94c3df2018-02-09 15:38:54 +01002061 if not k.startswith("OSMNBI_"):
2062 continue
tiernoe1281182018-05-22 12:24:36 +02002063 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01002064 if not k2:
2065 continue
2066 try:
2067 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01002068 if k == "OSMNBI_STATIC_DIR":
2069 engine_config["/static"]["tools.staticdir.dir"] = v
2070 engine_config["/static"]["tools.staticdir.on"] = True
2071 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
2072 update_dict["server.socket_port"] = int(v)
2073 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
2074 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02002075 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01002076 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002077 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02002078 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002079 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01002080 engine_config[k1][k2] = int(v)
2081 else:
2082 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002083
tiernoc94c3df2018-02-09 15:38:54 +01002084 except ValueError as e:
2085 cherrypy.log.error("Ignoring environ '{}': " + str(e))
2086 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01002087 cherrypy.log(
2088 "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
2089 )
tiernoc94c3df2018-02-09 15:38:54 +01002090
2091 if update_dict:
2092 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02002093 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01002094
2095 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01002096 log_format_simple = (
2097 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
2098 )
2099 log_formatter_simple = logging.Formatter(
2100 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
2101 )
tiernoc94c3df2018-02-09 15:38:54 +01002102 logger_server = logging.getLogger("cherrypy.error")
2103 logger_access = logging.getLogger("cherrypy.access")
2104 logger_cherry = logging.getLogger("cherrypy")
2105 logger_nbi = logging.getLogger("nbi")
2106
tiernof5298be2018-05-16 14:43:57 +02002107 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01002108 file_handler = logging.handlers.RotatingFileHandler(
2109 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
2110 )
tiernoc94c3df2018-02-09 15:38:54 +01002111 file_handler.setFormatter(log_formatter_simple)
2112 logger_cherry.addHandler(file_handler)
2113 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02002114 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01002115 for format_, logger in {
2116 "nbi.server %(filename)s:%(lineno)s": logger_server,
2117 "nbi.access %(filename)s:%(lineno)s": logger_access,
2118 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
2119 }.items():
tiernob24258a2018-10-04 18:39:49 +02002120 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01002121 log_formatter_cherry = logging.Formatter(
2122 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
2123 )
tiernob24258a2018-10-04 18:39:49 +02002124 str_handler = logging.StreamHandler()
2125 str_handler.setFormatter(log_formatter_cherry)
2126 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01002127
tiernof5298be2018-05-16 14:43:57 +02002128 if engine_config["global"].get("log.level"):
2129 logger_cherry.setLevel(engine_config["global"]["log.level"])
2130 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01002131
2132 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01002133 for k1, logname in {
2134 "message": "nbi.msg",
2135 "database": "nbi.db",
2136 "storage": "nbi.fs",
2137 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01002138 engine_config[k1]["logger_name"] = logname
2139 logger_module = logging.getLogger(logname)
2140 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01002141 file_handler = logging.handlers.RotatingFileHandler(
2142 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
2143 )
tiernoc94c3df2018-02-09 15:38:54 +01002144 file_handler.setFormatter(log_formatter_simple)
2145 logger_module.addHandler(file_handler)
2146 if "loglevel" in engine_config[k1]:
2147 logger_module.setLevel(engine_config[k1]["loglevel"])
2148 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01002149 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
2150 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
2151 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
2152 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
2153 target_version=auth_database_version
2154 )
tiernobee508e2019-01-21 11:21:49 +00002155
elumalai7802ff82023-04-24 20:38:32 +05302156 cef_logger = cef_event_builder(engine_config["authentication"])
2157
tierno932499c2019-01-28 17:28:10 +00002158 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01002159 subscription_thread = SubscriptionThread(
2160 config=engine_config, engine=nbi_server.engine
2161 )
tierno932499c2019-01-28 17:28:10 +00002162 subscription_thread.start()
2163 # Do not capture except SubscriptionException
2164
tiernob2e48bd2020-02-04 15:47:18 +00002165 backend = engine_config["authentication"]["backend"]
Rahulc72bc8e2023-12-05 11:54:38 +00002166 current_backend = backend
garciadeblas4568a372021-03-24 09:19:48 +01002167 cherrypy.log.error(
2168 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
2169 nbi_version, nbi_version_date, backend
2170 )
2171 )
tiernoc94c3df2018-02-09 15:38:54 +01002172
2173
2174def _stop_service():
2175 """
2176 Callback function called when cherrypy.engine stops
2177 TODO: Ending database connections.
2178 """
tierno932499c2019-01-28 17:28:10 +00002179 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01002180 if subscription_thread:
2181 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00002182 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01002183 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01002184 cherrypy.log.error("Stopping osm_nbi")
2185
tierno2236d202018-05-16 19:05:16 +02002186
tiernof5298be2018-05-16 14:43:57 +02002187def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00002188 global nbi_server
tiernoc94c3df2018-02-09 15:38:54 +01002189 # conf = {
2190 # '/': {
2191 # #'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
2192 # 'tools.sessions.on': True,
2193 # 'tools.response_headers.on': True,
2194 # # 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
2195 # }
2196 # }
2197 # cherrypy.Server.ssl_module = 'builtin'
2198 # cherrypy.Server.ssl_certificate = "http/cert.pem"
2199 # cherrypy.Server.ssl_private_key = "http/privkey.pem"
2200 # cherrypy.Server.thread_pool = 10
2201 # cherrypy.config.update({'Server.socket_port': config["port"], 'Server.socket_host': config["host"]})
2202
2203 # cherrypy.config.update({'tools.auth_basic.on': True,
2204 # 'tools.auth_basic.realm': 'localhost',
2205 # 'tools.auth_basic.checkpassword': validate_password})
tierno932499c2019-01-28 17:28:10 +00002206 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01002207 cherrypy.engine.subscribe("start", _start_service)
2208 cherrypy.engine.subscribe("stop", _stop_service)
2209 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02002210
2211
2212def usage():
garciadeblas4568a372021-03-24 09:19:48 +01002213 print(
2214 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02002215 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
2216 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01002217 """.format(
2218 sys.argv[0]
2219 )
2220 )
tierno2236d202018-05-16 19:05:16 +02002221 # --log-socket-host HOST: send logs to this host")
2222 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002223
2224
garciadeblas4568a372021-03-24 09:19:48 +01002225if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002226 try:
2227 # load parameters and configuration
2228 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2229 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2230 config_file = None
2231 for o, a in opts:
2232 if o in ("-h", "--help"):
2233 usage()
2234 sys.exit()
2235 elif o in ("-c", "--config"):
2236 config_file = a
2237 # elif o == "--log-socket-port":
2238 # log_socket_port = a
2239 # elif o == "--log-socket-host":
2240 # log_socket_host = a
2241 # elif o == "--log-file":
2242 # log_file = a
2243 else:
2244 assert False, "Unhandled option"
2245 if config_file:
2246 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002247 print(
2248 "configuration file '{}' that not exist".format(config_file),
2249 file=sys.stderr,
2250 )
tiernof5298be2018-05-16 14:43:57 +02002251 exit(1)
2252 else:
garciadeblas4568a372021-03-24 09:19:48 +01002253 for config_file in (
2254 __file__[: __file__.rfind(".")] + ".cfg",
2255 "./nbi.cfg",
2256 "/etc/osm/nbi.cfg",
2257 ):
tiernof5298be2018-05-16 14:43:57 +02002258 if path.isfile(config_file):
2259 break
2260 else:
garciadeblas4568a372021-03-24 09:19:48 +01002261 print(
2262 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2263 file=sys.stderr,
2264 )
tiernof5298be2018-05-16 14:43:57 +02002265 exit(1)
2266 nbi(config_file)
2267 except getopt.GetoptError as e:
2268 print(str(e), file=sys.stderr)
2269 # usage()
2270 exit(1)