blob: 1b03ea5911ac5ca8033a56c4d7ff2700a71ae0b5 [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>": {
688 "METHODS": ("GET", "DELETE"),
689 "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 },
714 "scale": {
715 "METHODS": ("POST",),
716 "ROLE_PERMISSION": "k8scluster:id:scale:",
717 },
718 "upgrade": {
719 "METHODS": ("POST",),
720 "ROLE_PERMISSION": "k8scluster:id:upgrade:",
721 },
rshri2d386cb2024-07-05 14:35:51 +0000722 },
723 "register": {
724 "METHODS": ("POST",),
725 "ROLE_PERMISSION": "k8scluster:register:",
726 },
727 },
728 "app_profiles": {
729 "METHODS": ("POST", "GET"),
730 "ROLE_PERMISSION": "k8scluster:app_profiles:",
731 "<ID>": {
732 "METHODS": ("GET", "PATCH", "DELETE"),
733 "ROLE_PERMISSION": "k8scluster:app_profiles:id:",
734 },
735 },
736 "infra_controller_profiles": {
737 "METHODS": ("POST", "GET"),
738 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:",
739 "<ID>": {
740 "METHODS": ("GET", "PATCH", "DELETE"),
741 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:id:",
742 },
743 },
744 "infra_config_profiles": {
745 "METHODS": ("POST", "GET"),
746 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:",
747 "<ID>": {
748 "METHODS": ("GET", "PATCH", "DELETE"),
749 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:id:",
750 },
751 },
752 "resource_profiles": {
753 "METHODS": ("POST", "GET"),
754 "ROLE_PERMISSION": "k8scluster:resource_profiles:",
755 "<ID>": {
756 "METHODS": ("GET", "PATCH", "DELETE"),
757 "ROLE_PERMISSION": "k8scluster:resource_profiles:id:",
758 },
759 },
760 }
761 },
yshah53cc9eb2024-07-05 13:06:31 +0000762 "ksu": {
763 "v1": {
764 "ksus": {
765 "METHODS": ("GET", "POST"),
766 "ROLE_PERMISSION": "ksu:",
767 "<ID>": {
768 "METHODS": ("GET", "PATCH", "DELETE"),
769 "ROLE_PERMISSION": "ksu:id:",
770 "clone": {
771 "METHODS": ("POST",),
772 "ROLE_PERMISSION": "ksu:id:clone:",
773 },
774 "move": {
775 "METHODS": ("POST",),
776 "ROLE_PERMISSION": "ksu:id:move:",
777 },
778 },
779 "update": {
780 "METHODS": ("POST",),
781 "ROLE_PERMISSION": "ksu:",
782 },
783 "delete": {
784 "METHODS": ("POST",),
785 "ROLE_PERMISSION": "ksu:",
786 },
787 },
788 }
789 },
790 "oka": {
791 "v1": {
792 "oka_packages": {
793 "METHODS": ("GET", "POST"),
794 "ROLE_PERMISSION": "oka_pkg:",
795 "<ID>": {
796 "METHODS": ("GET", "PATCH", "DELETE", "PUT"),
797 "ROLE_PERMISSION": "oka_pkg:id:",
798 },
799 }
800 }
801 },
tierno701018c2019-06-25 11:13:14 +0000802}
803
tiernoc94c3df2018-02-09 15:38:54 +0100804
805class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100806 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
807 Exception.__init__(self, message)
808 self.http_code = http_code
809
810
811class Server(object):
812 instance = 0
813 # to decode bytes to str
814 reader = getreader("utf-8")
815
816 def __init__(self):
817 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000818 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100819 self.engine = Engine(self.authenticator)
yshah53cc9eb2024-07-05 13:06:31 +0000820 self.logger = logging.getLogger("nbi.server")
tiernoc94c3df2018-02-09 15:38:54 +0100821
tiernoc94c3df2018-02-09 15:38:54 +0100822 def _format_in(self, kwargs):
garciadeblasf2af4a12023-01-24 16:56:54 +0100823 error_text = "" # error_text must be initialized outside try
tiernoc94c3df2018-02-09 15:38:54 +0100824 try:
825 indata = None
826 if cherrypy.request.body.length:
827 error_text = "Invalid input format "
828
829 if "Content-Type" in cherrypy.request.headers:
830 if "application/json" in cherrypy.request.headers["Content-Type"]:
831 error_text = "Invalid json format "
832 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100833 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100834 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
835 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100836 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100837 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100838 elif (
839 "application/binary" in cherrypy.request.headers["Content-Type"]
840 or "application/gzip"
841 in cherrypy.request.headers["Content-Type"]
842 or "application/zip" in cherrypy.request.headers["Content-Type"]
843 or "text/plain" in cherrypy.request.headers["Content-Type"]
844 ):
tiernof27c79b2018-03-12 17:08:42 +0100845 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100846 elif (
847 "multipart/form-data"
848 in cherrypy.request.headers["Content-Type"]
849 ):
yshah53cc9eb2024-07-05 13:06:31 +0000850 if (
851 "descriptor_file" in kwargs
852 or "package" in kwargs
853 and "name" in kwargs
854 ):
855 filecontent = ""
856 if "descriptor_file" in kwargs:
857 filecontent = kwargs.pop("descriptor_file")
858 if "package" in kwargs:
859 filecontent = kwargs.pop("package")
tiernoc94c3df2018-02-09 15:38:54 +0100860 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100861 raise NbiException(
862 "empty file or content", HTTPStatus.BAD_REQUEST
863 )
yshah53cc9eb2024-07-05 13:06:31 +0000864 indata = filecontent
865 if filecontent.content_type.value:
866 cherrypy.request.headers[
867 "Content-Type"
868 ] = filecontent.content_type.value
869 elif "package" in kwargs:
870 filecontent = kwargs.pop("package")
871 if not filecontent.file:
872 raise NbiException(
873 "empty file or content", HTTPStatus.BAD_REQUEST
874 )
875 indata = filecontent
tiernoc94c3df2018-02-09 15:38:54 +0100876 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100877 cherrypy.request.headers[
878 "Content-Type"
879 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100880 else:
881 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
882 # "Only 'Content-Type' of type 'application/json' or
883 # 'application/yaml' for input format are available")
884 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100885 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100886 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100887 else:
888 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100889 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100890 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100891 if not indata:
892 indata = {}
tiernoc94c3df2018-02-09 15:38:54 +0100893 format_yaml = False
894 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
895 format_yaml = True
896
897 for k, v in kwargs.items():
898 if isinstance(v, str):
899 if v == "":
900 kwargs[k] = None
901 elif format_yaml:
902 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100903 kwargs[k] = yaml.safe_load(v)
tiernoe1281182018-05-22 12:24:36 +0200904 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100905 pass
garciadeblas4568a372021-03-24 09:19:48 +0100906 elif (
907 k.endswith(".gt")
908 or k.endswith(".lt")
909 or k.endswith(".gte")
910 or k.endswith(".lte")
911 ):
tiernoc94c3df2018-02-09 15:38:54 +0100912 try:
913 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200914 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100915 try:
916 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200917 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100918 pass
919 elif v.find(",") > 0:
920 kwargs[k] = v.split(",")
921 elif isinstance(v, (list, tuple)):
922 for index in range(0, len(v)):
923 if v[index] == "":
924 v[index] = None
925 elif format_yaml:
926 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100927 v[index] = yaml.safe_load(v[index])
tiernoe1281182018-05-22 12:24:36 +0200928 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100929 pass
930
tiernof27c79b2018-03-12 17:08:42 +0100931 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100932 except (ValueError, yaml.YAMLError) as exc:
933 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
934 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100935 raise NbiException(
936 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
937 )
tiernob92094f2018-05-11 13:44:22 +0200938 except Exception as exc:
939 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100940
941 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000942 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100943 """
944 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100945 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000946 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000947 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100948 :return: None
949 """
tierno0f98af52018-03-19 10:28:22 +0100950 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100951 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100952 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100953 return html.format(
954 data, cherrypy.request, cherrypy.response, token_info
955 )
tierno09c073e2018-04-26 13:36:48 +0200956 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100957 return
958 elif hasattr(data, "read"): # file object
959 if _format:
960 cherrypy.response.headers["Content-Type"] = _format
961 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100962 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100963 else:
garciadeblas4568a372021-03-24 09:19:48 +0100964 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100965 # TODO check that cherrypy close file. If not implement pending things to close per thread next
966 return data
tierno0f98af52018-03-19 10:28:22 +0100967 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000968 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100969 return html.format(
970 data, cherrypy.request, cherrypy.response, token_info
971 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000972 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100973 pass
garciadeblas4568a372021-03-24 09:19:48 +0100974 elif "application/json" in accept or (
975 cherrypy.response.status and cherrypy.response.status >= 300
976 ):
977 cherrypy.response.headers[
978 "Content-Type"
979 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000980 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100981 return a.encode("utf8")
982 cherrypy.response.headers["Content-Type"] = "application/yaml"
983 return yaml.safe_dump(
984 data,
985 explicit_start=True,
986 indent=4,
987 default_flow_style=False,
988 tags=False,
989 encoding="utf-8",
990 allow_unicode=True,
991 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +0100992
993 @cherrypy.expose
994 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +0000995 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100996 try:
997 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +0000998 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +0100999 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +01001000 else:
garciadeblas4568a372021-03-24 09:19:48 +01001001 raise cherrypy.HTTPError(
1002 HTTPStatus.METHOD_NOT_ALLOWED.value,
1003 "Method {} not allowed for tokens".format(cherrypy.request.method),
1004 )
tiernoc94c3df2018-02-09 15:38:54 +01001005
tierno701018c2019-06-25 11:13:14 +00001006 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001007
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001008 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +00001009 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +01001010 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +00001011 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001012
1013 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +02001014 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +02001015 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +02001016 try:
1017 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001018 raise NbiException(
1019 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
1020 )
tierno55945e72018-04-06 16:40:27 +02001021 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001022 raise NbiException(
1023 "Invalid URL or query string for version",
1024 HTTPStatus.METHOD_NOT_ALLOWED,
1025 )
tierno9c630112019-08-29 14:21:41 +00001026 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +00001027 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
1028 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +02001029 except NbiException as e:
1030 cherrypy.response.status = e.http_code.value
1031 problem_details = {
1032 "code": e.http_code.name,
1033 "status": e.http_code.value,
1034 "detail": str(e),
1035 }
1036 return self._format_out(problem_details, None)
1037
tierno12eac3c2020-03-19 23:22:08 +00001038 def domain(self):
1039 try:
1040 domains = {
garciadeblas4568a372021-03-24 09:19:48 +01001041 "user_domain_name": cherrypy.tree.apps["/osm"]
1042 .config["authentication"]
1043 .get("user_domain_name"),
1044 "project_domain_name": cherrypy.tree.apps["/osm"]
1045 .config["authentication"]
1046 .get("project_domain_name"),
1047 }
tierno12eac3c2020-03-19 23:22:08 +00001048 return self._format_out(domains)
1049 except NbiException as e:
1050 cherrypy.response.status = e.http_code.value
1051 problem_details = {
1052 "code": e.http_code.name,
1053 "status": e.http_code.value,
1054 "detail": str(e),
1055 }
1056 return self._format_out(problem_details, None)
1057
tiernoa5035702019-07-29 08:54:42 +00001058 @staticmethod
1059 def _format_login(token_info):
1060 """
1061 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
1062 log this information
1063 :param token_info: Dictionary with token content
1064 :return: None
1065 """
1066 cherrypy.request.login = token_info.get("username", "-")
1067 if token_info.get("project_name"):
1068 cherrypy.request.login += "/" + token_info["project_name"]
1069 if token_info.get("id"):
1070 cherrypy.request.login += ";session=" + token_info["id"][0:12]
1071
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001072 # NS Fault Management
1073 @cherrypy.expose
garciadeblasf2af4a12023-01-24 16:56:54 +01001074 def nsfm(
1075 self,
1076 version=None,
1077 topic=None,
1078 uuid=None,
1079 project_name=None,
1080 ns_id=None,
1081 *args,
1082 **kwargs
1083 ):
1084 if topic == "alarms":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001085 try:
1086 method = cherrypy.request.method
garciadeblasf2af4a12023-01-24 16:56:54 +01001087 role_permission = self._check_valid_url_method(
1088 method, "nsfm", version, topic, None, None, *args
1089 )
1090 query_string_operations = self._extract_query_string_operations(
1091 kwargs, method
1092 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001093
garciadeblasf2af4a12023-01-24 16:56:54 +01001094 self.authenticator.authorize(
1095 role_permission, query_string_operations, None
1096 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001097
1098 # to handle get request
garciadeblasf2af4a12023-01-24 16:56:54 +01001099 if cherrypy.request.method == "GET":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001100 # if request is on basis of uuid
garciadeblasf2af4a12023-01-24 16:56:54 +01001101 if uuid and uuid != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001102 try:
1103 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
garciadeblasf2af4a12023-01-24 16:56:54 +01001104 alarm_action = self.engine.db.get_one(
1105 "alarms_action", {"uuid": uuid}
1106 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001107 alarm.update(alarm_action)
garciadeblasf2af4a12023-01-24 16:56:54 +01001108 vnf = self.engine.db.get_one(
1109 "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}
1110 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001111 alarm["vnf-id"] = vnf["_id"]
1112 return self._format_out(str(alarm))
1113 except Exception:
1114 return self._format_out("Please provide valid alarm uuid")
garciadeblasf2af4a12023-01-24 16:56:54 +01001115 elif ns_id and ns_id != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001116 # if request is on basis of ns_id
1117 try:
garciadeblasf2af4a12023-01-24 16:56:54 +01001118 alarms = self.engine.db.get_list(
1119 "alarms", {"tags.ns_id": ns_id}
1120 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001121 for alarm in alarms:
garciadeblasf2af4a12023-01-24 16:56:54 +01001122 alarm_action = self.engine.db.get_one(
1123 "alarms_action", {"uuid": alarm["uuid"]}
1124 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001125 alarm.update(alarm_action)
1126 return self._format_out(str(alarms))
1127 except Exception:
1128 return self._format_out("Please provide valid ns id")
1129 else:
1130 # to return only alarm which are related to given project
garciadeblasf2af4a12023-01-24 16:56:54 +01001131 project = self.engine.db.get_one(
1132 "projects", {"name": project_name}
1133 )
1134 project_id = project.get("_id")
1135 ns_list = self.engine.db.get_list(
1136 "nsrs", {"_admin.projects_read": project_id}
1137 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001138 ns_ids = []
1139 for ns in ns_list:
1140 ns_ids.append(ns.get("_id"))
1141 alarms = self.engine.db.get_list("alarms")
garciadeblasf2af4a12023-01-24 16:56:54 +01001142 alarm_list = [
1143 alarm
1144 for alarm in alarms
1145 if alarm["tags"]["ns_id"] in ns_ids
1146 ]
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001147 for alrm in alarm_list:
garciadeblasf2af4a12023-01-24 16:56:54 +01001148 action = self.engine.db.get_one(
1149 "alarms_action", {"uuid": alrm.get("uuid")}
1150 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001151 alrm.update(action)
1152 return self._format_out(str(alarm_list))
1153 # to handle patch request for alarm update
garciadeblasf2af4a12023-01-24 16:56:54 +01001154 elif cherrypy.request.method == "PATCH":
garciadeblas4cd875d2023-02-14 19:05:34 +01001155 data = yaml.safe_load(cherrypy.request.body)
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001156 try:
1157 # check if uuid is valid
1158 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
1159 except Exception:
1160 return self._format_out("Please provide valid alarm uuid.")
1161 if data.get("is_enable") is not None:
1162 if data.get("is_enable"):
garciadeblasf2af4a12023-01-24 16:56:54 +01001163 alarm_status = "ok"
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001164 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001165 alarm_status = "disabled"
1166 self.engine.db.set_one(
1167 "alarms",
1168 {"uuid": data.get("uuid")},
1169 {"alarm_status": alarm_status},
1170 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001171 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001172 self.engine.db.set_one(
1173 "alarms",
1174 {"uuid": data.get("uuid")},
1175 {"threshold": data.get("threshold")},
1176 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001177 return self._format_out("Alarm updated")
1178 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01001179 if isinstance(
1180 e,
1181 (
1182 NbiException,
1183 EngineException,
1184 DbException,
1185 FsException,
1186 MsgException,
1187 AuthException,
1188 ValidationError,
1189 AuthconnException,
1190 ),
1191 ):
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001192 http_code_value = cherrypy.response.status = e.http_code.value
1193 http_code_name = e.http_code.name
1194 cherrypy.log("Exception {}".format(e))
1195 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001196 http_code_value = (
1197 cherrypy.response.status
1198 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001199 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
1200 http_code_name = HTTPStatus.BAD_REQUEST.name
1201 problem_details = {
1202 "code": http_code_name,
1203 "status": http_code_value,
1204 "detail": str(e),
1205 }
1206 return self._format_out(problem_details)
1207
tierno55945e72018-04-06 16:40:27 +02001208 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +01001209 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +00001210 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001211 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +01001212 indata = self._format_in(kwargs)
1213 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001214 raise NbiException(
1215 "Expected application/yaml or application/json Content-Type",
1216 HTTPStatus.BAD_REQUEST,
1217 )
tiernoa5035702019-07-29 08:54:42 +00001218
1219 if method == "GET":
1220 token_info = self.authenticator.authorize()
1221 # for logging
1222 self._format_login(token_info)
1223 if token_id:
1224 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +01001225 else:
tiernoa5035702019-07-29 08:54:42 +00001226 outdata = self.authenticator.get_token_list(token_info)
1227 elif method == "POST":
1228 try:
1229 token_info = self.authenticator.authorize()
1230 except Exception:
1231 token_info = None
1232 if kwargs:
1233 indata.update(kwargs)
1234 # This is needed to log the user when authentication fails
1235 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +01001236 outdata = token_info = self.authenticator.new_token(
1237 token_info, indata, cherrypy.request.remote
1238 )
garciadeblasf2af4a12023-01-24 16:56:54 +01001239 cherrypy.session["Authorization"] = outdata["_id"] # pylint: disable=E1101
tiernoa5035702019-07-29 08:54:42 +00001240 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1241 # for logging
1242 self._format_login(token_info)
selvi.ja9a1fc82022-04-04 06:54:30 +00001243 # password expiry check
1244 if self.authenticator.check_password_expiry(outdata):
garciadeblasf2af4a12023-01-24 16:56:54 +01001245 outdata = {
1246 "id": outdata["id"],
1247 "message": "change_password",
1248 "user_id": outdata["user_id"],
1249 }
tiernoa5035702019-07-29 08:54:42 +00001250 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1251 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
elumalai7802ff82023-04-24 20:38:32 +05301252 cef_event(
1253 cef_logger,
1254 {
1255 "name": "User Login",
1256 "sourceUserName": token_info.get("username"),
1257 "message": "User Logged In, Project={} Outcome=Success".format(
1258 token_info.get("project_name")
1259 ),
1260 },
1261 )
1262 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001263 elif method == "DELETE":
1264 if not token_id and "id" in kwargs:
1265 token_id = kwargs["id"]
1266 elif not token_id:
1267 token_info = self.authenticator.authorize()
1268 # for logging
1269 self._format_login(token_info)
1270 token_id = token_info["_id"]
Rahulc72bc8e2023-12-05 11:54:38 +00001271 if current_backend != "keystone":
1272 token_details = self.engine.db.get_one("tokens", {"_id": token_id})
1273 current_user = token_details.get("username")
1274 current_project = token_details.get("project_name")
1275 else:
1276 current_user = "keystone backend"
1277 current_project = "keystone backend"
tiernoa5035702019-07-29 08:54:42 +00001278 outdata = self.authenticator.del_token(token_id)
1279 token_info = None
garciadeblasf2af4a12023-01-24 16:56:54 +01001280 cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101
elumalai7802ff82023-04-24 20:38:32 +05301281 cef_event(
1282 cef_logger,
1283 {
1284 "name": "User Logout",
1285 "sourceUserName": current_user,
1286 "message": "User Logged Out, Project={} Outcome=Success".format(
1287 current_project
1288 ),
1289 },
1290 )
1291 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001292 # cherrypy.response.cookie["Authorization"] = token_id
1293 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1294 else:
garciadeblas4568a372021-03-24 09:19:48 +01001295 raise NbiException(
1296 "Method {} not allowed for token".format(method),
1297 HTTPStatus.METHOD_NOT_ALLOWED,
1298 )
tiernoa5035702019-07-29 08:54:42 +00001299 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001300
1301 @cherrypy.expose
1302 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001303 if not cherrypy.config.get("server.enable_test") or (
1304 isinstance(cherrypy.config["server.enable_test"], str)
1305 and cherrypy.config["server.enable_test"].lower() == "false"
1306 ):
tierno4836bac2020-01-15 14:41:48 +00001307 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1308 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001309 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001310 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001311 return (
1312 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1313 "sleep/<time>\nmessage/topic\n</pre></html>"
1314 )
tiernof27c79b2018-03-12 17:08:42 +01001315
1316 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001317 try:
1318 # self.engine.load_dbase(cherrypy.request.app.config)
garciadeblasf2af4a12023-01-24 16:56:54 +01001319 pid = self.authenticator.create_admin_project()
1320 self.authenticator.create_admin_user(pid)
tiernoc94c3df2018-02-09 15:38:54 +01001321 return "Done. User 'admin', password 'admin' created"
1322 except Exception:
1323 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1324 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001325 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001326 return cherrypy.lib.static.serve_file(
1327 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1328 "text/plain",
1329 "attachment",
1330 )
tiernof27c79b2018-03-12 17:08:42 +01001331 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001332 f_path = (
1333 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1334 )
tiernof27c79b2018-03-12 17:08:42 +01001335 f = open(f_path, "r")
1336 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001337 return f
tierno55945e72018-04-06 16:40:27 +02001338
tiernof27c79b2018-03-12 17:08:42 +01001339 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001340 deleted_info = self.engine.db.del_list(args[1], kwargs)
1341 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1342 elif len(args) and args[0] == "fs-clear":
1343 if len(args) >= 2:
1344 folders = (args[1],)
1345 else:
1346 folders = self.engine.fs.dir_ls(".")
1347 for folder in folders:
1348 self.engine.fs.file_delete(folder)
1349 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001350 elif args and args[0] == "login":
1351 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001352 cherrypy.response.headers[
1353 "WWW-Authenticate"
1354 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001355 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1356 elif args and args[0] == "login2":
1357 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001358 cherrypy.response.headers[
1359 "WWW-Authenticate"
1360 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001361 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1362 elif args and args[0] == "sleep":
1363 sleep_time = 5
1364 try:
1365 sleep_time = int(args[1])
1366 except Exception:
1367 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1368 return self._format_out("Database already initialized")
1369 thread_info = cherrypy.thread_data
1370 print(thread_info)
1371 time.sleep(sleep_time)
1372 # thread_info
1373 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001374 main_topic = args[1]
1375 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001376 try:
garciadeblas4568a372021-03-24 09:19:48 +01001377 if cherrypy.request.method == "POST":
garciadeblas4cd875d2023-02-14 19:05:34 +01001378 to_send = yaml.safe_load(cherrypy.request.body)
tierno55945e72018-04-06 16:40:27 +02001379 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001380 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001381 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001382 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001383 for k, v in kwargs.items():
garciadeblas4cd875d2023-02-14 19:05:34 +01001384 v_dict = yaml.safe_load(v)
tiernof1509b22020-05-12 14:32:37 +00001385 self.engine.msg.write(main_topic, k, v_dict)
1386 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001387 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001388 return_text += "Error: " + str(e)
1389 return_text += "</pre></html>\n"
1390 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001391
1392 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001393 "<html><pre>\nheaders:\n args: {}\n".format(args)
1394 + " kwargs: {}\n".format(kwargs)
1395 + " headers: {}\n".format(cherrypy.request.headers)
1396 + " path_info: {}\n".format(cherrypy.request.path_info)
1397 + " query_string: {}\n".format(cherrypy.request.query_string)
garciadeblasf2af4a12023-01-24 16:56:54 +01001398 + " session: {}\n".format(cherrypy.session) # pylint: disable=E1101
garciadeblas4568a372021-03-24 09:19:48 +01001399 + " cookie: {}\n".format(cherrypy.request.cookie)
1400 + " method: {}\n".format(cherrypy.request.method)
garciadeblasf2af4a12023-01-24 16:56:54 +01001401 + " session: {}\n".format(
1402 cherrypy.session.get("fieldname") # pylint: disable=E1101
1403 )
garciadeblas4568a372021-03-24 09:19:48 +01001404 + " body:\n"
1405 )
tiernoc94c3df2018-02-09 15:38:54 +01001406 return_text += " length: {}\n".format(cherrypy.request.body.length)
1407 if cherrypy.request.body.length:
1408 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001409 str(
1410 cherrypy.request.body.read(
1411 int(cherrypy.request.headers.get("Content-Length", 0))
1412 )
1413 )
1414 )
tiernoc94c3df2018-02-09 15:38:54 +01001415 if thread_info:
1416 return_text += "thread: {}\n".format(thread_info)
1417 return_text += "</pre></html>"
1418 return return_text
1419
tierno701018c2019-06-25 11:13:14 +00001420 @staticmethod
1421 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001422 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001423 raise NbiException(
1424 "URL must contain at least 'main_topic/version/topic'",
1425 HTTPStatus.METHOD_NOT_ALLOWED,
1426 )
tiernof27c79b2018-03-12 17:08:42 +01001427
tierno701018c2019-06-25 11:13:14 +00001428 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001429 for arg in args:
1430 if arg is None:
1431 break
1432 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001433 raise NbiException(
1434 "URL contains unexpected extra items '{}'".format(arg),
1435 HTTPStatus.METHOD_NOT_ALLOWED,
1436 )
tiernof27c79b2018-03-12 17:08:42 +01001437
1438 if arg in reference:
1439 reference = reference[arg]
1440 elif "<ID>" in reference:
1441 reference = reference["<ID>"]
1442 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001443 # if there is content
1444 if reference["*"]:
1445 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001446 break
1447 else:
garciadeblas4568a372021-03-24 09:19:48 +01001448 raise NbiException(
1449 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1450 )
tiernof27c79b2018-03-12 17:08:42 +01001451 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001452 raise NbiException(
1453 "Method {} not supported yet for this URL".format(method),
1454 HTTPStatus.NOT_IMPLEMENTED,
1455 )
tierno2236d202018-05-16 19:05:16 +02001456 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001457 raise NbiException(
1458 "Method {} not supported for this URL".format(method),
1459 HTTPStatus.METHOD_NOT_ALLOWED,
1460 )
tierno701018c2019-06-25 11:13:14 +00001461 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001462
1463 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001464 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001465 """
1466 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001467 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001468 :param version:
tiernob24258a2018-10-04 18:39:49 +02001469 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001470 :param id:
1471 :return: None
1472 """
1473 # 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 +01001474 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1475 main_topic, version, topic, id
1476 )
tiernof27c79b2018-03-12 17:08:42 +01001477 return
1478
tierno65ca36d2019-02-12 19:27:52 +01001479 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001480 def _extract_query_string_operations(kwargs, method):
1481 """
1482
1483 :param kwargs:
1484 :return:
1485 """
1486 query_string_operations = []
1487 if kwargs:
1488 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1489 if qs in kwargs and kwargs[qs].lower() != "false":
1490 query_string_operations.append(qs.lower() + ":" + method.lower())
1491 return query_string_operations
1492
1493 @staticmethod
1494 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001495 """
1496 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1497 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001498 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001499 :param kwargs: query string input.
1500 :param method: http method: GET, POSST, PUT, ...
1501 :param _id:
1502 :return: admin_query dictionary with keys:
1503 public: True, False or None
1504 force: True or False
1505 project_id: tuple with projects used for accessing an element
1506 set_project: tuple with projects that a created element will belong to
1507 method: show, list, delete, write
1508 """
garciadeblas4568a372021-03-24 09:19:48 +01001509 admin_query = {
1510 "force": False,
1511 "project_id": (token_info["project_id"],),
1512 "username": token_info["username"],
Adurtif0265c92024-05-07 06:04:37 +00001513 "user_id": token_info["user_id"],
garciadeblas4568a372021-03-24 09:19:48 +01001514 "admin": token_info["admin"],
371771188c192024-11-01 08:55:59 +00001515 "admin_show": token_info["admin_show"],
garciadeblas4568a372021-03-24 09:19:48 +01001516 "public": None,
1517 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1518 }
tierno65ca36d2019-02-12 19:27:52 +01001519 if kwargs:
1520 # FORCE
1521 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001522 if (
1523 kwargs["FORCE"].lower() != "false"
1524 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001525 admin_query["force"] = True
1526 del kwargs["FORCE"]
1527 # PUBLIC
1528 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001529 if (
1530 kwargs["PUBLIC"].lower() != "false"
1531 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001532 admin_query["public"] = True
1533 else:
1534 admin_query["public"] = False
1535 del kwargs["PUBLIC"]
1536 # ADMIN
1537 if "ADMIN" in kwargs:
1538 behave_as = kwargs.pop("ADMIN")
1539 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001540 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001541 raise NbiException(
1542 "Only admin projects can use 'ADMIN' query string",
1543 HTTPStatus.UNAUTHORIZED,
1544 )
1545 if (
1546 not behave_as or behave_as.lower() == "true"
1547 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001548 admin_query["project_id"] = ()
1549 elif isinstance(behave_as, (list, tuple)):
1550 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001551 else: # isinstance(behave_as, str)
1552 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001553 if "SET_PROJECT" in kwargs:
1554 set_project = kwargs.pop("SET_PROJECT")
1555 if not set_project:
1556 admin_query["set_project"] = list(admin_query["project_id"])
1557 else:
1558 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001559 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001560 if admin_query["project_id"]:
1561 for p in set_project:
1562 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001563 raise NbiException(
1564 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1565 "'ADMIN='{p}'".format(p=p),
1566 HTTPStatus.UNAUTHORIZED,
1567 )
tierno65ca36d2019-02-12 19:27:52 +01001568 admin_query["set_project"] = set_project
1569
1570 # PROJECT_READ
1571 # if "PROJECT_READ" in kwargs:
1572 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001573 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001574 if method == "GET":
1575 if _id:
1576 admin_query["method"] = "show"
1577 else:
1578 admin_query["method"] = "list"
1579 elif method == "DELETE":
1580 admin_query["method"] = "delete"
1581 else:
1582 admin_query["method"] = "write"
1583 return admin_query
1584
tiernoc94c3df2018-02-09 15:38:54 +01001585 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001586 def default(
1587 self,
1588 main_topic=None,
1589 version=None,
1590 topic=None,
1591 _id=None,
1592 item=None,
1593 *args,
1594 **kwargs
1595 ):
tierno701018c2019-06-25 11:13:14 +00001596 token_info = None
selvi.j9919bbc2023-04-26 12:22:13 +00001597 outdata = {}
tiernof27c79b2018-03-12 17:08:42 +01001598 _format = None
tierno0f98af52018-03-19 10:28:22 +01001599 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001600 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001601 rollback = []
tierno701018c2019-06-25 11:13:14 +00001602 engine_session = None
elumalai7802ff82023-04-24 20:38:32 +05301603 url_id = ""
1604 log_mapping = {
1605 "POST": "Creating",
1606 "GET": "Fetching",
1607 "DELETE": "Deleting",
1608 "PUT": "Updating",
1609 "PATCH": "Updating",
1610 }
tiernoc94c3df2018-02-09 15:38:54 +01001611 try:
tiernob24258a2018-10-04 18:39:49 +02001612 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001613 raise NbiException(
1614 "URL must contain at least 'main_topic/version/topic'",
1615 HTTPStatus.METHOD_NOT_ALLOWED,
1616 )
1617 if main_topic not in (
1618 "admin",
1619 "vnfpkgm",
1620 "nsd",
1621 "nslcm",
1622 "pdu",
1623 "nst",
1624 "nsilcm",
1625 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001626 "vnflcm",
rshri2d386cb2024-07-05 14:35:51 +00001627 "k8scluster",
yshah53cc9eb2024-07-05 13:06:31 +00001628 "ksu",
1629 "oka",
garciadeblas4568a372021-03-24 09:19:48 +01001630 ):
1631 raise NbiException(
1632 "URL main_topic '{}' not supported".format(main_topic),
1633 HTTPStatus.METHOD_NOT_ALLOWED,
1634 )
1635 if version != "v1":
1636 raise NbiException(
1637 "URL version '{}' not supported".format(version),
1638 HTTPStatus.METHOD_NOT_ALLOWED,
1639 )
elumalai7802ff82023-04-24 20:38:32 +05301640 if _id is not None:
1641 url_id = _id
tiernoc94c3df2018-02-09 15:38:54 +01001642
garciadeblas4568a372021-03-24 09:19:48 +01001643 if (
1644 kwargs
1645 and "METHOD" in kwargs
1646 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1647 ):
tiernof27c79b2018-03-12 17:08:42 +01001648 method = kwargs.pop("METHOD")
1649 else:
1650 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001651
garciadeblas4568a372021-03-24 09:19:48 +01001652 role_permission = self._check_valid_url_method(
1653 method, main_topic, version, topic, _id, item, *args
1654 )
1655 query_string_operations = self._extract_query_string_operations(
1656 kwargs, method
1657 )
tiernob24258a2018-10-04 18:39:49 +02001658 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001659 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001660 token_info = self.authenticator.authorize(
1661 role_permission, query_string_operations, _id
1662 )
tierno12eac3c2020-03-19 23:22:08 +00001663 if main_topic == "admin" and topic == "domains":
1664 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001665 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001666 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001667 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301668
vijay.r35ef2f72019-04-30 17:55:49 +05301669 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001670 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001671
tiernob24258a2018-10-04 18:39:49 +02001672 if main_topic == "nsd":
1673 engine_topic = "nsds"
kayal2001f71c2e82024-06-25 15:26:24 +05301674 if topic == "ns_config_template":
1675 engine_topic = "nsconfigtemps"
tiernob24258a2018-10-04 18:39:49 +02001676 elif main_topic == "vnfpkgm":
1677 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001678 if topic == "vnfpkg_op_occs":
1679 engine_topic = "vnfpkgops"
1680 if topic == "vnf_packages" and item == "action":
1681 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001682 elif main_topic == "nslcm":
1683 engine_topic = "nsrs"
1684 if topic == "ns_lcm_op_occs":
1685 engine_topic = "nslcmops"
1686 if topic == "vnfrs" or topic == "vnf_instances":
1687 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001688 elif main_topic == "vnflcm":
1689 if topic == "vnf_lcm_op_occs":
1690 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001691 elif main_topic == "nst":
1692 engine_topic = "nsts"
1693 elif main_topic == "nsilcm":
1694 engine_topic = "nsis"
1695 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001696 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001697 elif main_topic == "pdu":
1698 engine_topic = "pdus"
rshri2d386cb2024-07-05 14:35:51 +00001699 elif main_topic == "k8scluster":
1700 engine_topic = "k8s"
1701 if topic == "clusters" and _id == "register" or item == "deregister":
1702 engine_topic = "k8sops"
1703 elif topic == "infra_controller_profiles":
1704 engine_topic = "infras_cont"
1705 elif topic == "infra_config_profiles":
1706 engine_topic = "infras_conf"
1707 elif topic == "resource_profiles":
1708 engine_topic = "resources"
1709 elif topic == "app_profiles":
1710 engine_topic = "apps"
yshah53cc9eb2024-07-05 13:06:31 +00001711 elif main_topic == "k8scluster" and item in (
1712 "upgrade",
1713 "get_creds",
1714 "scale",
1715 ):
1716 engine_topic = "k8s"
1717 elif main_topic == "ksu" and engine_topic in ("ksus", "clone", "move"):
1718 engine_topic = "ksus"
garciadeblas4568a372021-03-24 09:19:48 +01001719 if (
1720 engine_topic == "vims"
1721 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001722 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001723
preethika.p329b8182020-04-22 12:25:39 +05301724 if topic == "subscriptions":
1725 engine_topic = main_topic + "_" + topic
1726
tiernoc94c3df2018-02-09 15:38:54 +01001727 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001728 if item in (
1729 "nsd_content",
1730 "package_content",
1731 "artifacts",
1732 "vnfd",
1733 "nsd",
1734 "nst",
1735 "nst_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301736 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001737 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001738 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001739 path = "$DESCRIPTOR"
1740 elif args:
1741 path = args
tiernob24258a2018-10-04 18:39:49 +02001742 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001743 path = ()
1744 else:
1745 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001746 file, _format = self.engine.get_file(
1747 engine_session,
1748 engine_topic,
1749 _id,
1750 path,
1751 cherrypy.request.headers.get("Accept"),
1752 )
tiernof27c79b2018-03-12 17:08:42 +01001753 outdata = file
1754 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001755 outdata = self.engine.get_item_list(
1756 engine_session, engine_topic, kwargs, api_req=True
1757 )
rshri2d386cb2024-07-05 14:35:51 +00001758 elif topic == "clusters" and item in (
1759 "infra_controller_profiles",
1760 "infra_config_profiles",
1761 "app_profiles",
1762 "resource_profiles",
1763 ):
1764 profile = item
1765 filter_q = None
1766 outdata = self.engine.get_one_item(
1767 engine_session,
1768 engine_topic,
1769 _id,
1770 profile,
1771 filter_q,
1772 api_req=True,
1773 )
yshah53cc9eb2024-07-05 13:06:31 +00001774 elif topic == "clusters" and item == "get_creds":
1775 outdata = self.engine.get_cluster_info(
1776 engine_session, engine_topic, _id, item
1777 )
tiernoc94c3df2018-02-09 15:38:54 +01001778 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301779 if item == "reports":
1780 # TODO check that project_id (_id in this context) has permissions
1781 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301782 filter_q = None
1783 if "vcaStatusRefresh" in kwargs:
1784 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
garciadeblasf2af4a12023-01-24 16:56:54 +01001785 outdata = self.engine.get_item(
1786 engine_session, engine_topic, _id, filter_q, True
1787 )
delacruzramo271d2002019-12-02 21:00:37 +01001788
tiernof27c79b2018-03-12 17:08:42 +01001789 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001790 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001791 if topic in (
1792 "ns_descriptors_content",
1793 "vnf_packages_content",
1794 "netslice_templates_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301795 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001796 ):
tiernof27c79b2018-03-12 17:08:42 +01001797 _id = cherrypy.request.headers.get("Transaction-Id")
yshah53cc9eb2024-07-05 13:06:31 +00001798
tiernof27c79b2018-03-12 17:08:42 +01001799 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001800 _id, _ = self.engine.new_item(
1801 rollback,
1802 engine_session,
1803 engine_topic,
1804 {},
1805 None,
1806 cherrypy.request.headers,
1807 )
1808 completed = self.engine.upload_content(
1809 engine_session,
1810 engine_topic,
1811 _id,
1812 indata,
1813 kwargs,
1814 cherrypy.request.headers,
1815 )
tiernof27c79b2018-03-12 17:08:42 +01001816 if completed:
tiernob24258a2018-10-04 18:39:49 +02001817 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001818 else:
1819 cherrypy.response.headers["Transaction-Id"] = _id
1820 outdata = {"id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001821 elif topic == "oka_packages":
1822 _id = cherrypy.request.headers.get("Transaction-Id")
1823
1824 if not _id:
1825 _id, _ = self.engine.new_item(
1826 rollback,
1827 engine_session,
1828 engine_topic,
1829 {},
1830 kwargs,
1831 cherrypy.request.headers,
1832 )
1833 cherrypy.request.headers["method"] = cherrypy.request.method
1834 if indata:
1835 completed = self.engine.upload_content(
1836 engine_session,
1837 engine_topic,
1838 _id,
1839 indata,
1840 None,
1841 cherrypy.request.headers,
1842 )
1843 if completed:
1844 self._set_location_header(main_topic, version, topic, _id)
1845 else:
1846 cherrypy.response.headers["Transaction-Id"] = _id
1847 outdata = {"_id": _id}
tiernob24258a2018-10-04 18:39:49 +02001848 elif topic == "ns_instances_content":
1849 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001850 _id, _ = self.engine.new_item(
1851 rollback, engine_session, engine_topic, indata, kwargs
1852 )
tiernob24258a2018-10-04 18:39:49 +02001853 # creates nslcmop
1854 indata["lcmOperationType"] = "instantiate"
1855 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001856 nslcmop_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001857 rollback, engine_session, "nslcmops", indata, None
1858 )
tiernob24258a2018-10-04 18:39:49 +02001859 self._set_location_header(main_topic, version, topic, _id)
garciadeblasf53612b2024-07-12 14:44:37 +02001860 outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName}
adurti3af50952024-05-31 11:36:57 +05301861 elif topic == "ns_instances_terminate":
1862 if indata.get("ns_ids"):
1863 for ns_id in indata.get("ns_ids"):
1864 nslcmop_desc = {
1865 "lcmOperationType": "terminate",
1866 "nsInstanceId": ns_id,
1867 "autoremove": indata.get("autoremove")
1868 if "autoremove" in indata
1869 else True,
1870 }
1871 op_id, _, _ = self.engine.new_item(
1872 rollback,
1873 engine_session,
1874 "nslcmops",
1875 nslcmop_desc,
1876 kwargs,
1877 )
1878 if not op_id:
1879 _ = self.engine.del_item(
1880 engine_session, engine_topic, ns_id
1881 )
1882 outdata = {"ns_ids": indata.get("ns_ids")}
1883 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001884 elif topic == "ns_instances" and item:
1885 indata["lcmOperationType"] = item
1886 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001887 _id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001888 rollback, engine_session, "nslcmops", indata, kwargs
1889 )
1890 self._set_location_header(
1891 main_topic, version, "ns_lcm_op_occs", _id
1892 )
garciadeblasf53612b2024-07-12 14:44:37 +02001893 outdata = {"id": _id, "nsName": nsName}
tierno65acb4d2018-04-06 16:42:40 +02001894 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001895 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001896 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001897 _id, _ = self.engine.new_item(
1898 rollback, engine_session, engine_topic, indata, kwargs
1899 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001900 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001901 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001902 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001903 nsilcmop_id, _ = self.engine.new_item(
1904 rollback, engine_session, "nsilcmops", indata, kwargs
1905 )
kuuse078f55e2019-05-16 19:24:21 +02001906 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001907 elif topic == "netslice_instances" and item:
1908 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001909 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001910 _id, _ = self.engine.new_item(
1911 rollback, engine_session, "nsilcmops", indata, kwargs
1912 )
1913 self._set_location_header(
1914 main_topic, version, "nsi_lcm_op_occs", _id
1915 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001916 outdata = {"id": _id}
1917 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001918 elif topic == "vnf_packages" and item == "action":
1919 indata["lcmOperationType"] = item
1920 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001921 _id, _ = self.engine.new_item(
1922 rollback, engine_session, "vnfpkgops", indata, kwargs
1923 )
1924 self._set_location_header(
1925 main_topic, version, "vnfpkg_op_occs", _id
1926 )
delacruzramo271d2002019-12-02 21:00:37 +01001927 outdata = {"id": _id}
1928 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301929 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001930 _id, _ = self.engine.new_item(
1931 rollback, engine_session, engine_topic, indata, kwargs
1932 )
preethika.p329b8182020-04-22 12:25:39 +05301933 self._set_location_header(main_topic, version, topic, _id)
1934 link = {}
1935 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001936 outdata = {
1937 "id": _id,
1938 "filter": indata["filter"],
1939 "callbackUri": indata["CallbackUri"],
1940 "_links": link,
1941 }
preethika.p329b8182020-04-22 12:25:39 +05301942 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001943 elif topic == "vnf_instances" and item:
1944 indata["lcmOperationType"] = item
1945 indata["vnfInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001946 _id, nsName, _ = self.engine.new_item(
garciadeblasf2af4a12023-01-24 16:56:54 +01001947 rollback, engine_session, "vnflcmops", indata, kwargs
1948 )
1949 self._set_location_header(
1950 main_topic, version, "vnf_lcm_op_occs", _id
1951 )
garciadeblasf53612b2024-07-12 14:44:37 +02001952 outdata = {"id": _id, "nsName": nsName}
almagiae47b9132022-05-17 14:12:22 +02001953 cherrypy.response.status = HTTPStatus.ACCEPTED.value
Gabriel Cuba84a60df2023-10-30 14:01:54 -05001954 elif topic == "ns_lcm_op_occs" and item == "cancel":
1955 indata["nsLcmOpOccId"] = _id
1956 self.engine.cancel_item(
1957 rollback, engine_session, "nslcmops", indata, None
1958 )
1959 self._set_location_header(main_topic, version, topic, _id)
1960 cherrypy.response.status = HTTPStatus.ACCEPTED.value
rshri2d386cb2024-07-05 14:35:51 +00001961 elif topic == "clusters" and _id == "register":
1962 # To register a cluster
1963 _id, _ = self.engine.add_item(
1964 rollback, engine_session, engine_topic, indata, kwargs
1965 )
1966 self._set_location_header(main_topic, version, topic, _id)
1967 outdata = {"id": _id}
1968 elif (
1969 topic
1970 in (
1971 "clusters",
1972 "infra_controller_profiles",
1973 "infra_config_profiles",
1974 "app_profiles",
1975 "resource_profiles",
1976 )
1977 and item is None
1978 ):
1979 # creates cluster, infra_controller_profiles, app_profiles, infra_config_profiles, and resource_profiles
1980 _id, _ = self.engine.new_item(
1981 rollback, engine_session, engine_topic, indata, kwargs
1982 )
1983 self._set_location_header(main_topic, version, topic, _id)
1984 outdata = {"_id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001985 elif topic == "ksus" and item:
1986 if item == "clone":
1987 _id = self.engine.clone(
1988 rollback,
1989 engine_session,
1990 engine_topic,
1991 _id,
1992 indata,
1993 kwargs,
1994 cherrypy.request.headers,
1995 )
1996 self._set_location_header(main_topic, version, topic, _id)
1997 outdata = {"id": _id}
1998 if item == "move":
1999 op_id = self.engine.move_ksu(
2000 engine_session, engine_topic, _id, indata, kwargs
2001 )
2002 outdata = {"op_id": op_id}
2003 elif topic == "ksus" and _id == "delete":
2004 op_id = self.engine.delete_ksu(
2005 engine_session, engine_topic, _id, indata
2006 )
2007 outdata = {"op_id": op_id}
2008 elif topic == "ksus" and _id == "update":
2009 op_id = self.engine.edit_item(
2010 engine_session, engine_topic, _id, indata, kwargs
2011 )
2012 outdata = {"op_id": op_id}
2013 elif topic == "clusters" and item in ("upgrade", "scale"):
2014 op_id = self.engine.update_cluster(
2015 engine_session, engine_topic, _id, item, indata
2016 )
2017 outdata = {"op_id": op_id}
tiernof27c79b2018-03-12 17:08:42 +01002018 else:
garciadeblas4568a372021-03-24 09:19:48 +01002019 _id, op_id = self.engine.new_item(
2020 rollback,
2021 engine_session,
2022 engine_topic,
2023 indata,
2024 kwargs,
2025 cherrypy.request.headers,
2026 )
tiernob24258a2018-10-04 18:39:49 +02002027 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01002028 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00002029 if op_id:
2030 outdata["op_id"] = op_id
2031 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02002032 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02002033
tiernoc94c3df2018-02-09 15:38:54 +01002034 elif method == "DELETE":
2035 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01002036 outdata = self.engine.del_item_list(
2037 engine_session, engine_topic, kwargs
2038 )
tierno09c073e2018-04-26 13:36:48 +02002039 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01002040 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00002041 # for NS NSI generate an operation
2042 op_id = None
tierno701018c2019-06-25 11:13:14 +00002043 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02002044 nslcmop_desc = {
2045 "lcmOperationType": "terminate",
2046 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002047 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02002048 }
garciadeblasf53612b2024-07-12 14:44:37 +02002049 op_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01002050 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
2051 )
tierno22577432020-04-08 15:16:57 +00002052 if op_id:
garciadeblasf53612b2024-07-12 14:44:37 +02002053 outdata = {"_id": op_id, "nsName": nsName}
garciadeblas4568a372021-03-24 09:19:48 +01002054 elif (
2055 topic == "netslice_instances_content"
2056 and not engine_session["force"]
2057 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02002058 nsilcmop_desc = {
2059 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02002060 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002061 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02002062 }
garciadeblas4568a372021-03-24 09:19:48 +01002063 op_id, _ = self.engine.new_item(
2064 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
2065 )
tierno22577432020-04-08 15:16:57 +00002066 if op_id:
2067 outdata = {"_id": op_id}
rshri2d386cb2024-07-05 14:35:51 +00002068 elif topic == "clusters" and item == "deregister":
2069 if not op_id:
2070 op_id = self.engine.remove(
2071 engine_session, engine_topic, _id
2072 )
2073 if op_id:
2074 outdata = {"_id": op_id}
2075 cherrypy.response.status = (
2076 HTTPStatus.ACCEPTED.value
2077 if op_id
2078 else HTTPStatus.NO_CONTENT.value
2079 )
yshah53cc9eb2024-07-05 13:06:31 +00002080 elif topic == "ksus":
2081 op_id = self.engine.delete_ksu(
2082 engine_session, engine_topic, _id, indata
2083 )
2084 outdata = {"op_id": op_id}
tierno22577432020-04-08 15:16:57 +00002085 # if there is not any deletion in process, delete
rshri2d386cb2024-07-05 14:35:51 +00002086 elif not op_id:
tierno22577432020-04-08 15:16:57 +00002087 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2088 if op_id:
rshri2d386cb2024-07-05 14:35:51 +00002089 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01002090 cherrypy.response.status = (
2091 HTTPStatus.ACCEPTED.value
2092 if op_id
2093 else HTTPStatus.NO_CONTENT.value
2094 )
tierno09c073e2018-04-26 13:36:48 +02002095
tierno7ae10112018-05-18 14:36:02 +02002096 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00002097 op_id = None
tierno701018c2019-06-25 11:13:14 +00002098 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01002099 raise NbiException(
2100 "Nothing to update. Provide payload and/or query string",
2101 HTTPStatus.BAD_REQUEST,
2102 )
2103 if (
kayal2001f71c2e82024-06-25 15:26:24 +05302104 item
2105 in (
2106 "nsd_content",
2107 "package_content",
2108 "nst_content",
2109 "template_content",
2110 )
garciadeblas4568a372021-03-24 09:19:48 +01002111 and method == "PUT"
2112 ):
2113 completed = self.engine.upload_content(
2114 engine_session,
2115 engine_topic,
2116 _id,
2117 indata,
2118 kwargs,
2119 cherrypy.request.headers,
2120 )
tiernof27c79b2018-03-12 17:08:42 +01002121 if not completed:
2122 cherrypy.response.headers["Transaction-Id"] = id
rshri2d386cb2024-07-05 14:35:51 +00002123 elif item in (
2124 "app_profiles",
2125 "resource_profiles",
2126 "infra_controller_profiles",
2127 "infra_config_profiles",
2128 ):
2129 op_id = self.engine.edit(
2130 engine_session, engine_topic, _id, item, indata, kwargs
2131 )
yshah53cc9eb2024-07-05 13:06:31 +00002132 elif topic == "oka_packages" and method == "PATCH":
2133 if kwargs:
2134 op_id = self.engine.edit_item(
2135 engine_session, engine_topic, _id, None, kwargs
2136 )
2137 if indata:
yshahffcac5f2024-08-19 12:49:07 +00002138 if isinstance(indata, dict):
yshah53cc9eb2024-07-05 13:06:31 +00002139 op_id = self.engine.edit_item(
2140 engine_session, engine_topic, _id, indata, kwargs
2141 )
2142 else:
2143 cherrypy.request.headers["method"] = cherrypy.request.method
2144 completed = self.engine.upload_content(
2145 engine_session,
2146 engine_topic,
2147 _id,
2148 indata,
2149 {},
2150 cherrypy.request.headers,
2151 )
2152 if not completed:
2153 cherrypy.response.headers["Transaction-Id"] = id
2154 elif topic == "oka_packages" and method == "PUT":
2155 if indata:
2156 cherrypy.request.headers["method"] = cherrypy.request.method
2157 completed = self.engine.upload_content(
2158 engine_session,
2159 engine_topic,
2160 _id,
2161 indata,
2162 {},
2163 cherrypy.request.headers,
2164 )
2165 if not completed:
2166 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01002167 else:
garciadeblas4568a372021-03-24 09:19:48 +01002168 op_id = self.engine.edit_item(
2169 engine_session, engine_topic, _id, indata, kwargs
2170 )
tiernobdebce92019-07-01 15:36:49 +00002171
2172 if op_id:
2173 cherrypy.response.status = HTTPStatus.ACCEPTED.value
2174 outdata = {"op_id": op_id}
2175 else:
2176 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
2177 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01002178 else:
garciadeblas4568a372021-03-24 09:19:48 +01002179 raise NbiException(
2180 "Method {} not allowed".format(method),
2181 HTTPStatus.METHOD_NOT_ALLOWED,
2182 )
tiernoa6bb45d2019-06-14 09:45:39 +00002183
2184 # if Role information changes, it is needed to reload the information of roles
2185 if topic == "roles" and method != "GET":
2186 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01002187
garciadeblas4568a372021-03-24 09:19:48 +01002188 if (
2189 topic == "projects"
2190 and method == "DELETE"
2191 or topic in ["users", "roles"]
2192 and method in ["PUT", "PATCH", "DELETE"]
2193 ):
delacruzramoad682a52019-12-10 16:26:34 +01002194 self.authenticator.remove_token_from_cache()
2195
garciadeblasf53612b2024-07-12 14:44:37 +02002196 cef_event(
2197 cef_logger,
2198 {
2199 "name": "User Operation",
2200 "sourceUserName": token_info.get("username"),
2201 },
2202 )
2203 if topic == "ns_instances_content" and url_id:
2204 nsName = (
2205 outdata.get("name") if method == "GET" else outdata.get("nsName")
2206 )
elumalai7802ff82023-04-24 20:38:32 +05302207 cef_event(
2208 cef_logger,
2209 {
garciadeblasf53612b2024-07-12 14:44:37 +02002210 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2211 log_mapping[method],
2212 topic,
2213 nsName,
2214 outdata.get("id"),
2215 token_info.get("project_name"),
2216 ),
2217 },
2218 )
2219 cherrypy.log("{}".format(cef_logger))
2220 elif topic == "ns_instances_content" and method == "POST":
2221 cef_event(
2222 cef_logger,
2223 {
2224 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2225 log_mapping[method],
2226 topic,
2227 outdata.get("nsName"),
2228 outdata.get("id"),
2229 token_info.get("project_name"),
2230 ),
2231 },
2232 )
2233 cherrypy.log("{}".format(cef_logger))
2234 elif topic in ("ns_instances", "vnf_instances") and item:
2235 cef_event(
2236 cef_logger,
2237 {
2238 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2239 log_mapping[method],
2240 topic,
2241 outdata.get("nsName"),
2242 url_id,
2243 token_info.get("project_name"),
2244 ),
2245 },
2246 )
2247 cherrypy.log("{}".format(cef_logger))
2248 elif item is not None:
2249 cef_event(
2250 cef_logger,
2251 {
elumalai7802ff82023-04-24 20:38:32 +05302252 "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format(
2253 item,
2254 topic,
2255 url_id,
2256 token_info.get("project_name"),
2257 ),
2258 },
2259 )
2260 cherrypy.log("{}".format(cef_logger))
2261 else:
2262 cef_event(
2263 cef_logger,
2264 {
elumalai7802ff82023-04-24 20:38:32 +05302265 "message": "{} {} {}, Project={} Outcome=Success".format(
2266 log_mapping[method],
2267 topic,
2268 url_id,
2269 token_info.get("project_name"),
2270 ),
2271 },
2272 )
2273 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002274 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02002275 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01002276 if isinstance(
2277 e,
2278 (
2279 NbiException,
2280 EngineException,
2281 DbException,
2282 FsException,
2283 MsgException,
2284 AuthException,
2285 ValidationError,
2286 AuthconnException,
2287 ),
2288 ):
tiernob24258a2018-10-04 18:39:49 +02002289 http_code_value = cherrypy.response.status = e.http_code.value
2290 http_code_name = e.http_code.name
2291 cherrypy.log("Exception {}".format(e))
2292 else:
garciadeblas4568a372021-03-24 09:19:48 +01002293 http_code_value = (
2294 cherrypy.response.status
2295 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01002296 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02002297 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02002298 if hasattr(outdata, "close"): # is an open file
2299 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02002300 error_text = str(e)
2301 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02002302 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02002303 try:
tiernocc103432018-10-19 14:10:35 +02002304 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01002305 self.engine.db.set_one(
2306 rollback_item["topic"],
2307 {"_id": rollback_item["_id"]},
2308 rollback_item["content"],
2309 fail_on_empty=False,
2310 )
preethika.p329b8182020-04-22 12:25:39 +05302311 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01002312 self.engine.db.del_list(
2313 rollback_item["topic"],
2314 rollback_item["filter"],
garciadeblas4568a372021-03-24 09:19:48 +01002315 )
tiernocc103432018-10-19 14:10:35 +02002316 else:
garciadeblas4568a372021-03-24 09:19:48 +01002317 self.engine.db.del_one(
2318 rollback_item["topic"],
2319 {"_id": rollback_item["_id"]},
2320 fail_on_empty=False,
2321 )
tierno3ace63c2018-05-03 17:51:43 +02002322 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01002323 rollback_error_text = "Rollback Exception {}: {}".format(
2324 rollback_item, e2
2325 )
tiernob24258a2018-10-04 18:39:49 +02002326 cherrypy.log(rollback_error_text)
2327 error_text += ". " + rollback_error_text
2328 # if isinstance(e, MsgException):
2329 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
2330 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01002331 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02002332 "code": http_code_name,
2333 "status": http_code_value,
2334 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01002335 }
elumalai7802ff82023-04-24 20:38:32 +05302336 if item is not None and token_info is not None:
2337 cef_event(
2338 cef_logger,
2339 {
2340 "name": "User Operation",
2341 "sourceUserName": token_info.get("username", None),
2342 "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format(
2343 item,
2344 topic,
2345 url_id,
2346 token_info.get("project_name", None),
2347 ),
2348 "severity": "2",
2349 },
2350 )
2351 cherrypy.log("{}".format(cef_logger))
2352 elif token_info is not None:
2353 cef_event(
2354 cef_logger,
2355 {
2356 "name": "User Operation",
2357 "sourceUserName": token_info.get("username", None),
2358 "message": "{} {} {}, Project={} Outcome=Failure".format(
2359 item,
2360 topic,
2361 url_id,
2362 token_info.get("project_name", None),
2363 ),
2364 "severity": "2",
2365 },
2366 )
2367 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002368 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01002369 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00002370 finally:
2371 if token_info:
2372 self._format_login(token_info)
2373 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
2374 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
2375 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01002376 cherrypy.request.login += ";{}={}".format(
2377 logging_id, outdata[logging_id][:36]
2378 )
tiernoc94c3df2018-02-09 15:38:54 +01002379
2380
tiernoc94c3df2018-02-09 15:38:54 +01002381def _start_service():
2382 """
2383 Callback function called when cherrypy.engine starts
2384 Override configuration with env variables
2385 Set database, storage, message configuration
2386 Init database with admin/admin user password
2387 """
tierno932499c2019-01-28 17:28:10 +00002388 global nbi_server
2389 global subscription_thread
elumalai7802ff82023-04-24 20:38:32 +05302390 global cef_logger
Rahulc72bc8e2023-12-05 11:54:38 +00002391 global current_backend
tiernoc94c3df2018-02-09 15:38:54 +01002392 cherrypy.log.error("Starting osm_nbi")
2393 # update general cherrypy configuration
2394 update_dict = {}
2395
garciadeblas4568a372021-03-24 09:19:48 +01002396 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01002397 for k, v in environ.items():
garciadeblas6d83f8f2023-06-19 22:34:49 +02002398 if k == "OSMNBI_USER_MANAGEMENT":
garciadeblasdf901002024-09-17 18:00:50 +02002399 feature_state = v.lower() == "true"
garciadeblas6d83f8f2023-06-19 22:34:49 +02002400 engine_config["authentication"]["user_management"] = feature_state
Adurtid68526d2023-11-14 11:14:53 +00002401 elif k == "OSMNBI_PWD_EXPIRE_DAYS":
2402 pwd_expire_days = int(v)
2403 engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days
2404 elif k == "OSMNBI_MAX_PWD_ATTEMPT":
2405 max_pwd_attempt = int(v)
2406 engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt
2407 elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS":
2408 account_expire_days = int(v)
2409 engine_config["authentication"]["account_expire_days"] = account_expire_days
tiernoc94c3df2018-02-09 15:38:54 +01002410 if not k.startswith("OSMNBI_"):
2411 continue
tiernoe1281182018-05-22 12:24:36 +02002412 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01002413 if not k2:
2414 continue
2415 try:
2416 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01002417 if k == "OSMNBI_STATIC_DIR":
2418 engine_config["/static"]["tools.staticdir.dir"] = v
2419 engine_config["/static"]["tools.staticdir.on"] = True
2420 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
2421 update_dict["server.socket_port"] = int(v)
2422 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
2423 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02002424 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01002425 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002426 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02002427 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002428 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01002429 engine_config[k1][k2] = int(v)
2430 else:
2431 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002432
tiernoc94c3df2018-02-09 15:38:54 +01002433 except ValueError as e:
2434 cherrypy.log.error("Ignoring environ '{}': " + str(e))
2435 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01002436 cherrypy.log(
2437 "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
2438 )
tiernoc94c3df2018-02-09 15:38:54 +01002439
2440 if update_dict:
2441 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02002442 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01002443
2444 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01002445 log_format_simple = (
2446 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
2447 )
2448 log_formatter_simple = logging.Formatter(
2449 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
2450 )
tiernoc94c3df2018-02-09 15:38:54 +01002451 logger_server = logging.getLogger("cherrypy.error")
2452 logger_access = logging.getLogger("cherrypy.access")
2453 logger_cherry = logging.getLogger("cherrypy")
2454 logger_nbi = logging.getLogger("nbi")
2455
tiernof5298be2018-05-16 14:43:57 +02002456 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01002457 file_handler = logging.handlers.RotatingFileHandler(
2458 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
2459 )
tiernoc94c3df2018-02-09 15:38:54 +01002460 file_handler.setFormatter(log_formatter_simple)
2461 logger_cherry.addHandler(file_handler)
2462 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02002463 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01002464 for format_, logger in {
2465 "nbi.server %(filename)s:%(lineno)s": logger_server,
2466 "nbi.access %(filename)s:%(lineno)s": logger_access,
2467 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
2468 }.items():
tiernob24258a2018-10-04 18:39:49 +02002469 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01002470 log_formatter_cherry = logging.Formatter(
2471 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
2472 )
tiernob24258a2018-10-04 18:39:49 +02002473 str_handler = logging.StreamHandler()
2474 str_handler.setFormatter(log_formatter_cherry)
2475 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01002476
tiernof5298be2018-05-16 14:43:57 +02002477 if engine_config["global"].get("log.level"):
2478 logger_cherry.setLevel(engine_config["global"]["log.level"])
2479 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01002480
2481 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01002482 for k1, logname in {
2483 "message": "nbi.msg",
2484 "database": "nbi.db",
2485 "storage": "nbi.fs",
2486 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01002487 engine_config[k1]["logger_name"] = logname
2488 logger_module = logging.getLogger(logname)
2489 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01002490 file_handler = logging.handlers.RotatingFileHandler(
2491 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
2492 )
tiernoc94c3df2018-02-09 15:38:54 +01002493 file_handler.setFormatter(log_formatter_simple)
2494 logger_module.addHandler(file_handler)
2495 if "loglevel" in engine_config[k1]:
2496 logger_module.setLevel(engine_config[k1]["loglevel"])
2497 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01002498 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
2499 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
2500 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
2501 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
2502 target_version=auth_database_version
2503 )
tiernobee508e2019-01-21 11:21:49 +00002504
elumalai7802ff82023-04-24 20:38:32 +05302505 cef_logger = cef_event_builder(engine_config["authentication"])
2506
tierno932499c2019-01-28 17:28:10 +00002507 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01002508 subscription_thread = SubscriptionThread(
2509 config=engine_config, engine=nbi_server.engine
2510 )
tierno932499c2019-01-28 17:28:10 +00002511 subscription_thread.start()
2512 # Do not capture except SubscriptionException
2513
tiernob2e48bd2020-02-04 15:47:18 +00002514 backend = engine_config["authentication"]["backend"]
Rahulc72bc8e2023-12-05 11:54:38 +00002515 current_backend = backend
garciadeblas4568a372021-03-24 09:19:48 +01002516 cherrypy.log.error(
2517 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
2518 nbi_version, nbi_version_date, backend
2519 )
2520 )
tiernoc94c3df2018-02-09 15:38:54 +01002521
2522
2523def _stop_service():
2524 """
2525 Callback function called when cherrypy.engine stops
2526 TODO: Ending database connections.
2527 """
tierno932499c2019-01-28 17:28:10 +00002528 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01002529 if subscription_thread:
2530 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00002531 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01002532 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01002533 cherrypy.log.error("Stopping osm_nbi")
2534
tierno2236d202018-05-16 19:05:16 +02002535
tiernof5298be2018-05-16 14:43:57 +02002536def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00002537 global nbi_server
tierno932499c2019-01-28 17:28:10 +00002538 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01002539 cherrypy.engine.subscribe("start", _start_service)
2540 cherrypy.engine.subscribe("stop", _stop_service)
2541 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02002542
2543
2544def usage():
garciadeblas4568a372021-03-24 09:19:48 +01002545 print(
2546 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02002547 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
2548 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01002549 """.format(
2550 sys.argv[0]
2551 )
2552 )
tierno2236d202018-05-16 19:05:16 +02002553 # --log-socket-host HOST: send logs to this host")
2554 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002555
2556
garciadeblas4568a372021-03-24 09:19:48 +01002557if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002558 try:
2559 # load parameters and configuration
2560 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2561 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2562 config_file = None
2563 for o, a in opts:
2564 if o in ("-h", "--help"):
2565 usage()
2566 sys.exit()
2567 elif o in ("-c", "--config"):
2568 config_file = a
2569 # elif o == "--log-socket-port":
2570 # log_socket_port = a
2571 # elif o == "--log-socket-host":
2572 # log_socket_host = a
2573 # elif o == "--log-file":
2574 # log_file = a
2575 else:
2576 assert False, "Unhandled option"
2577 if config_file:
2578 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002579 print(
2580 "configuration file '{}' that not exist".format(config_file),
2581 file=sys.stderr,
2582 )
tiernof5298be2018-05-16 14:43:57 +02002583 exit(1)
2584 else:
garciadeblas4568a372021-03-24 09:19:48 +01002585 for config_file in (
2586 __file__[: __file__.rfind(".")] + ".cfg",
2587 "./nbi.cfg",
2588 "/etc/osm/nbi.cfg",
2589 ):
tiernof5298be2018-05-16 14:43:57 +02002590 if path.isfile(config_file):
2591 break
2592 else:
garciadeblas4568a372021-03-24 09:19:48 +01002593 print(
2594 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2595 file=sys.stderr,
2596 )
tiernof5298be2018-05-16 14:43:57 +02002597 exit(1)
2598 nbi(config_file)
2599 except getopt.GetoptError as e:
2600 print(str(e), file=sys.stderr)
2601 # usage()
2602 exit(1)