blob: 852dff06cefaba8b4ff798198b46c50da9aef32e [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"),
353 "ROLE_PERMISSION": "nsds:",
354 "<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
371 "ROLE_PERMISSION": "nsds:id:content:",
372 },
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"),
409 "ROLE_PERMISSION": "vnfds:",
410 "<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
423 "ROLE_PERMISSION": "vnfds:id:",
424 "upload_from_uri": {
425 "METHODS": (),
426 "TODO": ("POST",),
427 "ROLE_PERMISSION": "vnfds:id:upload:",
428 },
429 },
430 "vnfd": {
431 "METHODS": ("GET",), # descriptor inside package
432 "ROLE_PERMISSION": "vnfds:id:content:",
433 },
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"),
464 "ROLE_PERMISSION": "ns_instances:",
465 "<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>": {
yshah4ad91e12024-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 },
shahithya11664ac2024-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 },
yshah4ad91e12024-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 },
rshri2d386cb2024-07-05 14:35:51 +0000734 },
735 "register": {
736 "METHODS": ("POST",),
737 "ROLE_PERMISSION": "k8scluster:register:",
738 },
739 },
740 "app_profiles": {
741 "METHODS": ("POST", "GET"),
742 "ROLE_PERMISSION": "k8scluster:app_profiles:",
743 "<ID>": {
744 "METHODS": ("GET", "PATCH", "DELETE"),
745 "ROLE_PERMISSION": "k8scluster:app_profiles:id:",
746 },
747 },
748 "infra_controller_profiles": {
749 "METHODS": ("POST", "GET"),
750 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:",
751 "<ID>": {
752 "METHODS": ("GET", "PATCH", "DELETE"),
753 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:id:",
754 },
755 },
756 "infra_config_profiles": {
757 "METHODS": ("POST", "GET"),
758 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:",
759 "<ID>": {
760 "METHODS": ("GET", "PATCH", "DELETE"),
761 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:id:",
762 },
763 },
764 "resource_profiles": {
765 "METHODS": ("POST", "GET"),
766 "ROLE_PERMISSION": "k8scluster:resource_profiles:",
767 "<ID>": {
768 "METHODS": ("GET", "PATCH", "DELETE"),
769 "ROLE_PERMISSION": "k8scluster:resource_profiles:id:",
770 },
771 },
772 }
773 },
yshah53cc9eb2024-07-05 13:06:31 +0000774 "ksu": {
775 "v1": {
776 "ksus": {
777 "METHODS": ("GET", "POST"),
778 "ROLE_PERMISSION": "ksu:",
779 "<ID>": {
780 "METHODS": ("GET", "PATCH", "DELETE"),
781 "ROLE_PERMISSION": "ksu:id:",
782 "clone": {
783 "METHODS": ("POST",),
784 "ROLE_PERMISSION": "ksu:id:clone:",
785 },
786 "move": {
787 "METHODS": ("POST",),
788 "ROLE_PERMISSION": "ksu:id:move:",
789 },
790 },
791 "update": {
792 "METHODS": ("POST",),
793 "ROLE_PERMISSION": "ksu:",
794 },
795 "delete": {
796 "METHODS": ("POST",),
797 "ROLE_PERMISSION": "ksu:",
798 },
799 },
800 }
801 },
802 "oka": {
803 "v1": {
804 "oka_packages": {
805 "METHODS": ("GET", "POST"),
806 "ROLE_PERMISSION": "oka_pkg:",
807 "<ID>": {
808 "METHODS": ("GET", "PATCH", "DELETE", "PUT"),
809 "ROLE_PERMISSION": "oka_pkg:id:",
810 },
811 }
812 }
813 },
tierno701018c2019-06-25 11:13:14 +0000814}
815
tiernoc94c3df2018-02-09 15:38:54 +0100816
817class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100818 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
819 Exception.__init__(self, message)
820 self.http_code = http_code
821
822
823class Server(object):
824 instance = 0
825 # to decode bytes to str
826 reader = getreader("utf-8")
827
828 def __init__(self):
829 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000830 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100831 self.engine = Engine(self.authenticator)
yshah53cc9eb2024-07-05 13:06:31 +0000832 self.logger = logging.getLogger("nbi.server")
tiernoc94c3df2018-02-09 15:38:54 +0100833
tiernoc94c3df2018-02-09 15:38:54 +0100834 def _format_in(self, kwargs):
garciadeblasf2af4a12023-01-24 16:56:54 +0100835 error_text = "" # error_text must be initialized outside try
tiernoc94c3df2018-02-09 15:38:54 +0100836 try:
837 indata = None
838 if cherrypy.request.body.length:
839 error_text = "Invalid input format "
840
841 if "Content-Type" in cherrypy.request.headers:
842 if "application/json" in cherrypy.request.headers["Content-Type"]:
843 error_text = "Invalid json format "
844 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100845 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100846 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
847 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100848 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100849 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100850 elif (
851 "application/binary" in cherrypy.request.headers["Content-Type"]
852 or "application/gzip"
853 in cherrypy.request.headers["Content-Type"]
854 or "application/zip" in cherrypy.request.headers["Content-Type"]
855 or "text/plain" in cherrypy.request.headers["Content-Type"]
856 ):
tiernof27c79b2018-03-12 17:08:42 +0100857 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100858 elif (
859 "multipart/form-data"
860 in cherrypy.request.headers["Content-Type"]
861 ):
yshah53cc9eb2024-07-05 13:06:31 +0000862 if (
863 "descriptor_file" in kwargs
864 or "package" in kwargs
865 and "name" in kwargs
866 ):
867 filecontent = ""
868 if "descriptor_file" in kwargs:
869 filecontent = kwargs.pop("descriptor_file")
870 if "package" in kwargs:
871 filecontent = kwargs.pop("package")
tiernoc94c3df2018-02-09 15:38:54 +0100872 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100873 raise NbiException(
874 "empty file or content", HTTPStatus.BAD_REQUEST
875 )
yshah53cc9eb2024-07-05 13:06:31 +0000876 indata = filecontent
877 if filecontent.content_type.value:
878 cherrypy.request.headers[
879 "Content-Type"
880 ] = filecontent.content_type.value
881 elif "package" in kwargs:
882 filecontent = kwargs.pop("package")
883 if not filecontent.file:
884 raise NbiException(
885 "empty file or content", HTTPStatus.BAD_REQUEST
886 )
887 indata = filecontent
tiernoc94c3df2018-02-09 15:38:54 +0100888 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100889 cherrypy.request.headers[
890 "Content-Type"
891 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100892 else:
893 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
894 # "Only 'Content-Type' of type 'application/json' or
895 # 'application/yaml' for input format are available")
896 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100897 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100898 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100899 else:
900 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100901 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100902 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100903 if not indata:
904 indata = {}
tiernoc94c3df2018-02-09 15:38:54 +0100905 format_yaml = False
906 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
907 format_yaml = True
908
909 for k, v in kwargs.items():
910 if isinstance(v, str):
911 if v == "":
912 kwargs[k] = None
913 elif format_yaml:
914 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100915 kwargs[k] = yaml.safe_load(v)
tiernoe1281182018-05-22 12:24:36 +0200916 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100917 pass
garciadeblas4568a372021-03-24 09:19:48 +0100918 elif (
919 k.endswith(".gt")
920 or k.endswith(".lt")
921 or k.endswith(".gte")
922 or k.endswith(".lte")
923 ):
tiernoc94c3df2018-02-09 15:38:54 +0100924 try:
925 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200926 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100927 try:
928 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200929 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100930 pass
931 elif v.find(",") > 0:
932 kwargs[k] = v.split(",")
933 elif isinstance(v, (list, tuple)):
934 for index in range(0, len(v)):
935 if v[index] == "":
936 v[index] = None
937 elif format_yaml:
938 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100939 v[index] = yaml.safe_load(v[index])
tiernoe1281182018-05-22 12:24:36 +0200940 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100941 pass
942
tiernof27c79b2018-03-12 17:08:42 +0100943 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100944 except (ValueError, yaml.YAMLError) as exc:
945 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
946 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100947 raise NbiException(
948 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
949 )
tiernob92094f2018-05-11 13:44:22 +0200950 except Exception as exc:
951 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100952
953 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000954 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100955 """
956 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100957 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000958 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000959 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100960 :return: None
961 """
tierno0f98af52018-03-19 10:28:22 +0100962 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100963 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100964 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100965 return html.format(
966 data, cherrypy.request, cherrypy.response, token_info
967 )
tierno09c073e2018-04-26 13:36:48 +0200968 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100969 return
970 elif hasattr(data, "read"): # file object
971 if _format:
972 cherrypy.response.headers["Content-Type"] = _format
973 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100974 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100975 else:
garciadeblas4568a372021-03-24 09:19:48 +0100976 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100977 # TODO check that cherrypy close file. If not implement pending things to close per thread next
978 return data
tierno0f98af52018-03-19 10:28:22 +0100979 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000980 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100981 return html.format(
982 data, cherrypy.request, cherrypy.response, token_info
983 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000984 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100985 pass
garciadeblas4568a372021-03-24 09:19:48 +0100986 elif "application/json" in accept or (
987 cherrypy.response.status and cherrypy.response.status >= 300
988 ):
989 cherrypy.response.headers[
990 "Content-Type"
991 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000992 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100993 return a.encode("utf8")
994 cherrypy.response.headers["Content-Type"] = "application/yaml"
995 return yaml.safe_dump(
996 data,
997 explicit_start=True,
998 indent=4,
999 default_flow_style=False,
1000 tags=False,
1001 encoding="utf-8",
1002 allow_unicode=True,
1003 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +01001004
1005 @cherrypy.expose
1006 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +00001007 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001008 try:
1009 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +00001010 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +01001011 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +01001012 else:
garciadeblas4568a372021-03-24 09:19:48 +01001013 raise cherrypy.HTTPError(
1014 HTTPStatus.METHOD_NOT_ALLOWED.value,
1015 "Method {} not allowed for tokens".format(cherrypy.request.method),
1016 )
tiernoc94c3df2018-02-09 15:38:54 +01001017
tierno701018c2019-06-25 11:13:14 +00001018 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001019
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001020 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +00001021 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +01001022 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +00001023 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001024
1025 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +02001026 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +02001027 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +02001028 try:
1029 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001030 raise NbiException(
1031 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
1032 )
tierno55945e72018-04-06 16:40:27 +02001033 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001034 raise NbiException(
1035 "Invalid URL or query string for version",
1036 HTTPStatus.METHOD_NOT_ALLOWED,
1037 )
tierno9c630112019-08-29 14:21:41 +00001038 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +00001039 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
1040 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +02001041 except NbiException as e:
1042 cherrypy.response.status = e.http_code.value
1043 problem_details = {
1044 "code": e.http_code.name,
1045 "status": e.http_code.value,
1046 "detail": str(e),
1047 }
1048 return self._format_out(problem_details, None)
1049
tierno12eac3c2020-03-19 23:22:08 +00001050 def domain(self):
1051 try:
1052 domains = {
garciadeblas4568a372021-03-24 09:19:48 +01001053 "user_domain_name": cherrypy.tree.apps["/osm"]
1054 .config["authentication"]
1055 .get("user_domain_name"),
1056 "project_domain_name": cherrypy.tree.apps["/osm"]
1057 .config["authentication"]
1058 .get("project_domain_name"),
1059 }
tierno12eac3c2020-03-19 23:22:08 +00001060 return self._format_out(domains)
1061 except NbiException as e:
1062 cherrypy.response.status = e.http_code.value
1063 problem_details = {
1064 "code": e.http_code.name,
1065 "status": e.http_code.value,
1066 "detail": str(e),
1067 }
1068 return self._format_out(problem_details, None)
1069
tiernoa5035702019-07-29 08:54:42 +00001070 @staticmethod
1071 def _format_login(token_info):
1072 """
1073 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
1074 log this information
1075 :param token_info: Dictionary with token content
1076 :return: None
1077 """
1078 cherrypy.request.login = token_info.get("username", "-")
1079 if token_info.get("project_name"):
1080 cherrypy.request.login += "/" + token_info["project_name"]
1081 if token_info.get("id"):
1082 cherrypy.request.login += ";session=" + token_info["id"][0:12]
1083
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001084 # NS Fault Management
1085 @cherrypy.expose
garciadeblasf2af4a12023-01-24 16:56:54 +01001086 def nsfm(
1087 self,
1088 version=None,
1089 topic=None,
1090 uuid=None,
1091 project_name=None,
1092 ns_id=None,
1093 *args,
1094 **kwargs
1095 ):
1096 if topic == "alarms":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001097 try:
1098 method = cherrypy.request.method
garciadeblasf2af4a12023-01-24 16:56:54 +01001099 role_permission = self._check_valid_url_method(
1100 method, "nsfm", version, topic, None, None, *args
1101 )
1102 query_string_operations = self._extract_query_string_operations(
1103 kwargs, method
1104 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001105
garciadeblasf2af4a12023-01-24 16:56:54 +01001106 self.authenticator.authorize(
1107 role_permission, query_string_operations, None
1108 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001109
1110 # to handle get request
garciadeblasf2af4a12023-01-24 16:56:54 +01001111 if cherrypy.request.method == "GET":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001112 # if request is on basis of uuid
garciadeblasf2af4a12023-01-24 16:56:54 +01001113 if uuid and uuid != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001114 try:
1115 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
garciadeblasf2af4a12023-01-24 16:56:54 +01001116 alarm_action = self.engine.db.get_one(
1117 "alarms_action", {"uuid": uuid}
1118 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001119 alarm.update(alarm_action)
garciadeblasf2af4a12023-01-24 16:56:54 +01001120 vnf = self.engine.db.get_one(
1121 "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}
1122 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001123 alarm["vnf-id"] = vnf["_id"]
1124 return self._format_out(str(alarm))
1125 except Exception:
1126 return self._format_out("Please provide valid alarm uuid")
garciadeblasf2af4a12023-01-24 16:56:54 +01001127 elif ns_id and ns_id != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001128 # if request is on basis of ns_id
1129 try:
garciadeblasf2af4a12023-01-24 16:56:54 +01001130 alarms = self.engine.db.get_list(
1131 "alarms", {"tags.ns_id": ns_id}
1132 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001133 for alarm in alarms:
garciadeblasf2af4a12023-01-24 16:56:54 +01001134 alarm_action = self.engine.db.get_one(
1135 "alarms_action", {"uuid": alarm["uuid"]}
1136 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001137 alarm.update(alarm_action)
1138 return self._format_out(str(alarms))
1139 except Exception:
1140 return self._format_out("Please provide valid ns id")
1141 else:
1142 # to return only alarm which are related to given project
garciadeblasf2af4a12023-01-24 16:56:54 +01001143 project = self.engine.db.get_one(
1144 "projects", {"name": project_name}
1145 )
1146 project_id = project.get("_id")
1147 ns_list = self.engine.db.get_list(
1148 "nsrs", {"_admin.projects_read": project_id}
1149 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001150 ns_ids = []
1151 for ns in ns_list:
1152 ns_ids.append(ns.get("_id"))
1153 alarms = self.engine.db.get_list("alarms")
garciadeblasf2af4a12023-01-24 16:56:54 +01001154 alarm_list = [
1155 alarm
1156 for alarm in alarms
1157 if alarm["tags"]["ns_id"] in ns_ids
1158 ]
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001159 for alrm in alarm_list:
garciadeblasf2af4a12023-01-24 16:56:54 +01001160 action = self.engine.db.get_one(
1161 "alarms_action", {"uuid": alrm.get("uuid")}
1162 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001163 alrm.update(action)
1164 return self._format_out(str(alarm_list))
1165 # to handle patch request for alarm update
garciadeblasf2af4a12023-01-24 16:56:54 +01001166 elif cherrypy.request.method == "PATCH":
garciadeblas4cd875d2023-02-14 19:05:34 +01001167 data = yaml.safe_load(cherrypy.request.body)
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001168 try:
1169 # check if uuid is valid
1170 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
1171 except Exception:
1172 return self._format_out("Please provide valid alarm uuid.")
1173 if data.get("is_enable") is not None:
1174 if data.get("is_enable"):
garciadeblasf2af4a12023-01-24 16:56:54 +01001175 alarm_status = "ok"
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001176 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001177 alarm_status = "disabled"
1178 self.engine.db.set_one(
1179 "alarms",
1180 {"uuid": data.get("uuid")},
1181 {"alarm_status": alarm_status},
1182 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001183 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001184 self.engine.db.set_one(
1185 "alarms",
1186 {"uuid": data.get("uuid")},
1187 {"threshold": data.get("threshold")},
1188 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001189 return self._format_out("Alarm updated")
1190 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01001191 if isinstance(
1192 e,
1193 (
1194 NbiException,
1195 EngineException,
1196 DbException,
1197 FsException,
1198 MsgException,
1199 AuthException,
1200 ValidationError,
1201 AuthconnException,
1202 ),
1203 ):
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001204 http_code_value = cherrypy.response.status = e.http_code.value
1205 http_code_name = e.http_code.name
1206 cherrypy.log("Exception {}".format(e))
1207 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001208 http_code_value = (
1209 cherrypy.response.status
1210 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001211 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
1212 http_code_name = HTTPStatus.BAD_REQUEST.name
1213 problem_details = {
1214 "code": http_code_name,
1215 "status": http_code_value,
1216 "detail": str(e),
1217 }
1218 return self._format_out(problem_details)
1219
tierno55945e72018-04-06 16:40:27 +02001220 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +01001221 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +00001222 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001223 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +01001224 indata = self._format_in(kwargs)
1225 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001226 raise NbiException(
1227 "Expected application/yaml or application/json Content-Type",
1228 HTTPStatus.BAD_REQUEST,
1229 )
tiernoa5035702019-07-29 08:54:42 +00001230
1231 if method == "GET":
1232 token_info = self.authenticator.authorize()
1233 # for logging
1234 self._format_login(token_info)
1235 if token_id:
1236 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +01001237 else:
tiernoa5035702019-07-29 08:54:42 +00001238 outdata = self.authenticator.get_token_list(token_info)
1239 elif method == "POST":
1240 try:
1241 token_info = self.authenticator.authorize()
1242 except Exception:
1243 token_info = None
1244 if kwargs:
1245 indata.update(kwargs)
1246 # This is needed to log the user when authentication fails
1247 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +01001248 outdata = token_info = self.authenticator.new_token(
1249 token_info, indata, cherrypy.request.remote
1250 )
jegan2c4bf4e2024-06-04 12:05:19 +00001251 if outdata.get("email") or outdata.get("otp") == "invalid":
1252 return self._format_out(outdata, token_info)
garciadeblasf2af4a12023-01-24 16:56:54 +01001253 cherrypy.session["Authorization"] = outdata["_id"] # pylint: disable=E1101
tiernoa5035702019-07-29 08:54:42 +00001254 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1255 # for logging
1256 self._format_login(token_info)
jegan2c4bf4e2024-06-04 12:05:19 +00001257 if outdata.get("otp") == "valid":
1258 outdata = {
1259 "id": outdata["id"],
1260 "message": "valid_otp",
1261 "user_id": outdata["user_id"],
1262 }
selvi.ja9a1fc82022-04-04 06:54:30 +00001263 # password expiry check
jegan2c4bf4e2024-06-04 12:05:19 +00001264 elif self.authenticator.check_password_expiry(outdata):
garciadeblasf2af4a12023-01-24 16:56:54 +01001265 outdata = {
1266 "id": outdata["id"],
1267 "message": "change_password",
1268 "user_id": outdata["user_id"],
1269 }
tiernoa5035702019-07-29 08:54:42 +00001270 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1271 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
elumalai7802ff82023-04-24 20:38:32 +05301272 cef_event(
1273 cef_logger,
1274 {
1275 "name": "User Login",
1276 "sourceUserName": token_info.get("username"),
1277 "message": "User Logged In, Project={} Outcome=Success".format(
1278 token_info.get("project_name")
1279 ),
1280 },
1281 )
1282 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001283 elif method == "DELETE":
1284 if not token_id and "id" in kwargs:
1285 token_id = kwargs["id"]
1286 elif not token_id:
1287 token_info = self.authenticator.authorize()
1288 # for logging
1289 self._format_login(token_info)
1290 token_id = token_info["_id"]
Rahulc72bc8e2023-12-05 11:54:38 +00001291 if current_backend != "keystone":
1292 token_details = self.engine.db.get_one("tokens", {"_id": token_id})
1293 current_user = token_details.get("username")
1294 current_project = token_details.get("project_name")
1295 else:
1296 current_user = "keystone backend"
1297 current_project = "keystone backend"
tiernoa5035702019-07-29 08:54:42 +00001298 outdata = self.authenticator.del_token(token_id)
1299 token_info = None
garciadeblasf2af4a12023-01-24 16:56:54 +01001300 cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101
elumalai7802ff82023-04-24 20:38:32 +05301301 cef_event(
1302 cef_logger,
1303 {
1304 "name": "User Logout",
1305 "sourceUserName": current_user,
1306 "message": "User Logged Out, Project={} Outcome=Success".format(
1307 current_project
1308 ),
1309 },
1310 )
1311 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001312 # cherrypy.response.cookie["Authorization"] = token_id
1313 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1314 else:
garciadeblas4568a372021-03-24 09:19:48 +01001315 raise NbiException(
1316 "Method {} not allowed for token".format(method),
1317 HTTPStatus.METHOD_NOT_ALLOWED,
1318 )
tiernoa5035702019-07-29 08:54:42 +00001319 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001320
1321 @cherrypy.expose
1322 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001323 if not cherrypy.config.get("server.enable_test") or (
1324 isinstance(cherrypy.config["server.enable_test"], str)
1325 and cherrypy.config["server.enable_test"].lower() == "false"
1326 ):
tierno4836bac2020-01-15 14:41:48 +00001327 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1328 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001329 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001330 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001331 return (
1332 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1333 "sleep/<time>\nmessage/topic\n</pre></html>"
1334 )
tiernof27c79b2018-03-12 17:08:42 +01001335
1336 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001337 try:
1338 # self.engine.load_dbase(cherrypy.request.app.config)
garciadeblasf2af4a12023-01-24 16:56:54 +01001339 pid = self.authenticator.create_admin_project()
1340 self.authenticator.create_admin_user(pid)
tiernoc94c3df2018-02-09 15:38:54 +01001341 return "Done. User 'admin', password 'admin' created"
1342 except Exception:
1343 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1344 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001345 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001346 return cherrypy.lib.static.serve_file(
1347 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1348 "text/plain",
1349 "attachment",
1350 )
tiernof27c79b2018-03-12 17:08:42 +01001351 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001352 f_path = (
1353 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1354 )
tiernof27c79b2018-03-12 17:08:42 +01001355 f = open(f_path, "r")
1356 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001357 return f
tierno55945e72018-04-06 16:40:27 +02001358
tiernof27c79b2018-03-12 17:08:42 +01001359 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001360 deleted_info = self.engine.db.del_list(args[1], kwargs)
1361 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1362 elif len(args) and args[0] == "fs-clear":
1363 if len(args) >= 2:
1364 folders = (args[1],)
1365 else:
1366 folders = self.engine.fs.dir_ls(".")
1367 for folder in folders:
1368 self.engine.fs.file_delete(folder)
1369 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001370 elif args and args[0] == "login":
1371 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001372 cherrypy.response.headers[
1373 "WWW-Authenticate"
1374 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001375 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1376 elif args and args[0] == "login2":
1377 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001378 cherrypy.response.headers[
1379 "WWW-Authenticate"
1380 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001381 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1382 elif args and args[0] == "sleep":
1383 sleep_time = 5
1384 try:
1385 sleep_time = int(args[1])
1386 except Exception:
1387 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1388 return self._format_out("Database already initialized")
1389 thread_info = cherrypy.thread_data
1390 print(thread_info)
1391 time.sleep(sleep_time)
1392 # thread_info
1393 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001394 main_topic = args[1]
1395 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001396 try:
garciadeblas4568a372021-03-24 09:19:48 +01001397 if cherrypy.request.method == "POST":
garciadeblas4cd875d2023-02-14 19:05:34 +01001398 to_send = yaml.safe_load(cherrypy.request.body)
tierno55945e72018-04-06 16:40:27 +02001399 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001400 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001401 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001402 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001403 for k, v in kwargs.items():
garciadeblas4cd875d2023-02-14 19:05:34 +01001404 v_dict = yaml.safe_load(v)
tiernof1509b22020-05-12 14:32:37 +00001405 self.engine.msg.write(main_topic, k, v_dict)
1406 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001407 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001408 return_text += "Error: " + str(e)
1409 return_text += "</pre></html>\n"
1410 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001411
1412 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001413 "<html><pre>\nheaders:\n args: {}\n".format(args)
1414 + " kwargs: {}\n".format(kwargs)
1415 + " headers: {}\n".format(cherrypy.request.headers)
1416 + " path_info: {}\n".format(cherrypy.request.path_info)
1417 + " query_string: {}\n".format(cherrypy.request.query_string)
garciadeblasf2af4a12023-01-24 16:56:54 +01001418 + " session: {}\n".format(cherrypy.session) # pylint: disable=E1101
garciadeblas4568a372021-03-24 09:19:48 +01001419 + " cookie: {}\n".format(cherrypy.request.cookie)
1420 + " method: {}\n".format(cherrypy.request.method)
garciadeblasf2af4a12023-01-24 16:56:54 +01001421 + " session: {}\n".format(
1422 cherrypy.session.get("fieldname") # pylint: disable=E1101
1423 )
garciadeblas4568a372021-03-24 09:19:48 +01001424 + " body:\n"
1425 )
tiernoc94c3df2018-02-09 15:38:54 +01001426 return_text += " length: {}\n".format(cherrypy.request.body.length)
1427 if cherrypy.request.body.length:
1428 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001429 str(
1430 cherrypy.request.body.read(
1431 int(cherrypy.request.headers.get("Content-Length", 0))
1432 )
1433 )
1434 )
tiernoc94c3df2018-02-09 15:38:54 +01001435 if thread_info:
1436 return_text += "thread: {}\n".format(thread_info)
1437 return_text += "</pre></html>"
1438 return return_text
1439
tierno701018c2019-06-25 11:13:14 +00001440 @staticmethod
1441 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001442 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001443 raise NbiException(
1444 "URL must contain at least 'main_topic/version/topic'",
1445 HTTPStatus.METHOD_NOT_ALLOWED,
1446 )
tiernof27c79b2018-03-12 17:08:42 +01001447
tierno701018c2019-06-25 11:13:14 +00001448 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001449 for arg in args:
1450 if arg is None:
1451 break
1452 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001453 raise NbiException(
1454 "URL contains unexpected extra items '{}'".format(arg),
1455 HTTPStatus.METHOD_NOT_ALLOWED,
1456 )
tiernof27c79b2018-03-12 17:08:42 +01001457
1458 if arg in reference:
1459 reference = reference[arg]
1460 elif "<ID>" in reference:
1461 reference = reference["<ID>"]
1462 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001463 # if there is content
1464 if reference["*"]:
1465 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001466 break
1467 else:
garciadeblas4568a372021-03-24 09:19:48 +01001468 raise NbiException(
1469 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1470 )
tiernof27c79b2018-03-12 17:08:42 +01001471 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001472 raise NbiException(
1473 "Method {} not supported yet for this URL".format(method),
1474 HTTPStatus.NOT_IMPLEMENTED,
1475 )
tierno2236d202018-05-16 19:05:16 +02001476 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001477 raise NbiException(
1478 "Method {} not supported for this URL".format(method),
1479 HTTPStatus.METHOD_NOT_ALLOWED,
1480 )
tierno701018c2019-06-25 11:13:14 +00001481 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001482
1483 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001484 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001485 """
1486 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001487 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001488 :param version:
tiernob24258a2018-10-04 18:39:49 +02001489 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001490 :param id:
1491 :return: None
1492 """
1493 # 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 +01001494 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1495 main_topic, version, topic, id
1496 )
tiernof27c79b2018-03-12 17:08:42 +01001497 return
1498
tierno65ca36d2019-02-12 19:27:52 +01001499 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001500 def _extract_query_string_operations(kwargs, method):
1501 """
1502
1503 :param kwargs:
1504 :return:
1505 """
1506 query_string_operations = []
1507 if kwargs:
1508 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1509 if qs in kwargs and kwargs[qs].lower() != "false":
1510 query_string_operations.append(qs.lower() + ":" + method.lower())
1511 return query_string_operations
1512
1513 @staticmethod
1514 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001515 """
1516 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1517 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001518 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001519 :param kwargs: query string input.
1520 :param method: http method: GET, POSST, PUT, ...
1521 :param _id:
1522 :return: admin_query dictionary with keys:
1523 public: True, False or None
1524 force: True or False
1525 project_id: tuple with projects used for accessing an element
1526 set_project: tuple with projects that a created element will belong to
1527 method: show, list, delete, write
1528 """
garciadeblas4568a372021-03-24 09:19:48 +01001529 admin_query = {
1530 "force": False,
1531 "project_id": (token_info["project_id"],),
1532 "username": token_info["username"],
Adurtif0265c92024-05-07 06:04:37 +00001533 "user_id": token_info["user_id"],
garciadeblas4568a372021-03-24 09:19:48 +01001534 "admin": token_info["admin"],
371771188c192024-11-01 08:55:59 +00001535 "admin_show": token_info["admin_show"],
garciadeblas4568a372021-03-24 09:19:48 +01001536 "public": None,
1537 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1538 }
tierno65ca36d2019-02-12 19:27:52 +01001539 if kwargs:
1540 # FORCE
1541 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001542 if (
1543 kwargs["FORCE"].lower() != "false"
1544 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001545 admin_query["force"] = True
1546 del kwargs["FORCE"]
1547 # PUBLIC
1548 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001549 if (
1550 kwargs["PUBLIC"].lower() != "false"
1551 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001552 admin_query["public"] = True
1553 else:
1554 admin_query["public"] = False
1555 del kwargs["PUBLIC"]
1556 # ADMIN
1557 if "ADMIN" in kwargs:
1558 behave_as = kwargs.pop("ADMIN")
1559 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001560 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001561 raise NbiException(
1562 "Only admin projects can use 'ADMIN' query string",
1563 HTTPStatus.UNAUTHORIZED,
1564 )
1565 if (
1566 not behave_as or behave_as.lower() == "true"
1567 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001568 admin_query["project_id"] = ()
1569 elif isinstance(behave_as, (list, tuple)):
1570 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001571 else: # isinstance(behave_as, str)
1572 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001573 if "SET_PROJECT" in kwargs:
1574 set_project = kwargs.pop("SET_PROJECT")
1575 if not set_project:
1576 admin_query["set_project"] = list(admin_query["project_id"])
1577 else:
1578 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001579 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001580 if admin_query["project_id"]:
1581 for p in set_project:
1582 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001583 raise NbiException(
1584 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1585 "'ADMIN='{p}'".format(p=p),
1586 HTTPStatus.UNAUTHORIZED,
1587 )
tierno65ca36d2019-02-12 19:27:52 +01001588 admin_query["set_project"] = set_project
1589
1590 # PROJECT_READ
1591 # if "PROJECT_READ" in kwargs:
1592 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001593 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001594 if method == "GET":
1595 if _id:
1596 admin_query["method"] = "show"
1597 else:
1598 admin_query["method"] = "list"
1599 elif method == "DELETE":
1600 admin_query["method"] = "delete"
1601 else:
1602 admin_query["method"] = "write"
1603 return admin_query
1604
tiernoc94c3df2018-02-09 15:38:54 +01001605 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001606 def default(
1607 self,
1608 main_topic=None,
1609 version=None,
1610 topic=None,
1611 _id=None,
1612 item=None,
1613 *args,
1614 **kwargs
1615 ):
tierno701018c2019-06-25 11:13:14 +00001616 token_info = None
selvi.j9919bbc2023-04-26 12:22:13 +00001617 outdata = {}
tiernof27c79b2018-03-12 17:08:42 +01001618 _format = None
tierno0f98af52018-03-19 10:28:22 +01001619 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001620 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001621 rollback = []
tierno701018c2019-06-25 11:13:14 +00001622 engine_session = None
elumalai7802ff82023-04-24 20:38:32 +05301623 url_id = ""
1624 log_mapping = {
1625 "POST": "Creating",
1626 "GET": "Fetching",
1627 "DELETE": "Deleting",
1628 "PUT": "Updating",
1629 "PATCH": "Updating",
1630 }
tiernoc94c3df2018-02-09 15:38:54 +01001631 try:
tiernob24258a2018-10-04 18:39:49 +02001632 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001633 raise NbiException(
1634 "URL must contain at least 'main_topic/version/topic'",
1635 HTTPStatus.METHOD_NOT_ALLOWED,
1636 )
1637 if main_topic not in (
1638 "admin",
1639 "vnfpkgm",
1640 "nsd",
1641 "nslcm",
1642 "pdu",
1643 "nst",
1644 "nsilcm",
1645 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001646 "vnflcm",
rshri2d386cb2024-07-05 14:35:51 +00001647 "k8scluster",
yshah53cc9eb2024-07-05 13:06:31 +00001648 "ksu",
1649 "oka",
garciadeblas4568a372021-03-24 09:19:48 +01001650 ):
1651 raise NbiException(
1652 "URL main_topic '{}' not supported".format(main_topic),
1653 HTTPStatus.METHOD_NOT_ALLOWED,
1654 )
1655 if version != "v1":
1656 raise NbiException(
1657 "URL version '{}' not supported".format(version),
1658 HTTPStatus.METHOD_NOT_ALLOWED,
1659 )
elumalai7802ff82023-04-24 20:38:32 +05301660 if _id is not None:
1661 url_id = _id
tiernoc94c3df2018-02-09 15:38:54 +01001662
garciadeblas4568a372021-03-24 09:19:48 +01001663 if (
1664 kwargs
1665 and "METHOD" in kwargs
1666 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1667 ):
tiernof27c79b2018-03-12 17:08:42 +01001668 method = kwargs.pop("METHOD")
1669 else:
1670 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001671
garciadeblas4568a372021-03-24 09:19:48 +01001672 role_permission = self._check_valid_url_method(
1673 method, main_topic, version, topic, _id, item, *args
1674 )
1675 query_string_operations = self._extract_query_string_operations(
1676 kwargs, method
1677 )
tiernob24258a2018-10-04 18:39:49 +02001678 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001679 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001680 token_info = self.authenticator.authorize(
1681 role_permission, query_string_operations, _id
1682 )
tierno12eac3c2020-03-19 23:22:08 +00001683 if main_topic == "admin" and topic == "domains":
1684 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001685 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001686 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001687 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301688
vijay.r35ef2f72019-04-30 17:55:49 +05301689 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001690 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001691
tiernob24258a2018-10-04 18:39:49 +02001692 if main_topic == "nsd":
1693 engine_topic = "nsds"
kayal2001f71c2e82024-06-25 15:26:24 +05301694 if topic == "ns_config_template":
1695 engine_topic = "nsconfigtemps"
tiernob24258a2018-10-04 18:39:49 +02001696 elif main_topic == "vnfpkgm":
1697 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001698 if topic == "vnfpkg_op_occs":
1699 engine_topic = "vnfpkgops"
1700 if topic == "vnf_packages" and item == "action":
1701 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001702 elif main_topic == "nslcm":
1703 engine_topic = "nsrs"
1704 if topic == "ns_lcm_op_occs":
1705 engine_topic = "nslcmops"
1706 if topic == "vnfrs" or topic == "vnf_instances":
1707 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001708 elif main_topic == "vnflcm":
1709 if topic == "vnf_lcm_op_occs":
1710 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001711 elif main_topic == "nst":
1712 engine_topic = "nsts"
1713 elif main_topic == "nsilcm":
1714 engine_topic = "nsis"
1715 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001716 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001717 elif main_topic == "pdu":
1718 engine_topic = "pdus"
rshri2d386cb2024-07-05 14:35:51 +00001719 elif main_topic == "k8scluster":
1720 engine_topic = "k8s"
1721 if topic == "clusters" and _id == "register" or item == "deregister":
1722 engine_topic = "k8sops"
1723 elif topic == "infra_controller_profiles":
1724 engine_topic = "infras_cont"
1725 elif topic == "infra_config_profiles":
1726 engine_topic = "infras_conf"
1727 elif topic == "resource_profiles":
1728 engine_topic = "resources"
1729 elif topic == "app_profiles":
1730 engine_topic = "apps"
yshah53cc9eb2024-07-05 13:06:31 +00001731 elif main_topic == "k8scluster" and item in (
1732 "upgrade",
1733 "get_creds",
1734 "scale",
1735 ):
1736 engine_topic = "k8s"
1737 elif main_topic == "ksu" and engine_topic in ("ksus", "clone", "move"):
1738 engine_topic = "ksus"
garciadeblas4568a372021-03-24 09:19:48 +01001739 if (
1740 engine_topic == "vims"
1741 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001742 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001743
preethika.p329b8182020-04-22 12:25:39 +05301744 if topic == "subscriptions":
1745 engine_topic = main_topic + "_" + topic
1746
tiernoc94c3df2018-02-09 15:38:54 +01001747 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001748 if item in (
1749 "nsd_content",
1750 "package_content",
1751 "artifacts",
1752 "vnfd",
1753 "nsd",
1754 "nst",
1755 "nst_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301756 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001757 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001758 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001759 path = "$DESCRIPTOR"
1760 elif args:
1761 path = args
tiernob24258a2018-10-04 18:39:49 +02001762 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001763 path = ()
1764 else:
1765 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001766 file, _format = self.engine.get_file(
1767 engine_session,
1768 engine_topic,
1769 _id,
1770 path,
1771 cherrypy.request.headers.get("Accept"),
1772 )
tiernof27c79b2018-03-12 17:08:42 +01001773 outdata = file
1774 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001775 outdata = self.engine.get_item_list(
1776 engine_session, engine_topic, kwargs, api_req=True
1777 )
rshri2d386cb2024-07-05 14:35:51 +00001778 elif topic == "clusters" and item in (
1779 "infra_controller_profiles",
1780 "infra_config_profiles",
1781 "app_profiles",
1782 "resource_profiles",
1783 ):
1784 profile = item
1785 filter_q = None
1786 outdata = self.engine.get_one_item(
1787 engine_session,
1788 engine_topic,
1789 _id,
1790 profile,
1791 filter_q,
1792 api_req=True,
1793 )
shahithya11664ac2024-10-17 05:51:39 +00001794 elif (
1795 topic == "clusters"
1796 and item == "get_creds_file"
1797 or item == "get_creds"
1798 ):
1799 if item == "get_creds_file":
1800 op_id = args[0]
1801 file, _format = self.engine.get_cluster_creds_file(
1802 engine_session, engine_topic, _id, item, op_id
1803 )
1804 outdata = file
1805 if item == "get_creds":
1806 op_id = self.engine.get_cluster_creds(
1807 engine_session, engine_topic, _id, item
1808 )
1809 outdata = {"op_id": op_id}
tiernoc94c3df2018-02-09 15:38:54 +01001810 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301811 if item == "reports":
1812 # TODO check that project_id (_id in this context) has permissions
1813 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301814 filter_q = None
1815 if "vcaStatusRefresh" in kwargs:
1816 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
garciadeblasf2af4a12023-01-24 16:56:54 +01001817 outdata = self.engine.get_item(
1818 engine_session, engine_topic, _id, filter_q, True
1819 )
delacruzramo271d2002019-12-02 21:00:37 +01001820
tiernof27c79b2018-03-12 17:08:42 +01001821 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001822 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001823 if topic in (
1824 "ns_descriptors_content",
1825 "vnf_packages_content",
1826 "netslice_templates_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301827 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001828 ):
tiernof27c79b2018-03-12 17:08:42 +01001829 _id = cherrypy.request.headers.get("Transaction-Id")
yshah53cc9eb2024-07-05 13:06:31 +00001830
tiernof27c79b2018-03-12 17:08:42 +01001831 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001832 _id, _ = self.engine.new_item(
1833 rollback,
1834 engine_session,
1835 engine_topic,
1836 {},
1837 None,
1838 cherrypy.request.headers,
1839 )
1840 completed = self.engine.upload_content(
1841 engine_session,
1842 engine_topic,
1843 _id,
1844 indata,
1845 kwargs,
1846 cherrypy.request.headers,
1847 )
tiernof27c79b2018-03-12 17:08:42 +01001848 if completed:
tiernob24258a2018-10-04 18:39:49 +02001849 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001850 else:
1851 cherrypy.response.headers["Transaction-Id"] = _id
1852 outdata = {"id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001853 elif topic == "oka_packages":
1854 _id = cherrypy.request.headers.get("Transaction-Id")
1855
1856 if not _id:
1857 _id, _ = self.engine.new_item(
1858 rollback,
1859 engine_session,
1860 engine_topic,
1861 {},
1862 kwargs,
1863 cherrypy.request.headers,
1864 )
1865 cherrypy.request.headers["method"] = cherrypy.request.method
1866 if indata:
1867 completed = self.engine.upload_content(
1868 engine_session,
1869 engine_topic,
1870 _id,
1871 indata,
1872 None,
1873 cherrypy.request.headers,
1874 )
1875 if completed:
1876 self._set_location_header(main_topic, version, topic, _id)
1877 else:
1878 cherrypy.response.headers["Transaction-Id"] = _id
yshah93628192024-11-07 09:32:22 +00001879 outdata = {"_id": _id, "id": _id}
tiernob24258a2018-10-04 18:39:49 +02001880 elif topic == "ns_instances_content":
1881 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001882 _id, _ = self.engine.new_item(
1883 rollback, engine_session, engine_topic, indata, kwargs
1884 )
tiernob24258a2018-10-04 18:39:49 +02001885 # creates nslcmop
1886 indata["lcmOperationType"] = "instantiate"
1887 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001888 nslcmop_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001889 rollback, engine_session, "nslcmops", indata, None
1890 )
tiernob24258a2018-10-04 18:39:49 +02001891 self._set_location_header(main_topic, version, topic, _id)
garciadeblasf53612b2024-07-12 14:44:37 +02001892 outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName}
adurti3af50952024-05-31 11:36:57 +05301893 elif topic == "ns_instances_terminate":
1894 if indata.get("ns_ids"):
1895 for ns_id in indata.get("ns_ids"):
1896 nslcmop_desc = {
1897 "lcmOperationType": "terminate",
1898 "nsInstanceId": ns_id,
shahithya11664ac2024-10-17 05:51:39 +00001899 "autoremove": (
1900 indata.get("autoremove")
1901 if "autoremove" in indata
1902 else True
1903 ),
adurti3af50952024-05-31 11:36:57 +05301904 }
1905 op_id, _, _ = self.engine.new_item(
1906 rollback,
1907 engine_session,
1908 "nslcmops",
1909 nslcmop_desc,
1910 kwargs,
1911 )
1912 if not op_id:
1913 _ = self.engine.del_item(
1914 engine_session, engine_topic, ns_id
1915 )
1916 outdata = {"ns_ids": indata.get("ns_ids")}
1917 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001918 elif topic == "ns_instances" and item:
1919 indata["lcmOperationType"] = item
1920 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001921 _id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001922 rollback, engine_session, "nslcmops", indata, kwargs
1923 )
1924 self._set_location_header(
1925 main_topic, version, "ns_lcm_op_occs", _id
1926 )
garciadeblasf53612b2024-07-12 14:44:37 +02001927 outdata = {"id": _id, "nsName": nsName}
tierno65acb4d2018-04-06 16:42:40 +02001928 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001929 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001930 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001931 _id, _ = self.engine.new_item(
1932 rollback, engine_session, engine_topic, indata, kwargs
1933 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001934 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001935 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001936 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001937 nsilcmop_id, _ = self.engine.new_item(
1938 rollback, engine_session, "nsilcmops", indata, kwargs
1939 )
kuuse078f55e2019-05-16 19:24:21 +02001940 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001941 elif topic == "netslice_instances" and item:
1942 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001943 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001944 _id, _ = self.engine.new_item(
1945 rollback, engine_session, "nsilcmops", indata, kwargs
1946 )
1947 self._set_location_header(
1948 main_topic, version, "nsi_lcm_op_occs", _id
1949 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001950 outdata = {"id": _id}
1951 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001952 elif topic == "vnf_packages" and item == "action":
1953 indata["lcmOperationType"] = item
1954 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001955 _id, _ = self.engine.new_item(
1956 rollback, engine_session, "vnfpkgops", indata, kwargs
1957 )
1958 self._set_location_header(
1959 main_topic, version, "vnfpkg_op_occs", _id
1960 )
delacruzramo271d2002019-12-02 21:00:37 +01001961 outdata = {"id": _id}
1962 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301963 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001964 _id, _ = self.engine.new_item(
1965 rollback, engine_session, engine_topic, indata, kwargs
1966 )
preethika.p329b8182020-04-22 12:25:39 +05301967 self._set_location_header(main_topic, version, topic, _id)
1968 link = {}
1969 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001970 outdata = {
1971 "id": _id,
1972 "filter": indata["filter"],
1973 "callbackUri": indata["CallbackUri"],
1974 "_links": link,
1975 }
preethika.p329b8182020-04-22 12:25:39 +05301976 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001977 elif topic == "vnf_instances" and item:
1978 indata["lcmOperationType"] = item
1979 indata["vnfInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001980 _id, nsName, _ = self.engine.new_item(
garciadeblasf2af4a12023-01-24 16:56:54 +01001981 rollback, engine_session, "vnflcmops", indata, kwargs
1982 )
1983 self._set_location_header(
1984 main_topic, version, "vnf_lcm_op_occs", _id
1985 )
garciadeblasf53612b2024-07-12 14:44:37 +02001986 outdata = {"id": _id, "nsName": nsName}
almagiae47b9132022-05-17 14:12:22 +02001987 cherrypy.response.status = HTTPStatus.ACCEPTED.value
Gabriel Cuba84a60df2023-10-30 14:01:54 -05001988 elif topic == "ns_lcm_op_occs" and item == "cancel":
1989 indata["nsLcmOpOccId"] = _id
1990 self.engine.cancel_item(
1991 rollback, engine_session, "nslcmops", indata, None
1992 )
1993 self._set_location_header(main_topic, version, topic, _id)
1994 cherrypy.response.status = HTTPStatus.ACCEPTED.value
rshri2d386cb2024-07-05 14:35:51 +00001995 elif topic == "clusters" and _id == "register":
1996 # To register a cluster
1997 _id, _ = self.engine.add_item(
1998 rollback, engine_session, engine_topic, indata, kwargs
1999 )
2000 self._set_location_header(main_topic, version, topic, _id)
yshah93628192024-11-07 09:32:22 +00002001 outdata = {"_id": _id, "id": _id}
rshri2d386cb2024-07-05 14:35:51 +00002002 elif (
2003 topic
2004 in (
2005 "clusters",
2006 "infra_controller_profiles",
2007 "infra_config_profiles",
2008 "app_profiles",
2009 "resource_profiles",
2010 )
2011 and item is None
2012 ):
2013 # creates cluster, infra_controller_profiles, app_profiles, infra_config_profiles, and resource_profiles
2014 _id, _ = self.engine.new_item(
2015 rollback, engine_session, engine_topic, indata, kwargs
2016 )
2017 self._set_location_header(main_topic, version, topic, _id)
yshah93628192024-11-07 09:32:22 +00002018 outdata = {"_id": _id, "id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00002019 elif topic == "ksus" and item:
2020 if item == "clone":
2021 _id = self.engine.clone(
2022 rollback,
2023 engine_session,
2024 engine_topic,
2025 _id,
2026 indata,
2027 kwargs,
2028 cherrypy.request.headers,
2029 )
2030 self._set_location_header(main_topic, version, topic, _id)
yshah93628192024-11-07 09:32:22 +00002031 outdata = {"_id": _id, "id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00002032 if item == "move":
2033 op_id = self.engine.move_ksu(
2034 engine_session, engine_topic, _id, indata, kwargs
2035 )
2036 outdata = {"op_id": op_id}
2037 elif topic == "ksus" and _id == "delete":
2038 op_id = self.engine.delete_ksu(
2039 engine_session, engine_topic, _id, indata
2040 )
2041 outdata = {"op_id": op_id}
2042 elif topic == "ksus" and _id == "update":
2043 op_id = self.engine.edit_item(
2044 engine_session, engine_topic, _id, indata, kwargs
2045 )
2046 outdata = {"op_id": op_id}
yshah4ad91e12024-11-18 07:05:29 +00002047 elif topic == "clusters" and item in ("upgrade", "scale", "update"):
yshah53cc9eb2024-07-05 13:06:31 +00002048 op_id = self.engine.update_cluster(
2049 engine_session, engine_topic, _id, item, indata
2050 )
2051 outdata = {"op_id": op_id}
tiernof27c79b2018-03-12 17:08:42 +01002052 else:
garciadeblas4568a372021-03-24 09:19:48 +01002053 _id, op_id = self.engine.new_item(
2054 rollback,
2055 engine_session,
2056 engine_topic,
2057 indata,
2058 kwargs,
2059 cherrypy.request.headers,
2060 )
tiernob24258a2018-10-04 18:39:49 +02002061 self._set_location_header(main_topic, version, topic, _id)
yshah93628192024-11-07 09:32:22 +00002062 outdata = {"_id": _id, "id": _id}
tiernobdebce92019-07-01 15:36:49 +00002063 if op_id:
2064 outdata["op_id"] = op_id
2065 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02002066 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02002067
tiernoc94c3df2018-02-09 15:38:54 +01002068 elif method == "DELETE":
2069 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01002070 outdata = self.engine.del_item_list(
2071 engine_session, engine_topic, kwargs
2072 )
tierno09c073e2018-04-26 13:36:48 +02002073 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01002074 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00002075 # for NS NSI generate an operation
2076 op_id = None
tierno701018c2019-06-25 11:13:14 +00002077 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02002078 nslcmop_desc = {
2079 "lcmOperationType": "terminate",
2080 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002081 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02002082 }
garciadeblasf53612b2024-07-12 14:44:37 +02002083 op_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01002084 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
2085 )
tierno22577432020-04-08 15:16:57 +00002086 if op_id:
garciadeblasf53612b2024-07-12 14:44:37 +02002087 outdata = {"_id": op_id, "nsName": nsName}
garciadeblas4568a372021-03-24 09:19:48 +01002088 elif (
2089 topic == "netslice_instances_content"
2090 and not engine_session["force"]
2091 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02002092 nsilcmop_desc = {
2093 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02002094 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002095 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02002096 }
garciadeblas4568a372021-03-24 09:19:48 +01002097 op_id, _ = self.engine.new_item(
2098 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
2099 )
tierno22577432020-04-08 15:16:57 +00002100 if op_id:
2101 outdata = {"_id": op_id}
rshri2d386cb2024-07-05 14:35:51 +00002102 elif topic == "clusters" and item == "deregister":
2103 if not op_id:
2104 op_id = self.engine.remove(
2105 engine_session, engine_topic, _id
2106 )
2107 if op_id:
2108 outdata = {"_id": op_id}
2109 cherrypy.response.status = (
2110 HTTPStatus.ACCEPTED.value
2111 if op_id
2112 else HTTPStatus.NO_CONTENT.value
2113 )
yshah53cc9eb2024-07-05 13:06:31 +00002114 elif topic == "ksus":
2115 op_id = self.engine.delete_ksu(
2116 engine_session, engine_topic, _id, indata
2117 )
2118 outdata = {"op_id": op_id}
tierno22577432020-04-08 15:16:57 +00002119 # if there is not any deletion in process, delete
rshri2d386cb2024-07-05 14:35:51 +00002120 elif not op_id:
tierno22577432020-04-08 15:16:57 +00002121 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2122 if op_id:
rshri2d386cb2024-07-05 14:35:51 +00002123 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01002124 cherrypy.response.status = (
2125 HTTPStatus.ACCEPTED.value
2126 if op_id
2127 else HTTPStatus.NO_CONTENT.value
2128 )
tierno09c073e2018-04-26 13:36:48 +02002129
tierno7ae10112018-05-18 14:36:02 +02002130 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00002131 op_id = None
tierno701018c2019-06-25 11:13:14 +00002132 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01002133 raise NbiException(
2134 "Nothing to update. Provide payload and/or query string",
2135 HTTPStatus.BAD_REQUEST,
2136 )
2137 if (
kayal2001f71c2e82024-06-25 15:26:24 +05302138 item
2139 in (
2140 "nsd_content",
2141 "package_content",
2142 "nst_content",
2143 "template_content",
2144 )
garciadeblas4568a372021-03-24 09:19:48 +01002145 and method == "PUT"
2146 ):
2147 completed = self.engine.upload_content(
2148 engine_session,
2149 engine_topic,
2150 _id,
2151 indata,
2152 kwargs,
2153 cherrypy.request.headers,
2154 )
tiernof27c79b2018-03-12 17:08:42 +01002155 if not completed:
2156 cherrypy.response.headers["Transaction-Id"] = id
rshri2d386cb2024-07-05 14:35:51 +00002157 elif item in (
2158 "app_profiles",
2159 "resource_profiles",
2160 "infra_controller_profiles",
2161 "infra_config_profiles",
2162 ):
2163 op_id = self.engine.edit(
2164 engine_session, engine_topic, _id, item, indata, kwargs
2165 )
yshah53cc9eb2024-07-05 13:06:31 +00002166 elif topic == "oka_packages" and method == "PATCH":
2167 if kwargs:
2168 op_id = self.engine.edit_item(
2169 engine_session, engine_topic, _id, None, kwargs
2170 )
2171 if indata:
yshahffcac5f2024-08-19 12:49:07 +00002172 if isinstance(indata, dict):
yshah53cc9eb2024-07-05 13:06:31 +00002173 op_id = self.engine.edit_item(
2174 engine_session, engine_topic, _id, indata, kwargs
2175 )
2176 else:
2177 cherrypy.request.headers["method"] = cherrypy.request.method
2178 completed = self.engine.upload_content(
2179 engine_session,
2180 engine_topic,
2181 _id,
2182 indata,
2183 {},
2184 cherrypy.request.headers,
2185 )
2186 if not completed:
2187 cherrypy.response.headers["Transaction-Id"] = id
2188 elif topic == "oka_packages" and method == "PUT":
2189 if indata:
2190 cherrypy.request.headers["method"] = cherrypy.request.method
2191 completed = self.engine.upload_content(
2192 engine_session,
2193 engine_topic,
2194 _id,
2195 indata,
2196 {},
2197 cherrypy.request.headers,
2198 )
2199 if not completed:
2200 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01002201 else:
garciadeblas4568a372021-03-24 09:19:48 +01002202 op_id = self.engine.edit_item(
2203 engine_session, engine_topic, _id, indata, kwargs
2204 )
tiernobdebce92019-07-01 15:36:49 +00002205
2206 if op_id:
2207 cherrypy.response.status = HTTPStatus.ACCEPTED.value
2208 outdata = {"op_id": op_id}
2209 else:
2210 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
2211 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01002212 else:
garciadeblas4568a372021-03-24 09:19:48 +01002213 raise NbiException(
2214 "Method {} not allowed".format(method),
2215 HTTPStatus.METHOD_NOT_ALLOWED,
2216 )
tiernoa6bb45d2019-06-14 09:45:39 +00002217
2218 # if Role information changes, it is needed to reload the information of roles
2219 if topic == "roles" and method != "GET":
2220 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01002221
garciadeblas4568a372021-03-24 09:19:48 +01002222 if (
2223 topic == "projects"
2224 and method == "DELETE"
2225 or topic in ["users", "roles"]
2226 and method in ["PUT", "PATCH", "DELETE"]
2227 ):
delacruzramoad682a52019-12-10 16:26:34 +01002228 self.authenticator.remove_token_from_cache()
2229
garciadeblasf53612b2024-07-12 14:44:37 +02002230 cef_event(
2231 cef_logger,
2232 {
2233 "name": "User Operation",
2234 "sourceUserName": token_info.get("username"),
2235 },
2236 )
2237 if topic == "ns_instances_content" and url_id:
2238 nsName = (
2239 outdata.get("name") if method == "GET" else outdata.get("nsName")
2240 )
elumalai7802ff82023-04-24 20:38:32 +05302241 cef_event(
2242 cef_logger,
2243 {
garciadeblasf53612b2024-07-12 14:44:37 +02002244 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2245 log_mapping[method],
2246 topic,
2247 nsName,
2248 outdata.get("id"),
2249 token_info.get("project_name"),
2250 ),
2251 },
2252 )
2253 cherrypy.log("{}".format(cef_logger))
2254 elif topic == "ns_instances_content" and method == "POST":
2255 cef_event(
2256 cef_logger,
2257 {
2258 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2259 log_mapping[method],
2260 topic,
2261 outdata.get("nsName"),
2262 outdata.get("id"),
2263 token_info.get("project_name"),
2264 ),
2265 },
2266 )
2267 cherrypy.log("{}".format(cef_logger))
2268 elif topic in ("ns_instances", "vnf_instances") and item:
2269 cef_event(
2270 cef_logger,
2271 {
2272 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2273 log_mapping[method],
2274 topic,
2275 outdata.get("nsName"),
2276 url_id,
2277 token_info.get("project_name"),
2278 ),
2279 },
2280 )
2281 cherrypy.log("{}".format(cef_logger))
2282 elif item is not None:
2283 cef_event(
2284 cef_logger,
2285 {
elumalai7802ff82023-04-24 20:38:32 +05302286 "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format(
2287 item,
2288 topic,
2289 url_id,
2290 token_info.get("project_name"),
2291 ),
2292 },
2293 )
2294 cherrypy.log("{}".format(cef_logger))
2295 else:
2296 cef_event(
2297 cef_logger,
2298 {
elumalai7802ff82023-04-24 20:38:32 +05302299 "message": "{} {} {}, Project={} Outcome=Success".format(
2300 log_mapping[method],
2301 topic,
2302 url_id,
2303 token_info.get("project_name"),
2304 ),
2305 },
2306 )
2307 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002308 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02002309 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01002310 if isinstance(
2311 e,
2312 (
2313 NbiException,
2314 EngineException,
2315 DbException,
2316 FsException,
2317 MsgException,
2318 AuthException,
2319 ValidationError,
2320 AuthconnException,
2321 ),
2322 ):
tiernob24258a2018-10-04 18:39:49 +02002323 http_code_value = cherrypy.response.status = e.http_code.value
2324 http_code_name = e.http_code.name
2325 cherrypy.log("Exception {}".format(e))
2326 else:
garciadeblas4568a372021-03-24 09:19:48 +01002327 http_code_value = (
2328 cherrypy.response.status
2329 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01002330 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02002331 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02002332 if hasattr(outdata, "close"): # is an open file
2333 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02002334 error_text = str(e)
2335 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02002336 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02002337 try:
tiernocc103432018-10-19 14:10:35 +02002338 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01002339 self.engine.db.set_one(
2340 rollback_item["topic"],
2341 {"_id": rollback_item["_id"]},
2342 rollback_item["content"],
2343 fail_on_empty=False,
2344 )
preethika.p329b8182020-04-22 12:25:39 +05302345 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01002346 self.engine.db.del_list(
2347 rollback_item["topic"],
2348 rollback_item["filter"],
garciadeblas4568a372021-03-24 09:19:48 +01002349 )
tiernocc103432018-10-19 14:10:35 +02002350 else:
garciadeblas4568a372021-03-24 09:19:48 +01002351 self.engine.db.del_one(
2352 rollback_item["topic"],
2353 {"_id": rollback_item["_id"]},
2354 fail_on_empty=False,
2355 )
tierno3ace63c2018-05-03 17:51:43 +02002356 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01002357 rollback_error_text = "Rollback Exception {}: {}".format(
2358 rollback_item, e2
2359 )
tiernob24258a2018-10-04 18:39:49 +02002360 cherrypy.log(rollback_error_text)
2361 error_text += ". " + rollback_error_text
2362 # if isinstance(e, MsgException):
2363 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
2364 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01002365 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02002366 "code": http_code_name,
2367 "status": http_code_value,
2368 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01002369 }
elumalai7802ff82023-04-24 20:38:32 +05302370 if item is not None and token_info is not None:
2371 cef_event(
2372 cef_logger,
2373 {
2374 "name": "User Operation",
2375 "sourceUserName": token_info.get("username", None),
2376 "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format(
2377 item,
2378 topic,
2379 url_id,
2380 token_info.get("project_name", None),
2381 ),
2382 "severity": "2",
2383 },
2384 )
2385 cherrypy.log("{}".format(cef_logger))
2386 elif token_info is not None:
2387 cef_event(
2388 cef_logger,
2389 {
2390 "name": "User Operation",
2391 "sourceUserName": token_info.get("username", None),
2392 "message": "{} {} {}, Project={} Outcome=Failure".format(
2393 item,
2394 topic,
2395 url_id,
2396 token_info.get("project_name", None),
2397 ),
2398 "severity": "2",
2399 },
2400 )
2401 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002402 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01002403 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00002404 finally:
2405 if token_info:
2406 self._format_login(token_info)
2407 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
2408 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
2409 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01002410 cherrypy.request.login += ";{}={}".format(
2411 logging_id, outdata[logging_id][:36]
2412 )
tiernoc94c3df2018-02-09 15:38:54 +01002413
2414
tiernoc94c3df2018-02-09 15:38:54 +01002415def _start_service():
2416 """
2417 Callback function called when cherrypy.engine starts
2418 Override configuration with env variables
2419 Set database, storage, message configuration
2420 Init database with admin/admin user password
2421 """
tierno932499c2019-01-28 17:28:10 +00002422 global subscription_thread
elumalai7802ff82023-04-24 20:38:32 +05302423 global cef_logger
Rahulc72bc8e2023-12-05 11:54:38 +00002424 global current_backend
tiernoc94c3df2018-02-09 15:38:54 +01002425 cherrypy.log.error("Starting osm_nbi")
2426 # update general cherrypy configuration
2427 update_dict = {}
2428
garciadeblas4568a372021-03-24 09:19:48 +01002429 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01002430 for k, v in environ.items():
garciadeblas6d83f8f2023-06-19 22:34:49 +02002431 if k == "OSMNBI_USER_MANAGEMENT":
garciadeblasdf901002024-09-17 18:00:50 +02002432 feature_state = v.lower() == "true"
garciadeblas6d83f8f2023-06-19 22:34:49 +02002433 engine_config["authentication"]["user_management"] = feature_state
Adurtid68526d2023-11-14 11:14:53 +00002434 elif k == "OSMNBI_PWD_EXPIRE_DAYS":
2435 pwd_expire_days = int(v)
2436 engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days
2437 elif k == "OSMNBI_MAX_PWD_ATTEMPT":
2438 max_pwd_attempt = int(v)
2439 engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt
2440 elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS":
2441 account_expire_days = int(v)
2442 engine_config["authentication"]["account_expire_days"] = account_expire_days
jegan2c4bf4e2024-06-04 12:05:19 +00002443 elif k == "OSMNBI_SMTP_SERVER":
2444 engine_config["authentication"]["smtp_server"] = v
2445 engine_config["authentication"]["all"] = environ
2446 elif k == "OSMNBI_SMTP_PORT":
2447 port = int(v)
2448 engine_config["authentication"]["smtp_port"] = port
2449 elif k == "OSMNBI_SENDER_EMAIL":
2450 engine_config["authentication"]["sender_email"] = v
2451 elif k == "OSMNBI_EMAIL_PASSWORD":
2452 engine_config["authentication"]["sender_password"] = v
2453 elif k == "OSMNBI_OTP_RETRY_COUNT":
2454 otp_retry_count = int(v)
2455 engine_config["authentication"]["retry_count"] = otp_retry_count
2456 elif k == "OSMNBI_OTP_EXPIRY_TIME":
2457 otp_expiry_time = int(v)
2458 engine_config["authentication"]["otp_expiry_time"] = otp_expiry_time
tiernoc94c3df2018-02-09 15:38:54 +01002459 if not k.startswith("OSMNBI_"):
2460 continue
tiernoe1281182018-05-22 12:24:36 +02002461 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01002462 if not k2:
2463 continue
2464 try:
2465 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01002466 if k == "OSMNBI_STATIC_DIR":
2467 engine_config["/static"]["tools.staticdir.dir"] = v
2468 engine_config["/static"]["tools.staticdir.on"] = True
2469 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
2470 update_dict["server.socket_port"] = int(v)
2471 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
2472 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02002473 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01002474 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002475 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02002476 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002477 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01002478 engine_config[k1][k2] = int(v)
2479 else:
2480 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002481
tiernoc94c3df2018-02-09 15:38:54 +01002482 except ValueError as e:
2483 cherrypy.log.error("Ignoring environ '{}': " + str(e))
2484 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01002485 cherrypy.log(
2486 "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
2487 )
tiernoc94c3df2018-02-09 15:38:54 +01002488
2489 if update_dict:
2490 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02002491 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01002492
2493 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01002494 log_format_simple = (
2495 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
2496 )
2497 log_formatter_simple = logging.Formatter(
2498 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
2499 )
tiernoc94c3df2018-02-09 15:38:54 +01002500 logger_server = logging.getLogger("cherrypy.error")
2501 logger_access = logging.getLogger("cherrypy.access")
2502 logger_cherry = logging.getLogger("cherrypy")
2503 logger_nbi = logging.getLogger("nbi")
2504
tiernof5298be2018-05-16 14:43:57 +02002505 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01002506 file_handler = logging.handlers.RotatingFileHandler(
2507 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
2508 )
tiernoc94c3df2018-02-09 15:38:54 +01002509 file_handler.setFormatter(log_formatter_simple)
2510 logger_cherry.addHandler(file_handler)
2511 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02002512 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01002513 for format_, logger in {
2514 "nbi.server %(filename)s:%(lineno)s": logger_server,
2515 "nbi.access %(filename)s:%(lineno)s": logger_access,
2516 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
2517 }.items():
tiernob24258a2018-10-04 18:39:49 +02002518 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01002519 log_formatter_cherry = logging.Formatter(
2520 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
2521 )
tiernob24258a2018-10-04 18:39:49 +02002522 str_handler = logging.StreamHandler()
2523 str_handler.setFormatter(log_formatter_cherry)
2524 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01002525
tiernof5298be2018-05-16 14:43:57 +02002526 if engine_config["global"].get("log.level"):
2527 logger_cherry.setLevel(engine_config["global"]["log.level"])
2528 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01002529
2530 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01002531 for k1, logname in {
2532 "message": "nbi.msg",
2533 "database": "nbi.db",
2534 "storage": "nbi.fs",
2535 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01002536 engine_config[k1]["logger_name"] = logname
2537 logger_module = logging.getLogger(logname)
2538 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01002539 file_handler = logging.handlers.RotatingFileHandler(
2540 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
2541 )
tiernoc94c3df2018-02-09 15:38:54 +01002542 file_handler.setFormatter(log_formatter_simple)
2543 logger_module.addHandler(file_handler)
2544 if "loglevel" in engine_config[k1]:
2545 logger_module.setLevel(engine_config[k1]["loglevel"])
2546 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01002547 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
2548 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
2549 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
2550 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
2551 target_version=auth_database_version
2552 )
tiernobee508e2019-01-21 11:21:49 +00002553
elumalai7802ff82023-04-24 20:38:32 +05302554 cef_logger = cef_event_builder(engine_config["authentication"])
2555
tierno932499c2019-01-28 17:28:10 +00002556 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01002557 subscription_thread = SubscriptionThread(
2558 config=engine_config, engine=nbi_server.engine
2559 )
tierno932499c2019-01-28 17:28:10 +00002560 subscription_thread.start()
2561 # Do not capture except SubscriptionException
2562
tiernob2e48bd2020-02-04 15:47:18 +00002563 backend = engine_config["authentication"]["backend"]
Rahulc72bc8e2023-12-05 11:54:38 +00002564 current_backend = backend
garciadeblas4568a372021-03-24 09:19:48 +01002565 cherrypy.log.error(
2566 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
2567 nbi_version, nbi_version_date, backend
2568 )
2569 )
tiernoc94c3df2018-02-09 15:38:54 +01002570
2571
2572def _stop_service():
2573 """
2574 Callback function called when cherrypy.engine stops
2575 TODO: Ending database connections.
2576 """
tierno932499c2019-01-28 17:28:10 +00002577 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01002578 if subscription_thread:
2579 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00002580 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01002581 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01002582 cherrypy.log.error("Stopping osm_nbi")
2583
tierno2236d202018-05-16 19:05:16 +02002584
tiernof5298be2018-05-16 14:43:57 +02002585def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00002586 global nbi_server
tierno932499c2019-01-28 17:28:10 +00002587 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01002588 cherrypy.engine.subscribe("start", _start_service)
2589 cherrypy.engine.subscribe("stop", _stop_service)
2590 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02002591
2592
2593def usage():
garciadeblas4568a372021-03-24 09:19:48 +01002594 print(
2595 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02002596 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
2597 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01002598 """.format(
2599 sys.argv[0]
2600 )
2601 )
tierno2236d202018-05-16 19:05:16 +02002602 # --log-socket-host HOST: send logs to this host")
2603 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002604
2605
garciadeblas4568a372021-03-24 09:19:48 +01002606if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002607 try:
2608 # load parameters and configuration
2609 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2610 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2611 config_file = None
2612 for o, a in opts:
2613 if o in ("-h", "--help"):
2614 usage()
2615 sys.exit()
2616 elif o in ("-c", "--config"):
2617 config_file = a
2618 # elif o == "--log-socket-port":
2619 # log_socket_port = a
2620 # elif o == "--log-socket-host":
2621 # log_socket_host = a
2622 # elif o == "--log-file":
2623 # log_file = a
2624 else:
2625 assert False, "Unhandled option"
2626 if config_file:
2627 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002628 print(
2629 "configuration file '{}' that not exist".format(config_file),
2630 file=sys.stderr,
2631 )
tiernof5298be2018-05-16 14:43:57 +02002632 exit(1)
2633 else:
garciadeblas4568a372021-03-24 09:19:48 +01002634 for config_file in (
2635 __file__[: __file__.rfind(".")] + ".cfg",
2636 "./nbi.cfg",
2637 "/etc/osm/nbi.cfg",
2638 ):
tiernof5298be2018-05-16 14:43:57 +02002639 if path.isfile(config_file):
2640 break
2641 else:
garciadeblas4568a372021-03-24 09:19:48 +01002642 print(
2643 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2644 file=sys.stderr,
2645 )
tiernof5298be2018-05-16 14:43:57 +02002646 exit(1)
2647 nbi(config_file)
2648 except getopt.GetoptError as e:
2649 print(str(e), file=sys.stderr)
2650 # usage()
2651 exit(1)