blob: 09ae05d08f7ffc0dd433c9e3e6bc24d68e263e52 [file] [log] [blame]
tiernoc94c3df2018-02-09 15:38:54 +01001#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3
tiernod125caf2018-11-22 16:05:54 +00004# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
tiernoc94c3df2018-02-09 15:38:54 +010017import cherrypy
18import time
19import json
20import yaml
tierno9c630112019-08-29 14:21:41 +000021import osm_nbi.html_out as html
tiernoc94c3df2018-02-09 15:38:54 +010022import logging
tiernof5298be2018-05-16 14:43:57 +020023import logging.handlers
24import getopt
25import sys
Eduardo Sousa2f988212018-07-26 01:04:11 +010026
tierno9c630112019-08-29 14:21:41 +000027from osm_nbi.authconn import AuthException, AuthconnException
28from osm_nbi.auth import Authenticator
29from osm_nbi.engine import Engine, EngineException
30from osm_nbi.subscriptions import SubscriptionThread
elumalai7802ff82023-04-24 20:38:32 +053031from osm_nbi.utils import cef_event, cef_event_builder
tierno9c630112019-08-29 14:21:41 +000032from osm_nbi.validation import ValidationError
tiernoa8d63632018-05-10 13:12:32 +020033from osm_common.dbbase import DbException
34from osm_common.fsbase import FsException
35from osm_common.msgbase import MsgException
tiernoc94c3df2018-02-09 15:38:54 +010036from http import HTTPStatus
tiernoc94c3df2018-02-09 15:38:54 +010037from codecs import getreader
tiernof5298be2018-05-16 14:43:57 +020038from os import environ, path
tiernob2e48bd2020-02-04 15:47:18 +000039from osm_nbi import version as nbi_version, version_date as nbi_version_date
tiernoc94c3df2018-02-09 15:38:54 +010040
41__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
tiernodfe09572018-04-24 10:41:10 +020042
garciadeblas4568a372021-03-24 09:19:48 +010043__version__ = "0.1.3" # file version, not NBI version
tierno9c630112019-08-29 14:21:41 +000044version_date = "Aug 2019"
45
garciadeblas4568a372021-03-24 09:19:48 +010046database_version = "1.2"
47auth_database_version = "1.0"
48nbi_server = None # instance of Server class
tierno932499c2019-01-28 17:28:10 +000049subscription_thread = None # instance of SubscriptionThread class
elumalai7802ff82023-04-24 20:38:32 +053050cef_logger = None
yshah53cc9eb2024-07-05 13:06:31 +000051logger = logging.getLogger("nbi.nbi")
tiernoc94c3df2018-02-09 15:38:54 +010052
53"""
tiernof27c79b2018-03-12 17:08:42 +010054North Bound Interface (O: OSM specific; 5,X: SOL005 not implemented yet; O5: SOL005 implemented)
tiernoc94c3df2018-02-09 15:38:54 +010055URL: /osm GET POST PUT DELETE PATCH
garciadeblas9750c5a2018-10-15 16:20:35 +020056 /nsd/v1
tierno2236d202018-05-16 19:05:16 +020057 /ns_descriptors_content O O
58 /<nsdInfoId> O O O O
tiernoc94c3df2018-02-09 15:38:54 +010059 /ns_descriptors O5 O5
60 /<nsdInfoId> O5 O5 5
61 /nsd_content O5 O5
tiernof27c79b2018-03-12 17:08:42 +010062 /nsd O
63 /artifacts[/<artifactPath>] O
kayal2001f71c2e82024-06-25 15:26:24 +053064 /ns_config_template O O
65 /<nsConfigTemplateId> O O
66 /template_content O O
tiernoc94c3df2018-02-09 15:38:54 +010067 /pnf_descriptors 5 5
68 /<pnfdInfoId> 5 5 5
69 /pnfd_content 5 5
tiernof27c79b2018-03-12 17:08:42 +010070 /subscriptions 5 5
71 /<subscriptionId> 5 X
tiernoc94c3df2018-02-09 15:38:54 +010072
73 /vnfpkgm/v1
tierno55945e72018-04-06 16:40:27 +020074 /vnf_packages_content O O
tierno2236d202018-05-16 19:05:16 +020075 /<vnfPkgId> O O
tiernoc94c3df2018-02-09 15:38:54 +010076 /vnf_packages O5 O5
77 /<vnfPkgId> O5 O5 5
tiernoc94c3df2018-02-09 15:38:54 +010078 /package_content O5 O5
79 /upload_from_uri X
tiernof27c79b2018-03-12 17:08:42 +010080 /vnfd O5
81 /artifacts[/<artifactPath>] O5
82 /subscriptions X X
83 /<subscriptionId> X X
tiernoc94c3df2018-02-09 15:38:54 +010084
85 /nslcm/v1
tiernof27c79b2018-03-12 17:08:42 +010086 /ns_instances_content O O
tierno2236d202018-05-16 19:05:16 +020087 /<nsInstanceId> O O
tiernof27c79b2018-03-12 17:08:42 +010088 /ns_instances 5 5
tierno95692442018-05-24 18:05:28 +020089 /<nsInstanceId> O5 O5
tierno65acb4d2018-04-06 16:42:40 +020090 instantiate O5
91 terminate O5
92 action O
93 scale O5
elumalai8e3806c2022-04-28 17:26:24 +053094 migrate O
aticig544a2ae2022-04-05 09:00:17 +030095 update 05
garciadeblas0964edf2022-02-11 00:43:44 +010096 heal O5
tiernoc94c3df2018-02-09 15:38:54 +010097 /ns_lcm_op_occs 5 5
98 /<nsLcmOpOccId> 5 5 5
Gabriel Cuba84a60df2023-10-30 14:01:54 -050099 cancel 05
tiernof759d822018-06-11 18:54:54 +0200100 /vnf_instances (also vnfrs for compatibility) O
101 /<vnfInstanceId> O
tiernof27c79b2018-03-12 17:08:42 +0100102 /subscriptions 5 5
103 /<subscriptionId> 5 X
garciadeblas9750c5a2018-10-15 16:20:35 +0200104
tiernocb83c942018-09-24 17:28:13 +0200105 /pdu/v1
tierno032916c2019-03-22 13:27:12 +0000106 /pdu_descriptors O O
tiernocb83c942018-09-24 17:28:13 +0200107 /<id> O O O O
garciadeblas9750c5a2018-10-15 16:20:35 +0200108
tiernof27c79b2018-03-12 17:08:42 +0100109 /admin/v1
110 /tokens O O
tierno2236d202018-05-16 19:05:16 +0200111 /<id> O O
tiernof27c79b2018-03-12 17:08:42 +0100112 /users O O
tiernocd54a4a2018-09-12 16:40:35 +0200113 /<id> O O O O
tiernof27c79b2018-03-12 17:08:42 +0100114 /projects O O
tierno2236d202018-05-16 19:05:16 +0200115 /<id> O O
tierno55ba2e62018-12-11 17:22:22 +0000116 /vim_accounts (also vims for compatibility) O O
117 /<id> O O O
118 /wim_accounts O O
tierno2236d202018-05-16 19:05:16 +0200119 /<id> O O O
tierno0f98af52018-03-19 10:28:22 +0100120 /sdns O O
tierno2236d202018-05-16 19:05:16 +0200121 /<id> O O O
delacruzramofe598fe2019-10-23 18:25:11 +0200122 /k8sclusters O O
123 /<id> O O O
124 /k8srepos O O
125 /<id> O O
Felipe Vicensb66b0412020-05-06 10:11:00 +0200126 /osmrepos O O
127 /<id> O O
tiernoc94c3df2018-02-09 15:38:54 +0100128
garciadeblas9750c5a2018-10-15 16:20:35 +0200129 /nst/v1 O O
130 /netslice_templates_content O O
131 /<nstInfoId> O O O O
132 /netslice_templates O O
133 /<nstInfoId> O O O
134 /nst_content O O
135 /nst O
136 /artifacts[/<artifactPath>] O
137 /subscriptions X X
138 /<subscriptionId> X X
139
140 /nsilcm/v1
141 /netslice_instances_content O O
142 /<SliceInstanceId> O O
143 /netslice_instances O O
144 /<SliceInstanceId> O O
145 instantiate O
146 terminate O
147 action O
148 /nsi_lcm_op_occs O O
149 /<nsiLcmOpOccId> O O O
150 /subscriptions X X
151 /<subscriptionId> X X
152
rshri2d386cb2024-07-05 14:35:51 +0000153 /k8scluster/v1
154 /clusters O O
155 /<clustersId> O O
156 app_profiles O O
157 infra_controller_profiles O O
158 infra_config_profiles O O
159 resource_profiles O O
160 deregister O
161 /register O
162 /app_profiles O O
163 /<app_profilesId> O O O
164 /infra_controller_profiles O O
165 /<infra_controller_profilesId> O O O
166 /infra_config_profiles O O
167 /<infra_config_profilesId> O O O
168 /resource_profiles O O
169 /<resource_profilesID> O O O
170
tierno2236d202018-05-16 19:05:16 +0200171query string:
172 Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force.
tierno36ec8602018-11-02 17:27:11 +0100173 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
174 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
175 op := "eq" | "neq" (or "ne") | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
176 attrName := string
tierno2236d202018-05-16 19:05:16 +0200177 For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any
178 item of the array, that is, pass if any item of the array pass the filter.
179 It allows both ne and neq for not equal
180 TODO: 4.3.3 Attribute selectors
181 all_fields, fields=x,y,.., exclude_default, exclude_fields=x,y,...
tiernoc94c3df2018-02-09 15:38:54 +0100182 (none) … same as “exclude_default”
183 all_fields … all attributes.
tierno2236d202018-05-16 19:05:16 +0200184 fields=<list> … all attributes except all complex attributes with minimum cardinality of zero that are not
185 conditionally mandatory, and that are not provided in <list>.
186 exclude_fields=<list> … all attributes except those complex attributes with a minimum cardinality of zero that
187 are not conditionally mandatory, and that are provided in <list>.
188 exclude_default … all attributes except those complex attributes with a minimum cardinality of zero that are not
189 conditionally mandatory, and that are part of the "default exclude set" defined in the present specification for
190 the particular resource
191 exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality
192 of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the
193 present specification for the particular resource, but that are not part of <list>
tierno65ca36d2019-02-12 19:27:52 +0100194 Additionally it admits some administrator values:
195 FORCE: To force operations skipping dependency checkings
196 ADMIN: To act as an administrator or a different project
197 PUBLIC: To get public descriptors or set a descriptor as public
198 SET_PROJECT: To make a descriptor available for other project
beierlmbc5a5242022-05-17 21:25:29 -0400199
tiernoc94c3df2018-02-09 15:38:54 +0100200Header field name Reference Example Descriptions
201 Accept IETF RFC 7231 [19] application/json Content-Types that are acceptable for the response.
202 This header field shall be present if the response is expected to have a non-empty message body.
203 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the request.
204 This header field shall be present if the request has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200205 Authorization IETF RFC 7235 [22] Bearer mF_9.B5f-4.1JqM The authorization token for the request.
206 Details are specified in clause 4.5.3.
tiernoc94c3df2018-02-09 15:38:54 +0100207 Range IETF RFC 7233 [21] 1000-2000 Requested range of bytes from a file
208Header field name Reference Example Descriptions
209 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the response.
210 This header field shall be present if the response has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200211 Location IETF RFC 7231 [19] http://www.example.com/vnflcm/v1/vnf_instances/123 Used in redirection, or when a
212 new resource has been created.
tiernoc94c3df2018-02-09 15:38:54 +0100213 This header field shall be present if the response status code is 201 or 3xx.
tierno2236d202018-05-16 19:05:16 +0200214 In the present document this header field is also used if the response status code is 202 and a new resource was
215 created.
216 WWW-Authenticate IETF RFC 7235 [22] Bearer realm="example" Challenge if the corresponding HTTP request has not
217 provided authorization, or error details if the corresponding HTTP request has provided an invalid authorization
218 token.
219 Accept-Ranges IETF RFC 7233 [21] bytes Used by the Server to signal whether or not it supports ranges for
220 certain resources.
221 Content-Range IETF RFC 7233 [21] bytes 21010-47021/ 47022 Signals the byte range that is contained in the
222 response, and the total length of the file.
tiernoc94c3df2018-02-09 15:38:54 +0100223 Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT
tiernoc94c3df2018-02-09 15:38:54 +0100224"""
225
tierno701018c2019-06-25 11:13:14 +0000226valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC")
227# ^ Contains possible administrative query string words:
228# ADMIN=True(by default)|Project|Project-list: See all elements, or elements of a project
229# (not owned by my session project).
230# PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public
231# FORCE=True(by default)|False: Force edition/deletion operations
232# SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio
233
234valid_url_methods = {
235 # contains allowed URL and methods, and the role_permission name
236 "admin": {
237 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100238 "tokens": {
239 "METHODS": ("GET", "POST", "DELETE"),
240 "ROLE_PERMISSION": "tokens:",
241 "<ID>": {"METHODS": ("GET", "DELETE"), "ROLE_PERMISSION": "tokens:id:"},
242 },
243 "users": {
244 "METHODS": ("GET", "POST"),
245 "ROLE_PERMISSION": "users:",
246 "<ID>": {
247 "METHODS": ("GET", "DELETE", "PATCH"),
248 "ROLE_PERMISSION": "users:id:",
249 },
250 },
251 "projects": {
252 "METHODS": ("GET", "POST"),
253 "ROLE_PERMISSION": "projects:",
254 "<ID>": {
255 "METHODS": ("GET", "DELETE", "PATCH"),
256 "ROLE_PERMISSION": "projects:id:",
257 },
258 },
259 "roles": {
260 "METHODS": ("GET", "POST"),
261 "ROLE_PERMISSION": "roles:",
262 "<ID>": {
263 "METHODS": ("GET", "DELETE", "PATCH"),
264 "ROLE_PERMISSION": "roles:id:",
265 },
266 },
267 "vims": {
268 "METHODS": ("GET", "POST"),
269 "ROLE_PERMISSION": "vims:",
270 "<ID>": {
271 "METHODS": ("GET", "DELETE", "PATCH"),
272 "ROLE_PERMISSION": "vims:id:",
273 },
274 },
275 "vim_accounts": {
276 "METHODS": ("GET", "POST"),
277 "ROLE_PERMISSION": "vim_accounts:",
278 "<ID>": {
279 "METHODS": ("GET", "DELETE", "PATCH"),
280 "ROLE_PERMISSION": "vim_accounts:id:",
281 },
282 },
283 "wim_accounts": {
284 "METHODS": ("GET", "POST"),
285 "ROLE_PERMISSION": "wim_accounts:",
286 "<ID>": {
287 "METHODS": ("GET", "DELETE", "PATCH"),
288 "ROLE_PERMISSION": "wim_accounts:id:",
289 },
290 },
291 "sdns": {
292 "METHODS": ("GET", "POST"),
293 "ROLE_PERMISSION": "sdn_controllers:",
294 "<ID>": {
295 "METHODS": ("GET", "DELETE", "PATCH"),
296 "ROLE_PERMISSION": "sdn_controllers:id:",
297 },
298 },
299 "k8sclusters": {
300 "METHODS": ("GET", "POST"),
301 "ROLE_PERMISSION": "k8sclusters:",
302 "<ID>": {
303 "METHODS": ("GET", "DELETE", "PATCH"),
304 "ROLE_PERMISSION": "k8sclusters:id:",
305 },
306 },
307 "vca": {
308 "METHODS": ("GET", "POST"),
309 "ROLE_PERMISSION": "vca:",
310 "<ID>": {
311 "METHODS": ("GET", "DELETE", "PATCH"),
312 "ROLE_PERMISSION": "vca:id:",
313 },
314 },
315 "k8srepos": {
316 "METHODS": ("GET", "POST"),
317 "ROLE_PERMISSION": "k8srepos:",
318 "<ID>": {
319 "METHODS": ("GET", "DELETE"),
320 "ROLE_PERMISSION": "k8srepos:id:",
321 },
322 },
323 "osmrepos": {
324 "METHODS": ("GET", "POST"),
325 "ROLE_PERMISSION": "osmrepos:",
326 "<ID>": {
327 "METHODS": ("GET", "DELETE", "PATCH"),
328 "ROLE_PERMISSION": "osmrepos:id:",
329 },
330 },
331 "domains": {
332 "METHODS": ("GET",),
333 "ROLE_PERMISSION": "domains:",
334 },
tierno701018c2019-06-25 11:13:14 +0000335 }
336 },
337 "pdu": {
338 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100339 "pdu_descriptors": {
340 "METHODS": ("GET", "POST"),
341 "ROLE_PERMISSION": "pduds:",
342 "<ID>": {
343 "METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"),
344 "ROLE_PERMISSION": "pduds:id:",
345 },
346 },
tierno701018c2019-06-25 11:13:14 +0000347 }
348 },
349 "nsd": {
350 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100351 "ns_descriptors_content": {
352 "METHODS": ("GET", "POST"),
Adurtidb4540c2024-03-21 08:42:52 +0000353 "ROLE_PERMISSION": "nsds:content:",
garciadeblas4568a372021-03-24 09:19:48 +0100354 "<ID>": {
355 "METHODS": ("GET", "PUT", "DELETE"),
356 "ROLE_PERMISSION": "nsds:id:",
357 },
358 },
359 "ns_descriptors": {
360 "METHODS": ("GET", "POST"),
361 "ROLE_PERMISSION": "nsds:",
362 "<ID>": {
363 "METHODS": ("GET", "DELETE", "PATCH"),
364 "ROLE_PERMISSION": "nsds:id:",
365 "nsd_content": {
366 "METHODS": ("GET", "PUT"),
367 "ROLE_PERMISSION": "nsds:id:content:",
368 },
369 "nsd": {
370 "METHODS": ("GET",), # descriptor inside package
Adurtidb4540c2024-03-21 08:42:52 +0000371 "ROLE_PERMISSION": "nsds:id:nsd:",
garciadeblas4568a372021-03-24 09:19:48 +0100372 },
373 "artifacts": {
374 "METHODS": ("GET",),
375 "ROLE_PERMISSION": "nsds:id:nsd_artifact:",
376 "*": None,
377 },
378 },
379 },
kayal2001f71c2e82024-06-25 15:26:24 +0530380 "ns_config_template": {
381 "METHODS": ("GET", "POST"),
382 "ROLE_PERMISSION": "ns_config_template:content:",
383 "<ID>": {
384 "METHODS": ("GET", "DELETE"),
385 "ROLE_PERMISSION": "ns_config_template:id:",
386 "template_content": {
387 "METHODS": ("GET", "PUT"),
388 "ROLE_PERMISSION": "ns_config_template:id:content:",
389 },
390 },
391 },
garciadeblas4568a372021-03-24 09:19:48 +0100392 "pnf_descriptors": {
393 "TODO": ("GET", "POST"),
394 "<ID>": {
395 "TODO": ("GET", "DELETE", "PATCH"),
396 "pnfd_content": {"TODO": ("GET", "PUT")},
397 },
398 },
399 "subscriptions": {
400 "TODO": ("GET", "POST"),
401 "<ID>": {"TODO": ("GET", "DELETE")},
402 },
tierno701018c2019-06-25 11:13:14 +0000403 }
404 },
405 "vnfpkgm": {
406 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100407 "vnf_packages_content": {
408 "METHODS": ("GET", "POST"),
Adurtidb4540c2024-03-21 08:42:52 +0000409 "ROLE_PERMISSION": "vnfds:content:",
garciadeblas4568a372021-03-24 09:19:48 +0100410 "<ID>": {
411 "METHODS": ("GET", "PUT", "DELETE"),
412 "ROLE_PERMISSION": "vnfds:id:",
413 },
414 },
415 "vnf_packages": {
416 "METHODS": ("GET", "POST"),
417 "ROLE_PERMISSION": "vnfds:",
418 "<ID>": {
419 "METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo
420 "ROLE_PERMISSION": "vnfds:id:",
421 "package_content": {
422 "METHODS": ("GET", "PUT"), # package
Adurtidb4540c2024-03-21 08:42:52 +0000423 "ROLE_PERMISSION": "vnfds:id:content:",
garciadeblas4568a372021-03-24 09:19:48 +0100424 "upload_from_uri": {
425 "METHODS": (),
426 "TODO": ("POST",),
427 "ROLE_PERMISSION": "vnfds:id:upload:",
428 },
429 },
430 "vnfd": {
431 "METHODS": ("GET",), # descriptor inside package
Adurtidb4540c2024-03-21 08:42:52 +0000432 "ROLE_PERMISSION": "vnfds:id:vnfd:",
garciadeblas4568a372021-03-24 09:19:48 +0100433 },
434 "artifacts": {
435 "METHODS": ("GET",),
436 "ROLE_PERMISSION": "vnfds:id:vnfd_artifact:",
437 "*": None,
438 },
439 "action": {
440 "METHODS": ("POST",),
441 "ROLE_PERMISSION": "vnfds:id:action:",
442 },
443 },
444 },
445 "subscriptions": {
446 "TODO": ("GET", "POST"),
447 "<ID>": {"TODO": ("GET", "DELETE")},
448 },
449 "vnfpkg_op_occs": {
450 "METHODS": ("GET",),
451 "ROLE_PERMISSION": "vnfds:vnfpkgops:",
452 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnfds:vnfpkgops:id:"},
453 },
tierno701018c2019-06-25 11:13:14 +0000454 }
455 },
456 "nslcm": {
457 "v1": {
adurti3af50952024-05-31 11:36:57 +0530458 "ns_instances_terminate": {
459 "METHODS": ("POST"),
460 "ROLE_PERMISSION": "ns_instances:",
461 },
garciadeblas4568a372021-03-24 09:19:48 +0100462 "ns_instances_content": {
463 "METHODS": ("GET", "POST"),
Adurtidb4540c2024-03-21 08:42:52 +0000464 "ROLE_PERMISSION": "ns_instances:content:",
garciadeblas4568a372021-03-24 09:19:48 +0100465 "<ID>": {
466 "METHODS": ("GET", "DELETE"),
467 "ROLE_PERMISSION": "ns_instances:id:",
468 },
469 },
470 "ns_instances": {
471 "METHODS": ("GET", "POST"),
472 "ROLE_PERMISSION": "ns_instances:",
473 "<ID>": {
474 "METHODS": ("GET", "DELETE"),
475 "ROLE_PERMISSION": "ns_instances:id:",
garciadeblas0964edf2022-02-11 00:43:44 +0100476 "heal": {
477 "METHODS": ("POST",),
478 "ROLE_PERMISSION": "ns_instances:id:heal:",
479 },
garciadeblas4568a372021-03-24 09:19:48 +0100480 "scale": {
481 "METHODS": ("POST",),
482 "ROLE_PERMISSION": "ns_instances:id:scale:",
483 },
484 "terminate": {
485 "METHODS": ("POST",),
486 "ROLE_PERMISSION": "ns_instances:id:terminate:",
487 },
488 "instantiate": {
489 "METHODS": ("POST",),
490 "ROLE_PERMISSION": "ns_instances:id:instantiate:",
491 },
elumalai8e3806c2022-04-28 17:26:24 +0530492 "migrate": {
493 "METHODS": ("POST",),
494 "ROLE_PERMISSION": "ns_instances:id:migrate:",
495 },
garciadeblas4568a372021-03-24 09:19:48 +0100496 "action": {
497 "METHODS": ("POST",),
498 "ROLE_PERMISSION": "ns_instances:id:action:",
499 },
aticig544a2ae2022-04-05 09:00:17 +0300500 "update": {
501 "METHODS": ("POST",),
502 "ROLE_PERMISSION": "ns_instances:id:update:",
503 },
garciadeblas4568a372021-03-24 09:19:48 +0100504 },
505 },
506 "ns_lcm_op_occs": {
507 "METHODS": ("GET",),
508 "ROLE_PERMISSION": "ns_instances:opps:",
509 "<ID>": {
510 "METHODS": ("GET",),
511 "ROLE_PERMISSION": "ns_instances:opps:id:",
Gabriel Cuba84a60df2023-10-30 14:01:54 -0500512 "cancel": {
513 "METHODS": ("POST",),
514 "ROLE_PERMISSION": "ns_instances:opps:cancel:",
515 },
garciadeblas4568a372021-03-24 09:19:48 +0100516 },
517 },
518 "vnfrs": {
519 "METHODS": ("GET",),
520 "ROLE_PERMISSION": "vnf_instances:",
521 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
522 },
523 "vnf_instances": {
524 "METHODS": ("GET",),
525 "ROLE_PERMISSION": "vnf_instances:",
526 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
527 },
528 "subscriptions": {
529 "METHODS": ("GET", "POST"),
530 "ROLE_PERMISSION": "ns_subscriptions:",
531 "<ID>": {
532 "METHODS": ("GET", "DELETE"),
533 "ROLE_PERMISSION": "ns_subscriptions:id:",
534 },
535 },
tierno701018c2019-06-25 11:13:14 +0000536 }
537 },
almagiae47b9132022-05-17 14:12:22 +0200538 "vnflcm": {
539 "v1": {
garciadeblasf2af4a12023-01-24 16:56:54 +0100540 "vnf_instances": {
541 "METHODS": ("GET", "POST"),
542 "ROLE_PERMISSION": "vnflcm_instances:",
543 "<ID>": {
544 "METHODS": ("GET", "DELETE"),
545 "ROLE_PERMISSION": "vnflcm_instances:id:",
546 "scale": {
547 "METHODS": ("POST",),
548 "ROLE_PERMISSION": "vnflcm_instances:id:scale:",
549 },
550 "terminate": {
551 "METHODS": ("POST",),
552 "ROLE_PERMISSION": "vnflcm_instances:id:terminate:",
553 },
554 "instantiate": {
555 "METHODS": ("POST",),
556 "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:",
557 },
558 },
559 },
560 "vnf_lcm_op_occs": {
561 "METHODS": ("GET",),
562 "ROLE_PERMISSION": "vnf_instances:opps:",
563 "<ID>": {
564 "METHODS": ("GET",),
565 "ROLE_PERMISSION": "vnf_instances:opps:id:",
566 },
567 },
568 "subscriptions": {
569 "METHODS": ("GET", "POST"),
570 "ROLE_PERMISSION": "vnflcm_subscriptions:",
571 "<ID>": {
572 "METHODS": ("GET", "DELETE"),
573 "ROLE_PERMISSION": "vnflcm_subscriptions:id:",
574 },
575 },
almagiae47b9132022-05-17 14:12:22 +0200576 }
577 },
tierno701018c2019-06-25 11:13:14 +0000578 "nst": {
579 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100580 "netslice_templates_content": {
581 "METHODS": ("GET", "POST"),
582 "ROLE_PERMISSION": "slice_templates:",
583 "<ID>": {
584 "METHODS": ("GET", "PUT", "DELETE"),
585 "ROLE_PERMISSION": "slice_templates:id:",
586 },
587 },
588 "netslice_templates": {
589 "METHODS": ("GET", "POST"),
590 "ROLE_PERMISSION": "slice_templates:",
591 "<ID>": {
592 "METHODS": ("GET", "DELETE"),
593 "TODO": ("PATCH",),
594 "ROLE_PERMISSION": "slice_templates:id:",
595 "nst_content": {
596 "METHODS": ("GET", "PUT"),
597 "ROLE_PERMISSION": "slice_templates:id:content:",
598 },
599 "nst": {
600 "METHODS": ("GET",), # descriptor inside package
601 "ROLE_PERMISSION": "slice_templates:id:content:",
602 },
603 "artifacts": {
604 "METHODS": ("GET",),
605 "ROLE_PERMISSION": "slice_templates:id:content:",
606 "*": None,
607 },
608 },
609 },
610 "subscriptions": {
611 "TODO": ("GET", "POST"),
612 "<ID>": {"TODO": ("GET", "DELETE")},
613 },
tierno701018c2019-06-25 11:13:14 +0000614 }
615 },
616 "nsilcm": {
617 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100618 "netslice_instances_content": {
619 "METHODS": ("GET", "POST"),
620 "ROLE_PERMISSION": "slice_instances:",
621 "<ID>": {
622 "METHODS": ("GET", "DELETE"),
623 "ROLE_PERMISSION": "slice_instances:id:",
624 },
625 },
626 "netslice_instances": {
627 "METHODS": ("GET", "POST"),
628 "ROLE_PERMISSION": "slice_instances:",
629 "<ID>": {
630 "METHODS": ("GET", "DELETE"),
631 "ROLE_PERMISSION": "slice_instances:id:",
632 "terminate": {
633 "METHODS": ("POST",),
634 "ROLE_PERMISSION": "slice_instances:id:terminate:",
635 },
636 "instantiate": {
637 "METHODS": ("POST",),
638 "ROLE_PERMISSION": "slice_instances:id:instantiate:",
639 },
640 "action": {
641 "METHODS": ("POST",),
642 "ROLE_PERMISSION": "slice_instances:id:action:",
643 },
644 },
645 },
646 "nsi_lcm_op_occs": {
647 "METHODS": ("GET",),
648 "ROLE_PERMISSION": "slice_instances:opps:",
649 "<ID>": {
650 "METHODS": ("GET",),
651 "ROLE_PERMISSION": "slice_instances:opps:id:",
652 },
653 },
tierno701018c2019-06-25 11:13:14 +0000654 }
655 },
656 "nspm": {
657 "v1": {
658 "pm_jobs": {
659 "<ID>": {
660 "reports": {
garciadeblas4568a372021-03-24 09:19:48 +0100661 "<ID>": {
662 "METHODS": ("GET",),
663 "ROLE_PERMISSION": "reports:id:",
664 }
tierno701018c2019-06-25 11:13:14 +0000665 }
666 },
667 },
668 },
669 },
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000670 "nsfm": {
671 "v1": {
garciadeblasf2af4a12023-01-24 16:56:54 +0100672 "alarms": {
673 "METHODS": ("GET", "PATCH"),
674 "ROLE_PERMISSION": "alarms:",
675 "<ID>": {
676 "METHODS": ("GET", "PATCH"),
677 "ROLE_PERMISSION": "alarms:id:",
678 },
679 }
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000680 },
681 },
rshri2d386cb2024-07-05 14:35:51 +0000682 "k8scluster": {
683 "v1": {
684 "clusters": {
685 "METHODS": ("GET", "POST"),
686 "ROLE_PERMISSION": "k8scluster:",
687 "<ID>": {
yshah99122b82024-11-18 07:05:29 +0000688 "METHODS": ("GET", "PATCH", "DELETE"),
rshri2d386cb2024-07-05 14:35:51 +0000689 "ROLE_PERMISSION": "k8scluster:id:",
690 "app_profiles": {
691 "METHODS": ("PATCH", "GET"),
692 "ROLE_PERMISSION": "k8scluster:id:app_profiles:",
693 },
694 "infra_controller_profiles": {
695 "METHODS": ("PATCH", "GET"),
696 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:",
697 },
698 "infra_config_profiles": {
699 "METHODS": ("PATCH", "GET"),
700 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:",
701 },
702 "resource_profiles": {
703 "METHODS": ("PATCH", "GET"),
704 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:",
705 },
706 "deregister": {
707 "METHODS": ("DELETE",),
708 "ROLE_PERMISSION": "k8scluster:id:deregister:",
709 },
yshah53cc9eb2024-07-05 13:06:31 +0000710 "get_creds": {
711 "METHODS": ("GET",),
712 "ROLE_PERMISSION": "k8scluster:id:get_creds:",
713 },
shahithyab9eb4142024-10-17 05:51:39 +0000714 "get_creds_file": {
715 "METHODS": ("GET",),
716 "ROLE_PERMISSION": "k8scluster:id:get_creds_file:",
717 "<ID>": {
718 "METHODS": ("GET",),
719 "ROLE_PERMISSION": "k8scluster:id:get_creds_file:id",
720 },
721 },
yshah99122b82024-11-18 07:05:29 +0000722 "update": {
723 "METHODS": ("POST",),
724 "ROLE_PERMISSION": "k8scluster:id:update:",
725 },
yshah53cc9eb2024-07-05 13:06:31 +0000726 "scale": {
727 "METHODS": ("POST",),
728 "ROLE_PERMISSION": "k8scluster:id:scale:",
729 },
730 "upgrade": {
731 "METHODS": ("POST",),
732 "ROLE_PERMISSION": "k8scluster:id:upgrade:",
733 },
yshahd23c6a52025-06-13 05:49:31 +0000734 "nodegroup": {
735 "METHODS": ("POST", "GET"),
736 "ROLE_PERMISSION": "k8scluster:id:nodegroup:",
737 "<ID>": {
738 "METHODS": ("GET", "PATCH", "DELETE"),
739 "ROLE_PERMISSION": "k8scluster:id:nodegroup:id",
740 "scale": {
741 "METHODS": ("POST",),
742 "ROLE_PERMISSION": "k8scluster:id:nodegroup:id:scale:",
743 },
744 },
745 },
746 "ksus": {
747 "METHODS": ("GET",),
748 "ROLE_PERMISSION": "k8scluster:id:ksus:",
749 },
rshri2d386cb2024-07-05 14:35:51 +0000750 },
751 "register": {
752 "METHODS": ("POST",),
753 "ROLE_PERMISSION": "k8scluster:register:",
754 },
755 },
756 "app_profiles": {
757 "METHODS": ("POST", "GET"),
758 "ROLE_PERMISSION": "k8scluster:app_profiles:",
759 "<ID>": {
760 "METHODS": ("GET", "PATCH", "DELETE"),
761 "ROLE_PERMISSION": "k8scluster:app_profiles:id:",
762 },
763 },
764 "infra_controller_profiles": {
765 "METHODS": ("POST", "GET"),
766 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:",
767 "<ID>": {
768 "METHODS": ("GET", "PATCH", "DELETE"),
769 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:id:",
770 },
771 },
772 "infra_config_profiles": {
773 "METHODS": ("POST", "GET"),
774 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:",
775 "<ID>": {
776 "METHODS": ("GET", "PATCH", "DELETE"),
777 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:id:",
778 },
779 },
780 "resource_profiles": {
781 "METHODS": ("POST", "GET"),
782 "ROLE_PERMISSION": "k8scluster:resource_profiles:",
783 "<ID>": {
784 "METHODS": ("GET", "PATCH", "DELETE"),
785 "ROLE_PERMISSION": "k8scluster:resource_profiles:id:",
786 },
787 },
788 }
789 },
yshah53cc9eb2024-07-05 13:06:31 +0000790 "ksu": {
791 "v1": {
792 "ksus": {
793 "METHODS": ("GET", "POST"),
794 "ROLE_PERMISSION": "ksu:",
795 "<ID>": {
796 "METHODS": ("GET", "PATCH", "DELETE"),
797 "ROLE_PERMISSION": "ksu:id:",
798 "clone": {
799 "METHODS": ("POST",),
800 "ROLE_PERMISSION": "ksu:id:clone:",
801 },
802 "move": {
803 "METHODS": ("POST",),
804 "ROLE_PERMISSION": "ksu:id:move:",
805 },
806 },
807 "update": {
808 "METHODS": ("POST",),
809 "ROLE_PERMISSION": "ksu:",
810 },
811 "delete": {
812 "METHODS": ("POST",),
813 "ROLE_PERMISSION": "ksu:",
814 },
815 },
816 }
817 },
garciadeblasb798f452025-08-05 18:21:26 +0200818 "appinstance": {
819 "v1": {
820 "appinstances": {
821 "METHODS": ("GET", "POST"),
822 "ROLE_PERMISSION": "appinstance:",
823 "<ID>": {
824 "METHODS": ("GET", "PATCH", "DELETE"),
825 "ROLE_PERMISSION": "appinstance:id:",
826 },
827 "update": {
828 "METHODS": ("POST",),
829 "ROLE_PERMISSION": "appinstance:",
830 },
831 },
832 }
833 },
yshah53cc9eb2024-07-05 13:06:31 +0000834 "oka": {
835 "v1": {
836 "oka_packages": {
837 "METHODS": ("GET", "POST"),
838 "ROLE_PERMISSION": "oka_pkg:",
839 "<ID>": {
840 "METHODS": ("GET", "PATCH", "DELETE", "PUT"),
841 "ROLE_PERMISSION": "oka_pkg:id:",
842 },
843 }
844 }
845 },
tierno701018c2019-06-25 11:13:14 +0000846}
847
tiernoc94c3df2018-02-09 15:38:54 +0100848
849class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100850 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
851 Exception.__init__(self, message)
852 self.http_code = http_code
853
854
855class Server(object):
856 instance = 0
857 # to decode bytes to str
858 reader = getreader("utf-8")
859
860 def __init__(self):
861 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000862 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100863 self.engine = Engine(self.authenticator)
yshah53cc9eb2024-07-05 13:06:31 +0000864 self.logger = logging.getLogger("nbi.server")
tiernoc94c3df2018-02-09 15:38:54 +0100865
tiernoc94c3df2018-02-09 15:38:54 +0100866 def _format_in(self, kwargs):
garciadeblasf2af4a12023-01-24 16:56:54 +0100867 error_text = "" # error_text must be initialized outside try
tiernoc94c3df2018-02-09 15:38:54 +0100868 try:
869 indata = None
870 if cherrypy.request.body.length:
871 error_text = "Invalid input format "
872
873 if "Content-Type" in cherrypy.request.headers:
874 if "application/json" in cherrypy.request.headers["Content-Type"]:
875 error_text = "Invalid json format "
876 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100877 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100878 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
879 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100880 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100881 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100882 elif (
883 "application/binary" in cherrypy.request.headers["Content-Type"]
884 or "application/gzip"
885 in cherrypy.request.headers["Content-Type"]
886 or "application/zip" in cherrypy.request.headers["Content-Type"]
887 or "text/plain" in cherrypy.request.headers["Content-Type"]
888 ):
tiernof27c79b2018-03-12 17:08:42 +0100889 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100890 elif (
891 "multipart/form-data"
892 in cherrypy.request.headers["Content-Type"]
893 ):
yshah53cc9eb2024-07-05 13:06:31 +0000894 if (
895 "descriptor_file" in kwargs
896 or "package" in kwargs
897 and "name" in kwargs
898 ):
899 filecontent = ""
900 if "descriptor_file" in kwargs:
901 filecontent = kwargs.pop("descriptor_file")
902 if "package" in kwargs:
903 filecontent = kwargs.pop("package")
tiernoc94c3df2018-02-09 15:38:54 +0100904 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100905 raise NbiException(
906 "empty file or content", HTTPStatus.BAD_REQUEST
907 )
yshah53cc9eb2024-07-05 13:06:31 +0000908 indata = filecontent
909 if filecontent.content_type.value:
910 cherrypy.request.headers[
911 "Content-Type"
912 ] = filecontent.content_type.value
913 elif "package" in kwargs:
914 filecontent = kwargs.pop("package")
915 if not filecontent.file:
916 raise NbiException(
917 "empty file or content", HTTPStatus.BAD_REQUEST
918 )
919 indata = filecontent
tiernoc94c3df2018-02-09 15:38:54 +0100920 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100921 cherrypy.request.headers[
922 "Content-Type"
923 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100924 else:
925 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
926 # "Only 'Content-Type' of type 'application/json' or
927 # 'application/yaml' for input format are available")
928 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100929 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100930 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100931 else:
932 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100933 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100934 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100935 if not indata:
936 indata = {}
tiernoc94c3df2018-02-09 15:38:54 +0100937 format_yaml = False
938 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
939 format_yaml = True
940
941 for k, v in kwargs.items():
942 if isinstance(v, str):
943 if v == "":
944 kwargs[k] = None
945 elif format_yaml:
946 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100947 kwargs[k] = yaml.safe_load(v)
tiernoe1281182018-05-22 12:24:36 +0200948 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100949 pass
garciadeblas4568a372021-03-24 09:19:48 +0100950 elif (
951 k.endswith(".gt")
952 or k.endswith(".lt")
953 or k.endswith(".gte")
954 or k.endswith(".lte")
955 ):
tiernoc94c3df2018-02-09 15:38:54 +0100956 try:
957 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200958 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100959 try:
960 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200961 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100962 pass
963 elif v.find(",") > 0:
964 kwargs[k] = v.split(",")
965 elif isinstance(v, (list, tuple)):
966 for index in range(0, len(v)):
967 if v[index] == "":
968 v[index] = None
969 elif format_yaml:
970 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100971 v[index] = yaml.safe_load(v[index])
tiernoe1281182018-05-22 12:24:36 +0200972 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100973 pass
974
tiernof27c79b2018-03-12 17:08:42 +0100975 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100976 except (ValueError, yaml.YAMLError) as exc:
977 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
978 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100979 raise NbiException(
980 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
981 )
tiernob92094f2018-05-11 13:44:22 +0200982 except Exception as exc:
983 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100984
985 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000986 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100987 """
988 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100989 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000990 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000991 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100992 :return: None
993 """
tierno0f98af52018-03-19 10:28:22 +0100994 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100995 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100996 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100997 return html.format(
998 data, cherrypy.request, cherrypy.response, token_info
999 )
tierno09c073e2018-04-26 13:36:48 +02001000 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +01001001 return
1002 elif hasattr(data, "read"): # file object
1003 if _format:
1004 cherrypy.response.headers["Content-Type"] = _format
1005 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +01001006 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +01001007 else:
garciadeblas4568a372021-03-24 09:19:48 +01001008 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001009 # TODO check that cherrypy close file. If not implement pending things to close per thread next
1010 return data
tierno0f98af52018-03-19 10:28:22 +01001011 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +00001012 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +01001013 return html.format(
1014 data, cherrypy.request, cherrypy.response, token_info
1015 )
Frank Brydenb5422da2020-08-10 11:44:11 +00001016 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +01001017 pass
garciadeblas4568a372021-03-24 09:19:48 +01001018 elif "application/json" in accept or (
1019 cherrypy.response.status and cherrypy.response.status >= 300
1020 ):
1021 cherrypy.response.headers[
1022 "Content-Type"
1023 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +00001024 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +01001025 return a.encode("utf8")
1026 cherrypy.response.headers["Content-Type"] = "application/yaml"
1027 return yaml.safe_dump(
1028 data,
1029 explicit_start=True,
1030 indent=4,
1031 default_flow_style=False,
1032 tags=False,
1033 encoding="utf-8",
1034 allow_unicode=True,
1035 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +01001036
1037 @cherrypy.expose
1038 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +00001039 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001040 try:
1041 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +00001042 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +01001043 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +01001044 else:
garciadeblas4568a372021-03-24 09:19:48 +01001045 raise cherrypy.HTTPError(
1046 HTTPStatus.METHOD_NOT_ALLOWED.value,
1047 "Method {} not allowed for tokens".format(cherrypy.request.method),
1048 )
tiernoc94c3df2018-02-09 15:38:54 +01001049
tierno701018c2019-06-25 11:13:14 +00001050 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001051
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001052 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +00001053 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +01001054 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +00001055 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001056
1057 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +02001058 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +02001059 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +02001060 try:
1061 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001062 raise NbiException(
1063 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
1064 )
tierno55945e72018-04-06 16:40:27 +02001065 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001066 raise NbiException(
1067 "Invalid URL or query string for version",
1068 HTTPStatus.METHOD_NOT_ALLOWED,
1069 )
tierno9c630112019-08-29 14:21:41 +00001070 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +00001071 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
1072 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +02001073 except NbiException as e:
1074 cherrypy.response.status = e.http_code.value
1075 problem_details = {
1076 "code": e.http_code.name,
1077 "status": e.http_code.value,
1078 "detail": str(e),
1079 }
1080 return self._format_out(problem_details, None)
1081
tierno12eac3c2020-03-19 23:22:08 +00001082 def domain(self):
1083 try:
1084 domains = {
garciadeblas4568a372021-03-24 09:19:48 +01001085 "user_domain_name": cherrypy.tree.apps["/osm"]
1086 .config["authentication"]
1087 .get("user_domain_name"),
1088 "project_domain_name": cherrypy.tree.apps["/osm"]
1089 .config["authentication"]
1090 .get("project_domain_name"),
1091 }
tierno12eac3c2020-03-19 23:22:08 +00001092 return self._format_out(domains)
1093 except NbiException as e:
1094 cherrypy.response.status = e.http_code.value
1095 problem_details = {
1096 "code": e.http_code.name,
1097 "status": e.http_code.value,
1098 "detail": str(e),
1099 }
1100 return self._format_out(problem_details, None)
1101
tiernoa5035702019-07-29 08:54:42 +00001102 @staticmethod
1103 def _format_login(token_info):
1104 """
1105 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
1106 log this information
1107 :param token_info: Dictionary with token content
1108 :return: None
1109 """
1110 cherrypy.request.login = token_info.get("username", "-")
1111 if token_info.get("project_name"):
1112 cherrypy.request.login += "/" + token_info["project_name"]
1113 if token_info.get("id"):
1114 cherrypy.request.login += ";session=" + token_info["id"][0:12]
1115
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001116 # NS Fault Management
1117 @cherrypy.expose
garciadeblasf2af4a12023-01-24 16:56:54 +01001118 def nsfm(
1119 self,
1120 version=None,
1121 topic=None,
1122 uuid=None,
1123 project_name=None,
1124 ns_id=None,
1125 *args,
garciadeblas57dddce2025-07-04 09:24:53 +02001126 **kwargs,
garciadeblasf2af4a12023-01-24 16:56:54 +01001127 ):
1128 if topic == "alarms":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001129 try:
1130 method = cherrypy.request.method
garciadeblasf2af4a12023-01-24 16:56:54 +01001131 role_permission = self._check_valid_url_method(
1132 method, "nsfm", version, topic, None, None, *args
1133 )
1134 query_string_operations = self._extract_query_string_operations(
1135 kwargs, method
1136 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001137
garciadeblasf2af4a12023-01-24 16:56:54 +01001138 self.authenticator.authorize(
1139 role_permission, query_string_operations, None
1140 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001141
1142 # to handle get request
garciadeblasf2af4a12023-01-24 16:56:54 +01001143 if cherrypy.request.method == "GET":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001144 # if request is on basis of uuid
garciadeblasf2af4a12023-01-24 16:56:54 +01001145 if uuid and uuid != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001146 try:
1147 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
garciadeblasf2af4a12023-01-24 16:56:54 +01001148 alarm_action = self.engine.db.get_one(
1149 "alarms_action", {"uuid": uuid}
1150 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001151 alarm.update(alarm_action)
garciadeblasf2af4a12023-01-24 16:56:54 +01001152 vnf = self.engine.db.get_one(
1153 "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}
1154 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001155 alarm["vnf-id"] = vnf["_id"]
1156 return self._format_out(str(alarm))
1157 except Exception:
1158 return self._format_out("Please provide valid alarm uuid")
garciadeblasf2af4a12023-01-24 16:56:54 +01001159 elif ns_id and ns_id != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001160 # if request is on basis of ns_id
1161 try:
garciadeblasf2af4a12023-01-24 16:56:54 +01001162 alarms = self.engine.db.get_list(
1163 "alarms", {"tags.ns_id": ns_id}
1164 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001165 for alarm in alarms:
garciadeblasf2af4a12023-01-24 16:56:54 +01001166 alarm_action = self.engine.db.get_one(
1167 "alarms_action", {"uuid": alarm["uuid"]}
1168 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001169 alarm.update(alarm_action)
1170 return self._format_out(str(alarms))
1171 except Exception:
1172 return self._format_out("Please provide valid ns id")
1173 else:
1174 # to return only alarm which are related to given project
garciadeblasf2af4a12023-01-24 16:56:54 +01001175 project = self.engine.db.get_one(
1176 "projects", {"name": project_name}
1177 )
1178 project_id = project.get("_id")
1179 ns_list = self.engine.db.get_list(
1180 "nsrs", {"_admin.projects_read": project_id}
1181 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001182 ns_ids = []
1183 for ns in ns_list:
1184 ns_ids.append(ns.get("_id"))
1185 alarms = self.engine.db.get_list("alarms")
garciadeblasf2af4a12023-01-24 16:56:54 +01001186 alarm_list = [
1187 alarm
1188 for alarm in alarms
1189 if alarm["tags"]["ns_id"] in ns_ids
1190 ]
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001191 for alrm in alarm_list:
garciadeblasf2af4a12023-01-24 16:56:54 +01001192 action = self.engine.db.get_one(
1193 "alarms_action", {"uuid": alrm.get("uuid")}
1194 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001195 alrm.update(action)
1196 return self._format_out(str(alarm_list))
1197 # to handle patch request for alarm update
garciadeblasf2af4a12023-01-24 16:56:54 +01001198 elif cherrypy.request.method == "PATCH":
garciadeblas4cd875d2023-02-14 19:05:34 +01001199 data = yaml.safe_load(cherrypy.request.body)
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001200 try:
1201 # check if uuid is valid
1202 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
1203 except Exception:
1204 return self._format_out("Please provide valid alarm uuid.")
1205 if data.get("is_enable") is not None:
1206 if data.get("is_enable"):
garciadeblasf2af4a12023-01-24 16:56:54 +01001207 alarm_status = "ok"
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001208 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001209 alarm_status = "disabled"
1210 self.engine.db.set_one(
1211 "alarms",
1212 {"uuid": data.get("uuid")},
1213 {"alarm_status": alarm_status},
1214 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001215 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001216 self.engine.db.set_one(
1217 "alarms",
1218 {"uuid": data.get("uuid")},
1219 {"threshold": data.get("threshold")},
1220 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001221 return self._format_out("Alarm updated")
1222 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01001223 if isinstance(
1224 e,
1225 (
1226 NbiException,
1227 EngineException,
1228 DbException,
1229 FsException,
1230 MsgException,
1231 AuthException,
1232 ValidationError,
1233 AuthconnException,
1234 ),
1235 ):
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001236 http_code_value = cherrypy.response.status = e.http_code.value
1237 http_code_name = e.http_code.name
1238 cherrypy.log("Exception {}".format(e))
1239 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001240 http_code_value = (
1241 cherrypy.response.status
1242 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001243 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
1244 http_code_name = HTTPStatus.BAD_REQUEST.name
1245 problem_details = {
1246 "code": http_code_name,
1247 "status": http_code_value,
1248 "detail": str(e),
1249 }
1250 return self._format_out(problem_details)
1251
tierno55945e72018-04-06 16:40:27 +02001252 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +01001253 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +00001254 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001255 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +01001256 indata = self._format_in(kwargs)
1257 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001258 raise NbiException(
1259 "Expected application/yaml or application/json Content-Type",
1260 HTTPStatus.BAD_REQUEST,
1261 )
tiernoa5035702019-07-29 08:54:42 +00001262
1263 if method == "GET":
1264 token_info = self.authenticator.authorize()
1265 # for logging
1266 self._format_login(token_info)
1267 if token_id:
1268 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +01001269 else:
tiernoa5035702019-07-29 08:54:42 +00001270 outdata = self.authenticator.get_token_list(token_info)
1271 elif method == "POST":
1272 try:
1273 token_info = self.authenticator.authorize()
1274 except Exception:
1275 token_info = None
1276 if kwargs:
1277 indata.update(kwargs)
1278 # This is needed to log the user when authentication fails
1279 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +01001280 outdata = token_info = self.authenticator.new_token(
1281 token_info, indata, cherrypy.request.remote
1282 )
jeganbe1a3df2024-06-04 12:05:19 +00001283 if outdata.get("email") or outdata.get("otp") == "invalid":
1284 return self._format_out(outdata, token_info)
garciadeblasf2af4a12023-01-24 16:56:54 +01001285 cherrypy.session["Authorization"] = outdata["_id"] # pylint: disable=E1101
tiernoa5035702019-07-29 08:54:42 +00001286 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1287 # for logging
1288 self._format_login(token_info)
jeganbe1a3df2024-06-04 12:05:19 +00001289 if outdata.get("otp") == "valid":
1290 outdata = {
1291 "id": outdata["id"],
1292 "message": "valid_otp",
1293 "user_id": outdata["user_id"],
1294 }
selvi.ja9a1fc82022-04-04 06:54:30 +00001295 # password expiry check
jeganbe1a3df2024-06-04 12:05:19 +00001296 elif self.authenticator.check_password_expiry(outdata):
garciadeblasf2af4a12023-01-24 16:56:54 +01001297 outdata = {
1298 "id": outdata["id"],
1299 "message": "change_password",
1300 "user_id": outdata["user_id"],
1301 }
tiernoa5035702019-07-29 08:54:42 +00001302 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1303 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
elumalai7802ff82023-04-24 20:38:32 +05301304 cef_event(
1305 cef_logger,
1306 {
1307 "name": "User Login",
1308 "sourceUserName": token_info.get("username"),
1309 "message": "User Logged In, Project={} Outcome=Success".format(
1310 token_info.get("project_name")
1311 ),
1312 },
1313 )
1314 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001315 elif method == "DELETE":
1316 if not token_id and "id" in kwargs:
1317 token_id = kwargs["id"]
1318 elif not token_id:
1319 token_info = self.authenticator.authorize()
1320 # for logging
1321 self._format_login(token_info)
1322 token_id = token_info["_id"]
Rahulc72bc8e2023-12-05 11:54:38 +00001323 if current_backend != "keystone":
1324 token_details = self.engine.db.get_one("tokens", {"_id": token_id})
1325 current_user = token_details.get("username")
1326 current_project = token_details.get("project_name")
1327 else:
1328 current_user = "keystone backend"
1329 current_project = "keystone backend"
tiernoa5035702019-07-29 08:54:42 +00001330 outdata = self.authenticator.del_token(token_id)
1331 token_info = None
garciadeblasf2af4a12023-01-24 16:56:54 +01001332 cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101
elumalai7802ff82023-04-24 20:38:32 +05301333 cef_event(
1334 cef_logger,
1335 {
1336 "name": "User Logout",
1337 "sourceUserName": current_user,
1338 "message": "User Logged Out, Project={} Outcome=Success".format(
1339 current_project
1340 ),
1341 },
1342 )
1343 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001344 # cherrypy.response.cookie["Authorization"] = token_id
1345 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1346 else:
garciadeblas4568a372021-03-24 09:19:48 +01001347 raise NbiException(
1348 "Method {} not allowed for token".format(method),
1349 HTTPStatus.METHOD_NOT_ALLOWED,
1350 )
tiernoa5035702019-07-29 08:54:42 +00001351 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001352
1353 @cherrypy.expose
1354 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001355 if not cherrypy.config.get("server.enable_test") or (
1356 isinstance(cherrypy.config["server.enable_test"], str)
1357 and cherrypy.config["server.enable_test"].lower() == "false"
1358 ):
tierno4836bac2020-01-15 14:41:48 +00001359 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1360 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001361 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001362 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001363 return (
1364 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1365 "sleep/<time>\nmessage/topic\n</pre></html>"
1366 )
tiernof27c79b2018-03-12 17:08:42 +01001367
1368 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001369 try:
1370 # self.engine.load_dbase(cherrypy.request.app.config)
garciadeblasf2af4a12023-01-24 16:56:54 +01001371 pid = self.authenticator.create_admin_project()
1372 self.authenticator.create_admin_user(pid)
tiernoc94c3df2018-02-09 15:38:54 +01001373 return "Done. User 'admin', password 'admin' created"
1374 except Exception:
1375 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1376 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001377 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001378 return cherrypy.lib.static.serve_file(
1379 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1380 "text/plain",
1381 "attachment",
1382 )
tiernof27c79b2018-03-12 17:08:42 +01001383 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001384 f_path = (
1385 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1386 )
tiernof27c79b2018-03-12 17:08:42 +01001387 f = open(f_path, "r")
1388 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001389 return f
tierno55945e72018-04-06 16:40:27 +02001390
tiernof27c79b2018-03-12 17:08:42 +01001391 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001392 deleted_info = self.engine.db.del_list(args[1], kwargs)
1393 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1394 elif len(args) and args[0] == "fs-clear":
1395 if len(args) >= 2:
1396 folders = (args[1],)
1397 else:
1398 folders = self.engine.fs.dir_ls(".")
1399 for folder in folders:
1400 self.engine.fs.file_delete(folder)
1401 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001402 elif args and args[0] == "login":
1403 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001404 cherrypy.response.headers[
1405 "WWW-Authenticate"
1406 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001407 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1408 elif args and args[0] == "login2":
1409 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001410 cherrypy.response.headers[
1411 "WWW-Authenticate"
1412 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001413 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1414 elif args and args[0] == "sleep":
1415 sleep_time = 5
1416 try:
1417 sleep_time = int(args[1])
1418 except Exception:
1419 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1420 return self._format_out("Database already initialized")
1421 thread_info = cherrypy.thread_data
1422 print(thread_info)
1423 time.sleep(sleep_time)
1424 # thread_info
1425 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001426 main_topic = args[1]
1427 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001428 try:
garciadeblas4568a372021-03-24 09:19:48 +01001429 if cherrypy.request.method == "POST":
garciadeblas4cd875d2023-02-14 19:05:34 +01001430 to_send = yaml.safe_load(cherrypy.request.body)
tierno55945e72018-04-06 16:40:27 +02001431 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001432 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001433 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001434 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001435 for k, v in kwargs.items():
garciadeblas4cd875d2023-02-14 19:05:34 +01001436 v_dict = yaml.safe_load(v)
tiernof1509b22020-05-12 14:32:37 +00001437 self.engine.msg.write(main_topic, k, v_dict)
1438 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001439 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001440 return_text += "Error: " + str(e)
1441 return_text += "</pre></html>\n"
1442 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001443
1444 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001445 "<html><pre>\nheaders:\n args: {}\n".format(args)
1446 + " kwargs: {}\n".format(kwargs)
1447 + " headers: {}\n".format(cherrypy.request.headers)
1448 + " path_info: {}\n".format(cherrypy.request.path_info)
1449 + " query_string: {}\n".format(cherrypy.request.query_string)
garciadeblasf2af4a12023-01-24 16:56:54 +01001450 + " session: {}\n".format(cherrypy.session) # pylint: disable=E1101
garciadeblas4568a372021-03-24 09:19:48 +01001451 + " cookie: {}\n".format(cherrypy.request.cookie)
1452 + " method: {}\n".format(cherrypy.request.method)
garciadeblasf2af4a12023-01-24 16:56:54 +01001453 + " session: {}\n".format(
1454 cherrypy.session.get("fieldname") # pylint: disable=E1101
1455 )
garciadeblas4568a372021-03-24 09:19:48 +01001456 + " body:\n"
1457 )
tiernoc94c3df2018-02-09 15:38:54 +01001458 return_text += " length: {}\n".format(cherrypy.request.body.length)
1459 if cherrypy.request.body.length:
1460 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001461 str(
1462 cherrypy.request.body.read(
1463 int(cherrypy.request.headers.get("Content-Length", 0))
1464 )
1465 )
1466 )
tiernoc94c3df2018-02-09 15:38:54 +01001467 if thread_info:
1468 return_text += "thread: {}\n".format(thread_info)
1469 return_text += "</pre></html>"
1470 return return_text
1471
tierno701018c2019-06-25 11:13:14 +00001472 @staticmethod
1473 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001474 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001475 raise NbiException(
1476 "URL must contain at least 'main_topic/version/topic'",
1477 HTTPStatus.METHOD_NOT_ALLOWED,
1478 )
tiernof27c79b2018-03-12 17:08:42 +01001479
tierno701018c2019-06-25 11:13:14 +00001480 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001481 for arg in args:
1482 if arg is None:
1483 break
1484 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001485 raise NbiException(
1486 "URL contains unexpected extra items '{}'".format(arg),
1487 HTTPStatus.METHOD_NOT_ALLOWED,
1488 )
tiernof27c79b2018-03-12 17:08:42 +01001489
1490 if arg in reference:
1491 reference = reference[arg]
1492 elif "<ID>" in reference:
1493 reference = reference["<ID>"]
1494 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001495 # if there is content
1496 if reference["*"]:
1497 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001498 break
1499 else:
garciadeblas4568a372021-03-24 09:19:48 +01001500 raise NbiException(
1501 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1502 )
tiernof27c79b2018-03-12 17:08:42 +01001503 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001504 raise NbiException(
1505 "Method {} not supported yet for this URL".format(method),
1506 HTTPStatus.NOT_IMPLEMENTED,
1507 )
tierno2236d202018-05-16 19:05:16 +02001508 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001509 raise NbiException(
1510 "Method {} not supported for this URL".format(method),
1511 HTTPStatus.METHOD_NOT_ALLOWED,
1512 )
tierno701018c2019-06-25 11:13:14 +00001513 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001514
1515 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001516 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001517 """
1518 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001519 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001520 :param version:
tiernob24258a2018-10-04 18:39:49 +02001521 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001522 :param id:
1523 :return: None
1524 """
1525 # Use cherrypy.request.base for absoluted path and make use of request.header HOST just in case behind aNAT
garciadeblas4568a372021-03-24 09:19:48 +01001526 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1527 main_topic, version, topic, id
1528 )
tiernof27c79b2018-03-12 17:08:42 +01001529 return
1530
tierno65ca36d2019-02-12 19:27:52 +01001531 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001532 def _extract_query_string_operations(kwargs, method):
1533 """
1534
1535 :param kwargs:
1536 :return:
1537 """
1538 query_string_operations = []
1539 if kwargs:
1540 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1541 if qs in kwargs and kwargs[qs].lower() != "false":
1542 query_string_operations.append(qs.lower() + ":" + method.lower())
1543 return query_string_operations
1544
1545 @staticmethod
1546 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001547 """
1548 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1549 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001550 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001551 :param kwargs: query string input.
1552 :param method: http method: GET, POSST, PUT, ...
1553 :param _id:
1554 :return: admin_query dictionary with keys:
1555 public: True, False or None
1556 force: True or False
1557 project_id: tuple with projects used for accessing an element
1558 set_project: tuple with projects that a created element will belong to
1559 method: show, list, delete, write
1560 """
garciadeblas4568a372021-03-24 09:19:48 +01001561 admin_query = {
1562 "force": False,
1563 "project_id": (token_info["project_id"],),
1564 "username": token_info["username"],
Adurti76d4b762024-05-07 06:04:37 +00001565 "user_id": token_info["user_id"],
garciadeblas4568a372021-03-24 09:19:48 +01001566 "admin": token_info["admin"],
37177091c0322024-11-01 08:55:59 +00001567 "admin_show": token_info["admin_show"],
garciadeblas4568a372021-03-24 09:19:48 +01001568 "public": None,
1569 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1570 }
tierno65ca36d2019-02-12 19:27:52 +01001571 if kwargs:
1572 # FORCE
1573 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001574 if (
1575 kwargs["FORCE"].lower() != "false"
1576 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001577 admin_query["force"] = True
1578 del kwargs["FORCE"]
1579 # PUBLIC
1580 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001581 if (
1582 kwargs["PUBLIC"].lower() != "false"
1583 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001584 admin_query["public"] = True
1585 else:
1586 admin_query["public"] = False
1587 del kwargs["PUBLIC"]
1588 # ADMIN
1589 if "ADMIN" in kwargs:
1590 behave_as = kwargs.pop("ADMIN")
1591 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001592 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001593 raise NbiException(
1594 "Only admin projects can use 'ADMIN' query string",
1595 HTTPStatus.UNAUTHORIZED,
1596 )
1597 if (
1598 not behave_as or behave_as.lower() == "true"
1599 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001600 admin_query["project_id"] = ()
1601 elif isinstance(behave_as, (list, tuple)):
1602 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001603 else: # isinstance(behave_as, str)
1604 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001605 if "SET_PROJECT" in kwargs:
1606 set_project = kwargs.pop("SET_PROJECT")
1607 if not set_project:
1608 admin_query["set_project"] = list(admin_query["project_id"])
1609 else:
1610 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001611 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001612 if admin_query["project_id"]:
1613 for p in set_project:
1614 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001615 raise NbiException(
1616 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1617 "'ADMIN='{p}'".format(p=p),
1618 HTTPStatus.UNAUTHORIZED,
1619 )
tierno65ca36d2019-02-12 19:27:52 +01001620 admin_query["set_project"] = set_project
1621
1622 # PROJECT_READ
1623 # if "PROJECT_READ" in kwargs:
1624 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001625 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001626 if method == "GET":
1627 if _id:
1628 admin_query["method"] = "show"
1629 else:
1630 admin_query["method"] = "list"
1631 elif method == "DELETE":
1632 admin_query["method"] = "delete"
1633 else:
1634 admin_query["method"] = "write"
1635 return admin_query
1636
tiernoc94c3df2018-02-09 15:38:54 +01001637 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001638 def default(
1639 self,
1640 main_topic=None,
1641 version=None,
1642 topic=None,
1643 _id=None,
1644 item=None,
1645 *args,
garciadeblas57dddce2025-07-04 09:24:53 +02001646 **kwargs,
garciadeblas4568a372021-03-24 09:19:48 +01001647 ):
tierno701018c2019-06-25 11:13:14 +00001648 token_info = None
selvi.j9919bbc2023-04-26 12:22:13 +00001649 outdata = {}
tiernof27c79b2018-03-12 17:08:42 +01001650 _format = None
tierno0f98af52018-03-19 10:28:22 +01001651 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001652 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001653 rollback = []
tierno701018c2019-06-25 11:13:14 +00001654 engine_session = None
elumalai7802ff82023-04-24 20:38:32 +05301655 url_id = ""
1656 log_mapping = {
1657 "POST": "Creating",
1658 "GET": "Fetching",
1659 "DELETE": "Deleting",
1660 "PUT": "Updating",
1661 "PATCH": "Updating",
1662 }
tiernoc94c3df2018-02-09 15:38:54 +01001663 try:
tiernob24258a2018-10-04 18:39:49 +02001664 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001665 raise NbiException(
1666 "URL must contain at least 'main_topic/version/topic'",
1667 HTTPStatus.METHOD_NOT_ALLOWED,
1668 )
1669 if main_topic not in (
1670 "admin",
1671 "vnfpkgm",
1672 "nsd",
1673 "nslcm",
1674 "pdu",
1675 "nst",
1676 "nsilcm",
1677 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001678 "vnflcm",
rshri2d386cb2024-07-05 14:35:51 +00001679 "k8scluster",
yshah53cc9eb2024-07-05 13:06:31 +00001680 "ksu",
garciadeblasb798f452025-08-05 18:21:26 +02001681 "appinstance",
yshah53cc9eb2024-07-05 13:06:31 +00001682 "oka",
garciadeblas4568a372021-03-24 09:19:48 +01001683 ):
1684 raise NbiException(
1685 "URL main_topic '{}' not supported".format(main_topic),
1686 HTTPStatus.METHOD_NOT_ALLOWED,
1687 )
1688 if version != "v1":
1689 raise NbiException(
1690 "URL version '{}' not supported".format(version),
1691 HTTPStatus.METHOD_NOT_ALLOWED,
1692 )
elumalai7802ff82023-04-24 20:38:32 +05301693 if _id is not None:
1694 url_id = _id
tiernoc94c3df2018-02-09 15:38:54 +01001695
garciadeblas4568a372021-03-24 09:19:48 +01001696 if (
1697 kwargs
1698 and "METHOD" in kwargs
1699 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1700 ):
tiernof27c79b2018-03-12 17:08:42 +01001701 method = kwargs.pop("METHOD")
1702 else:
1703 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001704
garciadeblas4568a372021-03-24 09:19:48 +01001705 role_permission = self._check_valid_url_method(
1706 method, main_topic, version, topic, _id, item, *args
1707 )
1708 query_string_operations = self._extract_query_string_operations(
1709 kwargs, method
1710 )
tiernob24258a2018-10-04 18:39:49 +02001711 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001712 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001713 token_info = self.authenticator.authorize(
1714 role_permission, query_string_operations, _id
1715 )
tierno12eac3c2020-03-19 23:22:08 +00001716 if main_topic == "admin" and topic == "domains":
1717 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001718 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001719 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001720 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301721
vijay.r35ef2f72019-04-30 17:55:49 +05301722 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001723 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001724
tiernob24258a2018-10-04 18:39:49 +02001725 if main_topic == "nsd":
1726 engine_topic = "nsds"
kayal2001f71c2e82024-06-25 15:26:24 +05301727 if topic == "ns_config_template":
1728 engine_topic = "nsconfigtemps"
tiernob24258a2018-10-04 18:39:49 +02001729 elif main_topic == "vnfpkgm":
1730 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001731 if topic == "vnfpkg_op_occs":
1732 engine_topic = "vnfpkgops"
1733 if topic == "vnf_packages" and item == "action":
1734 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001735 elif main_topic == "nslcm":
1736 engine_topic = "nsrs"
1737 if topic == "ns_lcm_op_occs":
1738 engine_topic = "nslcmops"
1739 if topic == "vnfrs" or topic == "vnf_instances":
1740 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001741 elif main_topic == "vnflcm":
1742 if topic == "vnf_lcm_op_occs":
1743 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001744 elif main_topic == "nst":
1745 engine_topic = "nsts"
1746 elif main_topic == "nsilcm":
1747 engine_topic = "nsis"
1748 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001749 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001750 elif main_topic == "pdu":
1751 engine_topic = "pdus"
rshri2d386cb2024-07-05 14:35:51 +00001752 elif main_topic == "k8scluster":
shrinithi28d887f2025-01-08 05:27:19 +00001753 engine_topic = "cluster"
rshri2d386cb2024-07-05 14:35:51 +00001754 if topic == "clusters" and _id == "register" or item == "deregister":
shrinithi28d887f2025-01-08 05:27:19 +00001755 engine_topic = "clusterops"
rshri2d386cb2024-07-05 14:35:51 +00001756 elif topic == "infra_controller_profiles":
1757 engine_topic = "infras_cont"
1758 elif topic == "infra_config_profiles":
1759 engine_topic = "infras_conf"
1760 elif topic == "resource_profiles":
1761 engine_topic = "resources"
1762 elif topic == "app_profiles":
1763 engine_topic = "apps"
garciadeblas1fbe71a2025-08-05 09:36:10 +02001764 elif topic == "clusters" and item == "nodegroup":
yshahd23c6a52025-06-13 05:49:31 +00001765 engine_topic = "node_groups"
yshah53cc9eb2024-07-05 13:06:31 +00001766 elif main_topic == "ksu" and engine_topic in ("ksus", "clone", "move"):
1767 engine_topic = "ksus"
garciadeblasb798f452025-08-05 18:21:26 +02001768 elif main_topic == "appinstance":
1769 engine_topic = "appinstances"
garciadeblas4568a372021-03-24 09:19:48 +01001770 if (
1771 engine_topic == "vims"
1772 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001773 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001774
preethika.p329b8182020-04-22 12:25:39 +05301775 if topic == "subscriptions":
1776 engine_topic = main_topic + "_" + topic
1777
tiernoc94c3df2018-02-09 15:38:54 +01001778 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001779 if item in (
1780 "nsd_content",
1781 "package_content",
1782 "artifacts",
1783 "vnfd",
1784 "nsd",
1785 "nst",
1786 "nst_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301787 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001788 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001789 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001790 path = "$DESCRIPTOR"
1791 elif args:
1792 path = args
tiernob24258a2018-10-04 18:39:49 +02001793 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001794 path = ()
1795 else:
1796 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001797 file, _format = self.engine.get_file(
1798 engine_session,
1799 engine_topic,
1800 _id,
1801 path,
1802 cherrypy.request.headers.get("Accept"),
1803 )
tiernof27c79b2018-03-12 17:08:42 +01001804 outdata = file
shrinithi28d887f2025-01-08 05:27:19 +00001805 # elif not _id and topic != "clusters":
1806 # outdata = self.engine.get_item_list(
1807 # engine_session, engine_topic, kwargs, api_req=True
1808 # )
rshri2d386cb2024-07-05 14:35:51 +00001809 elif topic == "clusters" and item in (
1810 "infra_controller_profiles",
1811 "infra_config_profiles",
1812 "app_profiles",
1813 "resource_profiles",
1814 ):
1815 profile = item
1816 filter_q = None
1817 outdata = self.engine.get_one_item(
1818 engine_session,
1819 engine_topic,
1820 _id,
1821 profile,
1822 filter_q,
1823 api_req=True,
1824 )
shahithyab9eb4142024-10-17 05:51:39 +00001825 elif (
1826 topic == "clusters"
1827 and item == "get_creds_file"
1828 or item == "get_creds"
1829 ):
1830 if item == "get_creds_file":
1831 op_id = args[0]
1832 file, _format = self.engine.get_cluster_creds_file(
1833 engine_session, engine_topic, _id, item, op_id
1834 )
1835 outdata = file
1836 if item == "get_creds":
1837 op_id = self.engine.get_cluster_creds(
1838 engine_session, engine_topic, _id, item
1839 )
1840 outdata = {"op_id": op_id}
shrinithi28d887f2025-01-08 05:27:19 +00001841 elif topic == "clusters" and not _id:
1842 outdata = self.engine.get_item_list_cluster(
1843 engine_session, engine_topic, kwargs, api_req=True
1844 )
yshahd23c6a52025-06-13 05:49:31 +00001845 elif topic == "clusters" and item == "nodegroup" and args:
1846 _id = args[0]
1847 outdata = outdata = self.engine.get_item(
1848 engine_session, engine_topic, _id, kwargs, True
1849 )
1850 elif topic == "clusters" and item == "nodegroup":
1851 kwargs["cluster_id"] = _id
1852 outdata = self.engine.get_item_list(
1853 engine_session, engine_topic, kwargs, api_req=True
1854 )
1855 elif topic == "clusters" and item == "ksus":
1856 engine_topic = "ksus"
1857 kwargs["cluster_id"] = _id
1858 outdata = self.engine.get_cluster_list_ksu(
1859 engine_session, engine_topic, kwargs, api_req=True
1860 )
shrinithi28d887f2025-01-08 05:27:19 +00001861 elif not _id:
1862 outdata = self.engine.get_item_list(
1863 engine_session, engine_topic, kwargs, api_req=True
1864 )
tiernoc94c3df2018-02-09 15:38:54 +01001865 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301866 if item == "reports":
1867 # TODO check that project_id (_id in this context) has permissions
1868 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301869 filter_q = None
1870 if "vcaStatusRefresh" in kwargs:
1871 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
garciadeblasf2af4a12023-01-24 16:56:54 +01001872 outdata = self.engine.get_item(
1873 engine_session, engine_topic, _id, filter_q, True
1874 )
delacruzramo271d2002019-12-02 21:00:37 +01001875
tiernof27c79b2018-03-12 17:08:42 +01001876 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001877 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001878 if topic in (
1879 "ns_descriptors_content",
1880 "vnf_packages_content",
1881 "netslice_templates_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301882 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001883 ):
tiernof27c79b2018-03-12 17:08:42 +01001884 _id = cherrypy.request.headers.get("Transaction-Id")
yshah53cc9eb2024-07-05 13:06:31 +00001885
tiernof27c79b2018-03-12 17:08:42 +01001886 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001887 _id, _ = self.engine.new_item(
1888 rollback,
1889 engine_session,
1890 engine_topic,
1891 {},
1892 None,
1893 cherrypy.request.headers,
1894 )
1895 completed = self.engine.upload_content(
1896 engine_session,
1897 engine_topic,
1898 _id,
1899 indata,
1900 kwargs,
1901 cherrypy.request.headers,
1902 )
tiernof27c79b2018-03-12 17:08:42 +01001903 if completed:
tiernob24258a2018-10-04 18:39:49 +02001904 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001905 else:
1906 cherrypy.response.headers["Transaction-Id"] = _id
1907 outdata = {"id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001908 elif topic == "oka_packages":
1909 _id = cherrypy.request.headers.get("Transaction-Id")
1910
1911 if not _id:
1912 _id, _ = self.engine.new_item(
1913 rollback,
1914 engine_session,
1915 engine_topic,
1916 {},
1917 kwargs,
1918 cherrypy.request.headers,
1919 )
1920 cherrypy.request.headers["method"] = cherrypy.request.method
1921 if indata:
1922 completed = self.engine.upload_content(
1923 engine_session,
1924 engine_topic,
1925 _id,
1926 indata,
1927 None,
1928 cherrypy.request.headers,
1929 )
1930 if completed:
1931 self._set_location_header(main_topic, version, topic, _id)
1932 else:
1933 cherrypy.response.headers["Transaction-Id"] = _id
yshahe08aff12024-11-07 09:32:22 +00001934 outdata = {"_id": _id, "id": _id}
tiernob24258a2018-10-04 18:39:49 +02001935 elif topic == "ns_instances_content":
1936 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001937 _id, _ = self.engine.new_item(
1938 rollback, engine_session, engine_topic, indata, kwargs
1939 )
tiernob24258a2018-10-04 18:39:49 +02001940 # creates nslcmop
1941 indata["lcmOperationType"] = "instantiate"
1942 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001943 nslcmop_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001944 rollback, engine_session, "nslcmops", indata, None
1945 )
tiernob24258a2018-10-04 18:39:49 +02001946 self._set_location_header(main_topic, version, topic, _id)
garciadeblasf53612b2024-07-12 14:44:37 +02001947 outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName}
adurti3af50952024-05-31 11:36:57 +05301948 elif topic == "ns_instances_terminate":
1949 if indata.get("ns_ids"):
1950 for ns_id in indata.get("ns_ids"):
1951 nslcmop_desc = {
1952 "lcmOperationType": "terminate",
1953 "nsInstanceId": ns_id,
shahithyab9eb4142024-10-17 05:51:39 +00001954 "autoremove": (
1955 indata.get("autoremove")
1956 if "autoremove" in indata
1957 else True
1958 ),
adurti3af50952024-05-31 11:36:57 +05301959 }
1960 op_id, _, _ = self.engine.new_item(
1961 rollback,
1962 engine_session,
1963 "nslcmops",
1964 nslcmop_desc,
1965 kwargs,
1966 )
1967 if not op_id:
1968 _ = self.engine.del_item(
1969 engine_session, engine_topic, ns_id
1970 )
1971 outdata = {"ns_ids": indata.get("ns_ids")}
1972 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001973 elif topic == "ns_instances" and item:
1974 indata["lcmOperationType"] = item
1975 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001976 _id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001977 rollback, engine_session, "nslcmops", indata, kwargs
1978 )
1979 self._set_location_header(
1980 main_topic, version, "ns_lcm_op_occs", _id
1981 )
garciadeblasf53612b2024-07-12 14:44:37 +02001982 outdata = {"id": _id, "nsName": nsName}
tierno65acb4d2018-04-06 16:42:40 +02001983 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001984 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001985 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001986 _id, _ = self.engine.new_item(
1987 rollback, engine_session, engine_topic, indata, kwargs
1988 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001989 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001990 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001991 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001992 nsilcmop_id, _ = self.engine.new_item(
1993 rollback, engine_session, "nsilcmops", indata, kwargs
1994 )
kuuse078f55e2019-05-16 19:24:21 +02001995 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001996 elif topic == "netslice_instances" and item:
1997 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001998 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001999 _id, _ = self.engine.new_item(
2000 rollback, engine_session, "nsilcmops", indata, kwargs
2001 )
2002 self._set_location_header(
2003 main_topic, version, "nsi_lcm_op_occs", _id
2004 )
garciadeblas9750c5a2018-10-15 16:20:35 +02002005 outdata = {"id": _id}
2006 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01002007 elif topic == "vnf_packages" and item == "action":
2008 indata["lcmOperationType"] = item
2009 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01002010 _id, _ = self.engine.new_item(
2011 rollback, engine_session, "vnfpkgops", indata, kwargs
2012 )
2013 self._set_location_header(
2014 main_topic, version, "vnfpkg_op_occs", _id
2015 )
delacruzramo271d2002019-12-02 21:00:37 +01002016 outdata = {"id": _id}
2017 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05302018 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01002019 _id, _ = self.engine.new_item(
2020 rollback, engine_session, engine_topic, indata, kwargs
2021 )
preethika.p329b8182020-04-22 12:25:39 +05302022 self._set_location_header(main_topic, version, topic, _id)
2023 link = {}
2024 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01002025 outdata = {
2026 "id": _id,
2027 "filter": indata["filter"],
2028 "callbackUri": indata["CallbackUri"],
2029 "_links": link,
2030 }
preethika.p329b8182020-04-22 12:25:39 +05302031 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02002032 elif topic == "vnf_instances" and item:
2033 indata["lcmOperationType"] = item
2034 indata["vnfInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02002035 _id, nsName, _ = self.engine.new_item(
garciadeblasf2af4a12023-01-24 16:56:54 +01002036 rollback, engine_session, "vnflcmops", indata, kwargs
2037 )
2038 self._set_location_header(
2039 main_topic, version, "vnf_lcm_op_occs", _id
2040 )
garciadeblasf53612b2024-07-12 14:44:37 +02002041 outdata = {"id": _id, "nsName": nsName}
almagiae47b9132022-05-17 14:12:22 +02002042 cherrypy.response.status = HTTPStatus.ACCEPTED.value
Gabriel Cuba84a60df2023-10-30 14:01:54 -05002043 elif topic == "ns_lcm_op_occs" and item == "cancel":
2044 indata["nsLcmOpOccId"] = _id
2045 self.engine.cancel_item(
2046 rollback, engine_session, "nslcmops", indata, None
2047 )
2048 self._set_location_header(main_topic, version, topic, _id)
2049 cherrypy.response.status = HTTPStatus.ACCEPTED.value
rshri2d386cb2024-07-05 14:35:51 +00002050 elif topic == "clusters" and _id == "register":
2051 # To register a cluster
2052 _id, _ = self.engine.add_item(
2053 rollback, engine_session, engine_topic, indata, kwargs
2054 )
2055 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002056 outdata = {"_id": _id, "id": _id}
rshri2d386cb2024-07-05 14:35:51 +00002057 elif (
2058 topic
2059 in (
2060 "clusters",
2061 "infra_controller_profiles",
2062 "infra_config_profiles",
2063 "app_profiles",
2064 "resource_profiles",
2065 )
2066 and item is None
2067 ):
2068 # creates cluster, infra_controller_profiles, app_profiles, infra_config_profiles, and resource_profiles
2069 _id, _ = self.engine.new_item(
2070 rollback, engine_session, engine_topic, indata, kwargs
2071 )
2072 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002073 outdata = {"_id": _id, "id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00002074 elif topic == "ksus" and item:
2075 if item == "clone":
2076 _id = self.engine.clone(
2077 rollback,
2078 engine_session,
2079 engine_topic,
2080 _id,
2081 indata,
2082 kwargs,
2083 cherrypy.request.headers,
2084 )
2085 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002086 outdata = {"_id": _id, "id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00002087 if item == "move":
2088 op_id = self.engine.move_ksu(
2089 engine_session, engine_topic, _id, indata, kwargs
2090 )
2091 outdata = {"op_id": op_id}
2092 elif topic == "ksus" and _id == "delete":
2093 op_id = self.engine.delete_ksu(
2094 engine_session, engine_topic, _id, indata
2095 )
2096 outdata = {"op_id": op_id}
2097 elif topic == "ksus" and _id == "update":
2098 op_id = self.engine.edit_item(
2099 engine_session, engine_topic, _id, indata, kwargs
2100 )
2101 outdata = {"op_id": op_id}
garciadeblasb798f452025-08-05 18:21:26 +02002102 elif topic == "appinstances" and item == "update":
2103 op_id = self.engine.update_appinstance(
2104 engine_session, engine_topic, _id, indata, kwargs
2105 )
2106 outdata = {"op_id": op_id}
garciadeblasffce2a42025-03-21 11:39:39 +01002107 elif topic == "clusters" and item in ("upgrade", "scale"):
yshahd23c6a52025-06-13 05:49:31 +00002108 op_id = self.engine.update_item(
yshah53cc9eb2024-07-05 13:06:31 +00002109 engine_session, engine_topic, _id, item, indata
2110 )
2111 outdata = {"op_id": op_id}
yshahd23c6a52025-06-13 05:49:31 +00002112 elif topic == "clusters" and item == "nodegroup":
2113 indata["cluster_id"] = _id
2114 if args:
2115 _id = args[0]
2116 op_id = self.engine.update_item(
2117 engine_session, engine_topic, _id, item, indata
2118 )
2119 outdata = {"op_id": op_id}
2120 else:
2121 _id, _ = self.engine.new_item(
2122 rollback, engine_session, engine_topic, indata, kwargs
2123 )
2124 self._set_location_header(main_topic, version, topic, _id)
2125 outdata = {"_id": _id, "id": _id}
tiernof27c79b2018-03-12 17:08:42 +01002126 else:
garciadeblas4568a372021-03-24 09:19:48 +01002127 _id, op_id = self.engine.new_item(
2128 rollback,
2129 engine_session,
2130 engine_topic,
2131 indata,
2132 kwargs,
2133 cherrypy.request.headers,
2134 )
tiernob24258a2018-10-04 18:39:49 +02002135 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002136 outdata = {"_id": _id, "id": _id}
tiernobdebce92019-07-01 15:36:49 +00002137 if op_id:
2138 outdata["op_id"] = op_id
2139 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02002140 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02002141
tiernoc94c3df2018-02-09 15:38:54 +01002142 elif method == "DELETE":
2143 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01002144 outdata = self.engine.del_item_list(
2145 engine_session, engine_topic, kwargs
2146 )
tierno09c073e2018-04-26 13:36:48 +02002147 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01002148 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00002149 # for NS NSI generate an operation
2150 op_id = None
tierno701018c2019-06-25 11:13:14 +00002151 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02002152 nslcmop_desc = {
2153 "lcmOperationType": "terminate",
2154 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002155 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02002156 }
garciadeblasf53612b2024-07-12 14:44:37 +02002157 op_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01002158 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
2159 )
tierno22577432020-04-08 15:16:57 +00002160 if op_id:
garciadeblasf53612b2024-07-12 14:44:37 +02002161 outdata = {"_id": op_id, "nsName": nsName}
garciadeblas4568a372021-03-24 09:19:48 +01002162 elif (
2163 topic == "netslice_instances_content"
2164 and not engine_session["force"]
2165 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02002166 nsilcmop_desc = {
2167 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02002168 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002169 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02002170 }
garciadeblas4568a372021-03-24 09:19:48 +01002171 op_id, _ = self.engine.new_item(
2172 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
2173 )
tierno22577432020-04-08 15:16:57 +00002174 if op_id:
2175 outdata = {"_id": op_id}
rshri2d386cb2024-07-05 14:35:51 +00002176 elif topic == "clusters" and item == "deregister":
2177 if not op_id:
2178 op_id = self.engine.remove(
2179 engine_session, engine_topic, _id
2180 )
2181 if op_id:
2182 outdata = {"_id": op_id}
2183 cherrypy.response.status = (
2184 HTTPStatus.ACCEPTED.value
2185 if op_id
2186 else HTTPStatus.NO_CONTENT.value
2187 )
yshahd23c6a52025-06-13 05:49:31 +00002188 elif topic == "clusters" and item == "nodegroup" and args:
2189 _id = args[0]
2190 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2191 if op_id:
2192 outdata = {"_id": op_id}
yshah53cc9eb2024-07-05 13:06:31 +00002193 elif topic == "ksus":
2194 op_id = self.engine.delete_ksu(
2195 engine_session, engine_topic, _id, indata
2196 )
2197 outdata = {"op_id": op_id}
tierno22577432020-04-08 15:16:57 +00002198 # if there is not any deletion in process, delete
rshri2d386cb2024-07-05 14:35:51 +00002199 elif not op_id:
tierno22577432020-04-08 15:16:57 +00002200 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2201 if op_id:
rshri2d386cb2024-07-05 14:35:51 +00002202 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01002203 cherrypy.response.status = (
2204 HTTPStatus.ACCEPTED.value
2205 if op_id
2206 else HTTPStatus.NO_CONTENT.value
2207 )
tierno09c073e2018-04-26 13:36:48 +02002208
tierno7ae10112018-05-18 14:36:02 +02002209 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00002210 op_id = None
tierno701018c2019-06-25 11:13:14 +00002211 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01002212 raise NbiException(
2213 "Nothing to update. Provide payload and/or query string",
2214 HTTPStatus.BAD_REQUEST,
2215 )
2216 if (
kayal2001f71c2e82024-06-25 15:26:24 +05302217 item
2218 in (
2219 "nsd_content",
2220 "package_content",
2221 "nst_content",
2222 "template_content",
2223 )
garciadeblas4568a372021-03-24 09:19:48 +01002224 and method == "PUT"
2225 ):
2226 completed = self.engine.upload_content(
2227 engine_session,
2228 engine_topic,
2229 _id,
2230 indata,
2231 kwargs,
2232 cherrypy.request.headers,
2233 )
tiernof27c79b2018-03-12 17:08:42 +01002234 if not completed:
2235 cherrypy.response.headers["Transaction-Id"] = id
rshri2d386cb2024-07-05 14:35:51 +00002236 elif item in (
2237 "app_profiles",
2238 "resource_profiles",
2239 "infra_controller_profiles",
2240 "infra_config_profiles",
2241 ):
2242 op_id = self.engine.edit(
2243 engine_session, engine_topic, _id, item, indata, kwargs
2244 )
yshah53cc9eb2024-07-05 13:06:31 +00002245 elif topic == "oka_packages" and method == "PATCH":
2246 if kwargs:
2247 op_id = self.engine.edit_item(
2248 engine_session, engine_topic, _id, None, kwargs
2249 )
2250 if indata:
yshahffcac5f2024-08-19 12:49:07 +00002251 if isinstance(indata, dict):
yshah53cc9eb2024-07-05 13:06:31 +00002252 op_id = self.engine.edit_item(
2253 engine_session, engine_topic, _id, indata, kwargs
2254 )
2255 else:
2256 cherrypy.request.headers["method"] = cherrypy.request.method
2257 completed = self.engine.upload_content(
2258 engine_session,
2259 engine_topic,
2260 _id,
2261 indata,
2262 {},
2263 cherrypy.request.headers,
2264 )
2265 if not completed:
2266 cherrypy.response.headers["Transaction-Id"] = id
2267 elif topic == "oka_packages" and method == "PUT":
2268 if indata:
2269 cherrypy.request.headers["method"] = cherrypy.request.method
2270 completed = self.engine.upload_content(
2271 engine_session,
2272 engine_topic,
2273 _id,
2274 indata,
2275 {},
2276 cherrypy.request.headers,
2277 )
2278 if not completed:
2279 cherrypy.response.headers["Transaction-Id"] = id
yshahd23c6a52025-06-13 05:49:31 +00002280 elif topic == "clusters" and item == "nodegroup" and args:
2281 _id = args[0]
2282 op_id = self.engine.edit_item(
2283 engine_session, engine_topic, _id, indata, kwargs
2284 )
tiernof27c79b2018-03-12 17:08:42 +01002285 else:
garciadeblas4568a372021-03-24 09:19:48 +01002286 op_id = self.engine.edit_item(
2287 engine_session, engine_topic, _id, indata, kwargs
2288 )
tiernobdebce92019-07-01 15:36:49 +00002289
2290 if op_id:
2291 cherrypy.response.status = HTTPStatus.ACCEPTED.value
2292 outdata = {"op_id": op_id}
2293 else:
2294 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
2295 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01002296 else:
garciadeblas4568a372021-03-24 09:19:48 +01002297 raise NbiException(
2298 "Method {} not allowed".format(method),
2299 HTTPStatus.METHOD_NOT_ALLOWED,
2300 )
tiernoa6bb45d2019-06-14 09:45:39 +00002301
2302 # if Role information changes, it is needed to reload the information of roles
2303 if topic == "roles" and method != "GET":
2304 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01002305
garciadeblas4568a372021-03-24 09:19:48 +01002306 if (
2307 topic == "projects"
2308 and method == "DELETE"
2309 or topic in ["users", "roles"]
2310 and method in ["PUT", "PATCH", "DELETE"]
2311 ):
delacruzramoad682a52019-12-10 16:26:34 +01002312 self.authenticator.remove_token_from_cache()
2313
garciadeblasf53612b2024-07-12 14:44:37 +02002314 cef_event(
2315 cef_logger,
2316 {
2317 "name": "User Operation",
2318 "sourceUserName": token_info.get("username"),
2319 },
2320 )
2321 if topic == "ns_instances_content" and url_id:
2322 nsName = (
2323 outdata.get("name") if method == "GET" else outdata.get("nsName")
2324 )
elumalai7802ff82023-04-24 20:38:32 +05302325 cef_event(
2326 cef_logger,
2327 {
garciadeblasf53612b2024-07-12 14:44:37 +02002328 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2329 log_mapping[method],
2330 topic,
2331 nsName,
2332 outdata.get("id"),
2333 token_info.get("project_name"),
2334 ),
2335 },
2336 )
2337 cherrypy.log("{}".format(cef_logger))
2338 elif topic == "ns_instances_content" and method == "POST":
2339 cef_event(
2340 cef_logger,
2341 {
2342 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2343 log_mapping[method],
2344 topic,
2345 outdata.get("nsName"),
2346 outdata.get("id"),
2347 token_info.get("project_name"),
2348 ),
2349 },
2350 )
2351 cherrypy.log("{}".format(cef_logger))
2352 elif topic in ("ns_instances", "vnf_instances") and item:
2353 cef_event(
2354 cef_logger,
2355 {
2356 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2357 log_mapping[method],
2358 topic,
2359 outdata.get("nsName"),
2360 url_id,
2361 token_info.get("project_name"),
2362 ),
2363 },
2364 )
2365 cherrypy.log("{}".format(cef_logger))
2366 elif item is not None:
2367 cef_event(
2368 cef_logger,
2369 {
elumalai7802ff82023-04-24 20:38:32 +05302370 "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format(
2371 item,
2372 topic,
2373 url_id,
2374 token_info.get("project_name"),
2375 ),
2376 },
2377 )
2378 cherrypy.log("{}".format(cef_logger))
2379 else:
2380 cef_event(
2381 cef_logger,
2382 {
elumalai7802ff82023-04-24 20:38:32 +05302383 "message": "{} {} {}, Project={} Outcome=Success".format(
2384 log_mapping[method],
2385 topic,
2386 url_id,
2387 token_info.get("project_name"),
2388 ),
2389 },
2390 )
2391 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002392 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02002393 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01002394 if isinstance(
2395 e,
2396 (
2397 NbiException,
2398 EngineException,
2399 DbException,
2400 FsException,
2401 MsgException,
2402 AuthException,
2403 ValidationError,
2404 AuthconnException,
2405 ),
2406 ):
tiernob24258a2018-10-04 18:39:49 +02002407 http_code_value = cherrypy.response.status = e.http_code.value
2408 http_code_name = e.http_code.name
2409 cherrypy.log("Exception {}".format(e))
2410 else:
garciadeblas4568a372021-03-24 09:19:48 +01002411 http_code_value = (
2412 cherrypy.response.status
2413 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01002414 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02002415 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02002416 if hasattr(outdata, "close"): # is an open file
2417 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02002418 error_text = str(e)
2419 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02002420 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02002421 try:
tiernocc103432018-10-19 14:10:35 +02002422 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01002423 self.engine.db.set_one(
2424 rollback_item["topic"],
2425 {"_id": rollback_item["_id"]},
2426 rollback_item["content"],
2427 fail_on_empty=False,
2428 )
preethika.p329b8182020-04-22 12:25:39 +05302429 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01002430 self.engine.db.del_list(
2431 rollback_item["topic"],
2432 rollback_item["filter"],
garciadeblas4568a372021-03-24 09:19:48 +01002433 )
tiernocc103432018-10-19 14:10:35 +02002434 else:
garciadeblas4568a372021-03-24 09:19:48 +01002435 self.engine.db.del_one(
2436 rollback_item["topic"],
2437 {"_id": rollback_item["_id"]},
2438 fail_on_empty=False,
2439 )
tierno3ace63c2018-05-03 17:51:43 +02002440 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01002441 rollback_error_text = "Rollback Exception {}: {}".format(
2442 rollback_item, e2
2443 )
tiernob24258a2018-10-04 18:39:49 +02002444 cherrypy.log(rollback_error_text)
2445 error_text += ". " + rollback_error_text
2446 # if isinstance(e, MsgException):
2447 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
2448 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01002449 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02002450 "code": http_code_name,
2451 "status": http_code_value,
2452 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01002453 }
elumalai7802ff82023-04-24 20:38:32 +05302454 if item is not None and token_info is not None:
2455 cef_event(
2456 cef_logger,
2457 {
2458 "name": "User Operation",
2459 "sourceUserName": token_info.get("username", None),
2460 "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format(
2461 item,
2462 topic,
2463 url_id,
2464 token_info.get("project_name", None),
2465 ),
2466 "severity": "2",
2467 },
2468 )
2469 cherrypy.log("{}".format(cef_logger))
2470 elif token_info is not None:
2471 cef_event(
2472 cef_logger,
2473 {
2474 "name": "User Operation",
2475 "sourceUserName": token_info.get("username", None),
2476 "message": "{} {} {}, Project={} Outcome=Failure".format(
2477 item,
2478 topic,
2479 url_id,
2480 token_info.get("project_name", None),
2481 ),
2482 "severity": "2",
2483 },
2484 )
2485 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002486 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01002487 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00002488 finally:
2489 if token_info:
2490 self._format_login(token_info)
2491 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
2492 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
2493 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01002494 cherrypy.request.login += ";{}={}".format(
2495 logging_id, outdata[logging_id][:36]
2496 )
tiernoc94c3df2018-02-09 15:38:54 +01002497
2498
tiernoc94c3df2018-02-09 15:38:54 +01002499def _start_service():
2500 """
2501 Callback function called when cherrypy.engine starts
2502 Override configuration with env variables
2503 Set database, storage, message configuration
2504 Init database with admin/admin user password
2505 """
tierno932499c2019-01-28 17:28:10 +00002506 global subscription_thread
elumalai7802ff82023-04-24 20:38:32 +05302507 global cef_logger
Rahulc72bc8e2023-12-05 11:54:38 +00002508 global current_backend
tiernoc94c3df2018-02-09 15:38:54 +01002509 cherrypy.log.error("Starting osm_nbi")
2510 # update general cherrypy configuration
2511 update_dict = {}
2512
garciadeblas4568a372021-03-24 09:19:48 +01002513 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01002514 for k, v in environ.items():
garciadeblas6d83f8f2023-06-19 22:34:49 +02002515 if k == "OSMNBI_USER_MANAGEMENT":
garciadeblas7a0dace2024-09-17 18:00:50 +02002516 feature_state = v.lower() == "true"
garciadeblas6d83f8f2023-06-19 22:34:49 +02002517 engine_config["authentication"]["user_management"] = feature_state
Adurtid68526d2023-11-14 11:14:53 +00002518 elif k == "OSMNBI_PWD_EXPIRE_DAYS":
2519 pwd_expire_days = int(v)
2520 engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days
2521 elif k == "OSMNBI_MAX_PWD_ATTEMPT":
2522 max_pwd_attempt = int(v)
2523 engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt
2524 elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS":
2525 account_expire_days = int(v)
2526 engine_config["authentication"]["account_expire_days"] = account_expire_days
jeganbe1a3df2024-06-04 12:05:19 +00002527 elif k == "OSMNBI_SMTP_SERVER":
2528 engine_config["authentication"]["smtp_server"] = v
2529 engine_config["authentication"]["all"] = environ
2530 elif k == "OSMNBI_SMTP_PORT":
2531 port = int(v)
2532 engine_config["authentication"]["smtp_port"] = port
2533 elif k == "OSMNBI_SENDER_EMAIL":
2534 engine_config["authentication"]["sender_email"] = v
2535 elif k == "OSMNBI_EMAIL_PASSWORD":
2536 engine_config["authentication"]["sender_password"] = v
2537 elif k == "OSMNBI_OTP_RETRY_COUNT":
2538 otp_retry_count = int(v)
2539 engine_config["authentication"]["retry_count"] = otp_retry_count
2540 elif k == "OSMNBI_OTP_EXPIRY_TIME":
2541 otp_expiry_time = int(v)
2542 engine_config["authentication"]["otp_expiry_time"] = otp_expiry_time
tiernoc94c3df2018-02-09 15:38:54 +01002543 if not k.startswith("OSMNBI_"):
2544 continue
tiernoe1281182018-05-22 12:24:36 +02002545 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01002546 if not k2:
2547 continue
2548 try:
2549 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01002550 if k == "OSMNBI_STATIC_DIR":
2551 engine_config["/static"]["tools.staticdir.dir"] = v
2552 engine_config["/static"]["tools.staticdir.on"] = True
2553 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
2554 update_dict["server.socket_port"] = int(v)
2555 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
2556 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02002557 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01002558 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002559 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02002560 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002561 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01002562 engine_config[k1][k2] = int(v)
2563 else:
2564 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002565
tiernoc94c3df2018-02-09 15:38:54 +01002566 except ValueError as e:
2567 cherrypy.log.error("Ignoring environ '{}': " + str(e))
2568 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01002569 cherrypy.log(
2570 "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
2571 )
tiernoc94c3df2018-02-09 15:38:54 +01002572
2573 if update_dict:
2574 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02002575 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01002576
2577 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01002578 log_format_simple = (
2579 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
2580 )
2581 log_formatter_simple = logging.Formatter(
2582 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
2583 )
tiernoc94c3df2018-02-09 15:38:54 +01002584 logger_server = logging.getLogger("cherrypy.error")
2585 logger_access = logging.getLogger("cherrypy.access")
2586 logger_cherry = logging.getLogger("cherrypy")
2587 logger_nbi = logging.getLogger("nbi")
2588
tiernof5298be2018-05-16 14:43:57 +02002589 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01002590 file_handler = logging.handlers.RotatingFileHandler(
2591 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
2592 )
tiernoc94c3df2018-02-09 15:38:54 +01002593 file_handler.setFormatter(log_formatter_simple)
2594 logger_cherry.addHandler(file_handler)
2595 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02002596 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01002597 for format_, logger in {
2598 "nbi.server %(filename)s:%(lineno)s": logger_server,
2599 "nbi.access %(filename)s:%(lineno)s": logger_access,
2600 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
2601 }.items():
tiernob24258a2018-10-04 18:39:49 +02002602 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01002603 log_formatter_cherry = logging.Formatter(
2604 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
2605 )
tiernob24258a2018-10-04 18:39:49 +02002606 str_handler = logging.StreamHandler()
2607 str_handler.setFormatter(log_formatter_cherry)
2608 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01002609
tiernof5298be2018-05-16 14:43:57 +02002610 if engine_config["global"].get("log.level"):
2611 logger_cherry.setLevel(engine_config["global"]["log.level"])
2612 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01002613
2614 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01002615 for k1, logname in {
2616 "message": "nbi.msg",
2617 "database": "nbi.db",
2618 "storage": "nbi.fs",
2619 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01002620 engine_config[k1]["logger_name"] = logname
2621 logger_module = logging.getLogger(logname)
2622 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01002623 file_handler = logging.handlers.RotatingFileHandler(
2624 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
2625 )
tiernoc94c3df2018-02-09 15:38:54 +01002626 file_handler.setFormatter(log_formatter_simple)
2627 logger_module.addHandler(file_handler)
2628 if "loglevel" in engine_config[k1]:
2629 logger_module.setLevel(engine_config[k1]["loglevel"])
2630 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01002631 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
2632 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
2633 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
2634 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
2635 target_version=auth_database_version
2636 )
tiernobee508e2019-01-21 11:21:49 +00002637
elumalai7802ff82023-04-24 20:38:32 +05302638 cef_logger = cef_event_builder(engine_config["authentication"])
2639
tierno932499c2019-01-28 17:28:10 +00002640 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01002641 subscription_thread = SubscriptionThread(
2642 config=engine_config, engine=nbi_server.engine
2643 )
tierno932499c2019-01-28 17:28:10 +00002644 subscription_thread.start()
2645 # Do not capture except SubscriptionException
2646
tiernob2e48bd2020-02-04 15:47:18 +00002647 backend = engine_config["authentication"]["backend"]
Rahulc72bc8e2023-12-05 11:54:38 +00002648 current_backend = backend
garciadeblas4568a372021-03-24 09:19:48 +01002649 cherrypy.log.error(
2650 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
2651 nbi_version, nbi_version_date, backend
2652 )
2653 )
tiernoc94c3df2018-02-09 15:38:54 +01002654
2655
2656def _stop_service():
2657 """
2658 Callback function called when cherrypy.engine stops
2659 TODO: Ending database connections.
2660 """
tierno932499c2019-01-28 17:28:10 +00002661 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01002662 if subscription_thread:
2663 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00002664 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01002665 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01002666 cherrypy.log.error("Stopping osm_nbi")
2667
tierno2236d202018-05-16 19:05:16 +02002668
tiernof5298be2018-05-16 14:43:57 +02002669def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00002670 global nbi_server
tierno932499c2019-01-28 17:28:10 +00002671 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01002672 cherrypy.engine.subscribe("start", _start_service)
2673 cherrypy.engine.subscribe("stop", _stop_service)
2674 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02002675
2676
2677def usage():
garciadeblas4568a372021-03-24 09:19:48 +01002678 print(
2679 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02002680 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
2681 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01002682 """.format(
2683 sys.argv[0]
2684 )
2685 )
tierno2236d202018-05-16 19:05:16 +02002686 # --log-socket-host HOST: send logs to this host")
2687 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002688
2689
garciadeblas4568a372021-03-24 09:19:48 +01002690if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002691 try:
2692 # load parameters and configuration
2693 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2694 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2695 config_file = None
2696 for o, a in opts:
2697 if o in ("-h", "--help"):
2698 usage()
2699 sys.exit()
2700 elif o in ("-c", "--config"):
2701 config_file = a
2702 # elif o == "--log-socket-port":
2703 # log_socket_port = a
2704 # elif o == "--log-socket-host":
2705 # log_socket_host = a
2706 # elif o == "--log-file":
2707 # log_file = a
2708 else:
garciadeblascffd2782025-07-03 17:52:04 +02002709 raise getopt.GetoptError(f"Unhandled option: {o}")
tiernof5298be2018-05-16 14:43:57 +02002710 if config_file:
2711 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002712 print(
2713 "configuration file '{}' that not exist".format(config_file),
2714 file=sys.stderr,
2715 )
tiernof5298be2018-05-16 14:43:57 +02002716 exit(1)
2717 else:
garciadeblas4568a372021-03-24 09:19:48 +01002718 for config_file in (
2719 __file__[: __file__.rfind(".")] + ".cfg",
2720 "./nbi.cfg",
2721 "/etc/osm/nbi.cfg",
2722 ):
tiernof5298be2018-05-16 14:43:57 +02002723 if path.isfile(config_file):
2724 break
2725 else:
garciadeblas4568a372021-03-24 09:19:48 +01002726 print(
2727 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2728 file=sys.stderr,
2729 )
tiernof5298be2018-05-16 14:43:57 +02002730 exit(1)
2731 nbi(config_file)
2732 except getopt.GetoptError as e:
2733 print(str(e), file=sys.stderr)
2734 # usage()
2735 exit(1)