blob: 1bb79275f7a76816a8dbc951aa24f0772011faae [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"],
Adurti76d4b762024-05-07 06:04:37 +00001513 "user_id": token_info["user_id"],
garciadeblas4568a372021-03-24 09:19:48 +01001514 "admin": token_info["admin"],
1515 "public": None,
1516 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1517 }
tierno65ca36d2019-02-12 19:27:52 +01001518 if kwargs:
1519 # FORCE
1520 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001521 if (
1522 kwargs["FORCE"].lower() != "false"
1523 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001524 admin_query["force"] = True
1525 del kwargs["FORCE"]
1526 # PUBLIC
1527 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001528 if (
1529 kwargs["PUBLIC"].lower() != "false"
1530 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001531 admin_query["public"] = True
1532 else:
1533 admin_query["public"] = False
1534 del kwargs["PUBLIC"]
1535 # ADMIN
1536 if "ADMIN" in kwargs:
1537 behave_as = kwargs.pop("ADMIN")
1538 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001539 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001540 raise NbiException(
1541 "Only admin projects can use 'ADMIN' query string",
1542 HTTPStatus.UNAUTHORIZED,
1543 )
1544 if (
1545 not behave_as or behave_as.lower() == "true"
1546 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001547 admin_query["project_id"] = ()
1548 elif isinstance(behave_as, (list, tuple)):
1549 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001550 else: # isinstance(behave_as, str)
1551 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001552 if "SET_PROJECT" in kwargs:
1553 set_project = kwargs.pop("SET_PROJECT")
1554 if not set_project:
1555 admin_query["set_project"] = list(admin_query["project_id"])
1556 else:
1557 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001558 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001559 if admin_query["project_id"]:
1560 for p in set_project:
1561 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001562 raise NbiException(
1563 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1564 "'ADMIN='{p}'".format(p=p),
1565 HTTPStatus.UNAUTHORIZED,
1566 )
tierno65ca36d2019-02-12 19:27:52 +01001567 admin_query["set_project"] = set_project
1568
1569 # PROJECT_READ
1570 # if "PROJECT_READ" in kwargs:
1571 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001572 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001573 if method == "GET":
1574 if _id:
1575 admin_query["method"] = "show"
1576 else:
1577 admin_query["method"] = "list"
1578 elif method == "DELETE":
1579 admin_query["method"] = "delete"
1580 else:
1581 admin_query["method"] = "write"
1582 return admin_query
1583
tiernoc94c3df2018-02-09 15:38:54 +01001584 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001585 def default(
1586 self,
1587 main_topic=None,
1588 version=None,
1589 topic=None,
1590 _id=None,
1591 item=None,
1592 *args,
1593 **kwargs
1594 ):
tierno701018c2019-06-25 11:13:14 +00001595 token_info = None
selvi.j9919bbc2023-04-26 12:22:13 +00001596 outdata = {}
tiernof27c79b2018-03-12 17:08:42 +01001597 _format = None
tierno0f98af52018-03-19 10:28:22 +01001598 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001599 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001600 rollback = []
tierno701018c2019-06-25 11:13:14 +00001601 engine_session = None
elumalai7802ff82023-04-24 20:38:32 +05301602 url_id = ""
1603 log_mapping = {
1604 "POST": "Creating",
1605 "GET": "Fetching",
1606 "DELETE": "Deleting",
1607 "PUT": "Updating",
1608 "PATCH": "Updating",
1609 }
tiernoc94c3df2018-02-09 15:38:54 +01001610 try:
tiernob24258a2018-10-04 18:39:49 +02001611 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001612 raise NbiException(
1613 "URL must contain at least 'main_topic/version/topic'",
1614 HTTPStatus.METHOD_NOT_ALLOWED,
1615 )
1616 if main_topic not in (
1617 "admin",
1618 "vnfpkgm",
1619 "nsd",
1620 "nslcm",
1621 "pdu",
1622 "nst",
1623 "nsilcm",
1624 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001625 "vnflcm",
rshri2d386cb2024-07-05 14:35:51 +00001626 "k8scluster",
yshah53cc9eb2024-07-05 13:06:31 +00001627 "ksu",
1628 "oka",
garciadeblas4568a372021-03-24 09:19:48 +01001629 ):
1630 raise NbiException(
1631 "URL main_topic '{}' not supported".format(main_topic),
1632 HTTPStatus.METHOD_NOT_ALLOWED,
1633 )
1634 if version != "v1":
1635 raise NbiException(
1636 "URL version '{}' not supported".format(version),
1637 HTTPStatus.METHOD_NOT_ALLOWED,
1638 )
elumalai7802ff82023-04-24 20:38:32 +05301639 if _id is not None:
1640 url_id = _id
tiernoc94c3df2018-02-09 15:38:54 +01001641
garciadeblas4568a372021-03-24 09:19:48 +01001642 if (
1643 kwargs
1644 and "METHOD" in kwargs
1645 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1646 ):
tiernof27c79b2018-03-12 17:08:42 +01001647 method = kwargs.pop("METHOD")
1648 else:
1649 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001650
garciadeblas4568a372021-03-24 09:19:48 +01001651 role_permission = self._check_valid_url_method(
1652 method, main_topic, version, topic, _id, item, *args
1653 )
1654 query_string_operations = self._extract_query_string_operations(
1655 kwargs, method
1656 )
tiernob24258a2018-10-04 18:39:49 +02001657 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001658 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001659 token_info = self.authenticator.authorize(
1660 role_permission, query_string_operations, _id
1661 )
tierno12eac3c2020-03-19 23:22:08 +00001662 if main_topic == "admin" and topic == "domains":
1663 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001664 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001665 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001666 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301667
vijay.r35ef2f72019-04-30 17:55:49 +05301668 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001669 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001670
tiernob24258a2018-10-04 18:39:49 +02001671 if main_topic == "nsd":
1672 engine_topic = "nsds"
kayal2001f71c2e82024-06-25 15:26:24 +05301673 if topic == "ns_config_template":
1674 engine_topic = "nsconfigtemps"
tiernob24258a2018-10-04 18:39:49 +02001675 elif main_topic == "vnfpkgm":
1676 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001677 if topic == "vnfpkg_op_occs":
1678 engine_topic = "vnfpkgops"
1679 if topic == "vnf_packages" and item == "action":
1680 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001681 elif main_topic == "nslcm":
1682 engine_topic = "nsrs"
1683 if topic == "ns_lcm_op_occs":
1684 engine_topic = "nslcmops"
1685 if topic == "vnfrs" or topic == "vnf_instances":
1686 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001687 elif main_topic == "vnflcm":
1688 if topic == "vnf_lcm_op_occs":
1689 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001690 elif main_topic == "nst":
1691 engine_topic = "nsts"
1692 elif main_topic == "nsilcm":
1693 engine_topic = "nsis"
1694 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001695 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001696 elif main_topic == "pdu":
1697 engine_topic = "pdus"
rshri2d386cb2024-07-05 14:35:51 +00001698 elif main_topic == "k8scluster":
1699 engine_topic = "k8s"
1700 if topic == "clusters" and _id == "register" or item == "deregister":
1701 engine_topic = "k8sops"
1702 elif topic == "infra_controller_profiles":
1703 engine_topic = "infras_cont"
1704 elif topic == "infra_config_profiles":
1705 engine_topic = "infras_conf"
1706 elif topic == "resource_profiles":
1707 engine_topic = "resources"
1708 elif topic == "app_profiles":
1709 engine_topic = "apps"
yshah53cc9eb2024-07-05 13:06:31 +00001710 elif main_topic == "k8scluster" and item in (
1711 "upgrade",
1712 "get_creds",
1713 "scale",
1714 ):
1715 engine_topic = "k8s"
1716 elif main_topic == "ksu" and engine_topic in ("ksus", "clone", "move"):
1717 engine_topic = "ksus"
garciadeblas4568a372021-03-24 09:19:48 +01001718 if (
1719 engine_topic == "vims"
1720 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001721 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001722
preethika.p329b8182020-04-22 12:25:39 +05301723 if topic == "subscriptions":
1724 engine_topic = main_topic + "_" + topic
1725
tiernoc94c3df2018-02-09 15:38:54 +01001726 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001727 if item in (
1728 "nsd_content",
1729 "package_content",
1730 "artifacts",
1731 "vnfd",
1732 "nsd",
1733 "nst",
1734 "nst_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301735 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001736 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001737 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001738 path = "$DESCRIPTOR"
1739 elif args:
1740 path = args
tiernob24258a2018-10-04 18:39:49 +02001741 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001742 path = ()
1743 else:
1744 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001745 file, _format = self.engine.get_file(
1746 engine_session,
1747 engine_topic,
1748 _id,
1749 path,
1750 cherrypy.request.headers.get("Accept"),
1751 )
tiernof27c79b2018-03-12 17:08:42 +01001752 outdata = file
1753 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001754 outdata = self.engine.get_item_list(
1755 engine_session, engine_topic, kwargs, api_req=True
1756 )
rshri2d386cb2024-07-05 14:35:51 +00001757 elif topic == "clusters" and item in (
1758 "infra_controller_profiles",
1759 "infra_config_profiles",
1760 "app_profiles",
1761 "resource_profiles",
1762 ):
1763 profile = item
1764 filter_q = None
1765 outdata = self.engine.get_one_item(
1766 engine_session,
1767 engine_topic,
1768 _id,
1769 profile,
1770 filter_q,
1771 api_req=True,
1772 )
yshah53cc9eb2024-07-05 13:06:31 +00001773 elif topic == "clusters" and item == "get_creds":
1774 outdata = self.engine.get_cluster_info(
1775 engine_session, engine_topic, _id, item
1776 )
tiernoc94c3df2018-02-09 15:38:54 +01001777 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301778 if item == "reports":
1779 # TODO check that project_id (_id in this context) has permissions
1780 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301781 filter_q = None
1782 if "vcaStatusRefresh" in kwargs:
1783 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
garciadeblasf2af4a12023-01-24 16:56:54 +01001784 outdata = self.engine.get_item(
1785 engine_session, engine_topic, _id, filter_q, True
1786 )
delacruzramo271d2002019-12-02 21:00:37 +01001787
tiernof27c79b2018-03-12 17:08:42 +01001788 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001789 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001790 if topic in (
1791 "ns_descriptors_content",
1792 "vnf_packages_content",
1793 "netslice_templates_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301794 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001795 ):
tiernof27c79b2018-03-12 17:08:42 +01001796 _id = cherrypy.request.headers.get("Transaction-Id")
yshah53cc9eb2024-07-05 13:06:31 +00001797
tiernof27c79b2018-03-12 17:08:42 +01001798 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001799 _id, _ = self.engine.new_item(
1800 rollback,
1801 engine_session,
1802 engine_topic,
1803 {},
1804 None,
1805 cherrypy.request.headers,
1806 )
1807 completed = self.engine.upload_content(
1808 engine_session,
1809 engine_topic,
1810 _id,
1811 indata,
1812 kwargs,
1813 cherrypy.request.headers,
1814 )
tiernof27c79b2018-03-12 17:08:42 +01001815 if completed:
tiernob24258a2018-10-04 18:39:49 +02001816 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001817 else:
1818 cherrypy.response.headers["Transaction-Id"] = _id
1819 outdata = {"id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001820 elif topic == "oka_packages":
1821 _id = cherrypy.request.headers.get("Transaction-Id")
1822
1823 if not _id:
1824 _id, _ = self.engine.new_item(
1825 rollback,
1826 engine_session,
1827 engine_topic,
1828 {},
1829 kwargs,
1830 cherrypy.request.headers,
1831 )
1832 cherrypy.request.headers["method"] = cherrypy.request.method
1833 if indata:
1834 completed = self.engine.upload_content(
1835 engine_session,
1836 engine_topic,
1837 _id,
1838 indata,
1839 None,
1840 cherrypy.request.headers,
1841 )
1842 if completed:
1843 self._set_location_header(main_topic, version, topic, _id)
1844 else:
1845 cherrypy.response.headers["Transaction-Id"] = _id
1846 outdata = {"_id": _id}
tiernob24258a2018-10-04 18:39:49 +02001847 elif topic == "ns_instances_content":
1848 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001849 _id, _ = self.engine.new_item(
1850 rollback, engine_session, engine_topic, indata, kwargs
1851 )
tiernob24258a2018-10-04 18:39:49 +02001852 # creates nslcmop
1853 indata["lcmOperationType"] = "instantiate"
1854 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001855 nslcmop_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001856 rollback, engine_session, "nslcmops", indata, None
1857 )
tiernob24258a2018-10-04 18:39:49 +02001858 self._set_location_header(main_topic, version, topic, _id)
garciadeblasf53612b2024-07-12 14:44:37 +02001859 outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName}
adurti3af50952024-05-31 11:36:57 +05301860 elif topic == "ns_instances_terminate":
1861 if indata.get("ns_ids"):
1862 for ns_id in indata.get("ns_ids"):
1863 nslcmop_desc = {
1864 "lcmOperationType": "terminate",
1865 "nsInstanceId": ns_id,
1866 "autoremove": indata.get("autoremove")
1867 if "autoremove" in indata
1868 else True,
1869 }
1870 op_id, _, _ = self.engine.new_item(
1871 rollback,
1872 engine_session,
1873 "nslcmops",
1874 nslcmop_desc,
1875 kwargs,
1876 )
1877 if not op_id:
1878 _ = self.engine.del_item(
1879 engine_session, engine_topic, ns_id
1880 )
1881 outdata = {"ns_ids": indata.get("ns_ids")}
1882 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001883 elif topic == "ns_instances" and item:
1884 indata["lcmOperationType"] = item
1885 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001886 _id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001887 rollback, engine_session, "nslcmops", indata, kwargs
1888 )
1889 self._set_location_header(
1890 main_topic, version, "ns_lcm_op_occs", _id
1891 )
garciadeblasf53612b2024-07-12 14:44:37 +02001892 outdata = {"id": _id, "nsName": nsName}
tierno65acb4d2018-04-06 16:42:40 +02001893 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001894 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001895 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001896 _id, _ = self.engine.new_item(
1897 rollback, engine_session, engine_topic, indata, kwargs
1898 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001899 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001900 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001901 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001902 nsilcmop_id, _ = self.engine.new_item(
1903 rollback, engine_session, "nsilcmops", indata, kwargs
1904 )
kuuse078f55e2019-05-16 19:24:21 +02001905 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001906 elif topic == "netslice_instances" and item:
1907 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001908 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001909 _id, _ = self.engine.new_item(
1910 rollback, engine_session, "nsilcmops", indata, kwargs
1911 )
1912 self._set_location_header(
1913 main_topic, version, "nsi_lcm_op_occs", _id
1914 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001915 outdata = {"id": _id}
1916 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001917 elif topic == "vnf_packages" and item == "action":
1918 indata["lcmOperationType"] = item
1919 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001920 _id, _ = self.engine.new_item(
1921 rollback, engine_session, "vnfpkgops", indata, kwargs
1922 )
1923 self._set_location_header(
1924 main_topic, version, "vnfpkg_op_occs", _id
1925 )
delacruzramo271d2002019-12-02 21:00:37 +01001926 outdata = {"id": _id}
1927 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301928 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001929 _id, _ = self.engine.new_item(
1930 rollback, engine_session, engine_topic, indata, kwargs
1931 )
preethika.p329b8182020-04-22 12:25:39 +05301932 self._set_location_header(main_topic, version, topic, _id)
1933 link = {}
1934 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001935 outdata = {
1936 "id": _id,
1937 "filter": indata["filter"],
1938 "callbackUri": indata["CallbackUri"],
1939 "_links": link,
1940 }
preethika.p329b8182020-04-22 12:25:39 +05301941 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001942 elif topic == "vnf_instances" and item:
1943 indata["lcmOperationType"] = item
1944 indata["vnfInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001945 _id, nsName, _ = self.engine.new_item(
garciadeblasf2af4a12023-01-24 16:56:54 +01001946 rollback, engine_session, "vnflcmops", indata, kwargs
1947 )
1948 self._set_location_header(
1949 main_topic, version, "vnf_lcm_op_occs", _id
1950 )
garciadeblasf53612b2024-07-12 14:44:37 +02001951 outdata = {"id": _id, "nsName": nsName}
almagiae47b9132022-05-17 14:12:22 +02001952 cherrypy.response.status = HTTPStatus.ACCEPTED.value
Gabriel Cuba84a60df2023-10-30 14:01:54 -05001953 elif topic == "ns_lcm_op_occs" and item == "cancel":
1954 indata["nsLcmOpOccId"] = _id
1955 self.engine.cancel_item(
1956 rollback, engine_session, "nslcmops", indata, None
1957 )
1958 self._set_location_header(main_topic, version, topic, _id)
1959 cherrypy.response.status = HTTPStatus.ACCEPTED.value
rshri2d386cb2024-07-05 14:35:51 +00001960 elif topic == "clusters" and _id == "register":
1961 # To register a cluster
1962 _id, _ = self.engine.add_item(
1963 rollback, engine_session, engine_topic, indata, kwargs
1964 )
1965 self._set_location_header(main_topic, version, topic, _id)
1966 outdata = {"id": _id}
1967 elif (
1968 topic
1969 in (
1970 "clusters",
1971 "infra_controller_profiles",
1972 "infra_config_profiles",
1973 "app_profiles",
1974 "resource_profiles",
1975 )
1976 and item is None
1977 ):
1978 # creates cluster, infra_controller_profiles, app_profiles, infra_config_profiles, and resource_profiles
1979 _id, _ = self.engine.new_item(
1980 rollback, engine_session, engine_topic, indata, kwargs
1981 )
1982 self._set_location_header(main_topic, version, topic, _id)
1983 outdata = {"_id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001984 elif topic == "ksus" and item:
1985 if item == "clone":
1986 _id = self.engine.clone(
1987 rollback,
1988 engine_session,
1989 engine_topic,
1990 _id,
1991 indata,
1992 kwargs,
1993 cherrypy.request.headers,
1994 )
1995 self._set_location_header(main_topic, version, topic, _id)
1996 outdata = {"id": _id}
1997 if item == "move":
1998 op_id = self.engine.move_ksu(
1999 engine_session, engine_topic, _id, indata, kwargs
2000 )
2001 outdata = {"op_id": op_id}
2002 elif topic == "ksus" and _id == "delete":
2003 op_id = self.engine.delete_ksu(
2004 engine_session, engine_topic, _id, indata
2005 )
2006 outdata = {"op_id": op_id}
2007 elif topic == "ksus" and _id == "update":
2008 op_id = self.engine.edit_item(
2009 engine_session, engine_topic, _id, indata, kwargs
2010 )
2011 outdata = {"op_id": op_id}
2012 elif topic == "clusters" and item in ("upgrade", "scale"):
2013 op_id = self.engine.update_cluster(
2014 engine_session, engine_topic, _id, item, indata
2015 )
2016 outdata = {"op_id": op_id}
tiernof27c79b2018-03-12 17:08:42 +01002017 else:
garciadeblas4568a372021-03-24 09:19:48 +01002018 _id, op_id = self.engine.new_item(
2019 rollback,
2020 engine_session,
2021 engine_topic,
2022 indata,
2023 kwargs,
2024 cherrypy.request.headers,
2025 )
tiernob24258a2018-10-04 18:39:49 +02002026 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01002027 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00002028 if op_id:
2029 outdata["op_id"] = op_id
2030 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02002031 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02002032
tiernoc94c3df2018-02-09 15:38:54 +01002033 elif method == "DELETE":
2034 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01002035 outdata = self.engine.del_item_list(
2036 engine_session, engine_topic, kwargs
2037 )
tierno09c073e2018-04-26 13:36:48 +02002038 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01002039 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00002040 # for NS NSI generate an operation
2041 op_id = None
tierno701018c2019-06-25 11:13:14 +00002042 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02002043 nslcmop_desc = {
2044 "lcmOperationType": "terminate",
2045 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002046 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02002047 }
garciadeblasf53612b2024-07-12 14:44:37 +02002048 op_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01002049 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
2050 )
tierno22577432020-04-08 15:16:57 +00002051 if op_id:
garciadeblasf53612b2024-07-12 14:44:37 +02002052 outdata = {"_id": op_id, "nsName": nsName}
garciadeblas4568a372021-03-24 09:19:48 +01002053 elif (
2054 topic == "netslice_instances_content"
2055 and not engine_session["force"]
2056 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02002057 nsilcmop_desc = {
2058 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02002059 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002060 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02002061 }
garciadeblas4568a372021-03-24 09:19:48 +01002062 op_id, _ = self.engine.new_item(
2063 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
2064 )
tierno22577432020-04-08 15:16:57 +00002065 if op_id:
2066 outdata = {"_id": op_id}
rshri2d386cb2024-07-05 14:35:51 +00002067 elif topic == "clusters" and item == "deregister":
2068 if not op_id:
2069 op_id = self.engine.remove(
2070 engine_session, engine_topic, _id
2071 )
2072 if op_id:
2073 outdata = {"_id": op_id}
2074 cherrypy.response.status = (
2075 HTTPStatus.ACCEPTED.value
2076 if op_id
2077 else HTTPStatus.NO_CONTENT.value
2078 )
yshah53cc9eb2024-07-05 13:06:31 +00002079 elif topic == "ksus":
2080 op_id = self.engine.delete_ksu(
2081 engine_session, engine_topic, _id, indata
2082 )
2083 outdata = {"op_id": op_id}
tierno22577432020-04-08 15:16:57 +00002084 # if there is not any deletion in process, delete
rshri2d386cb2024-07-05 14:35:51 +00002085 elif not op_id:
tierno22577432020-04-08 15:16:57 +00002086 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2087 if op_id:
rshri2d386cb2024-07-05 14:35:51 +00002088 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01002089 cherrypy.response.status = (
2090 HTTPStatus.ACCEPTED.value
2091 if op_id
2092 else HTTPStatus.NO_CONTENT.value
2093 )
tierno09c073e2018-04-26 13:36:48 +02002094
tierno7ae10112018-05-18 14:36:02 +02002095 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00002096 op_id = None
tierno701018c2019-06-25 11:13:14 +00002097 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01002098 raise NbiException(
2099 "Nothing to update. Provide payload and/or query string",
2100 HTTPStatus.BAD_REQUEST,
2101 )
2102 if (
kayal2001f71c2e82024-06-25 15:26:24 +05302103 item
2104 in (
2105 "nsd_content",
2106 "package_content",
2107 "nst_content",
2108 "template_content",
2109 )
garciadeblas4568a372021-03-24 09:19:48 +01002110 and method == "PUT"
2111 ):
2112 completed = self.engine.upload_content(
2113 engine_session,
2114 engine_topic,
2115 _id,
2116 indata,
2117 kwargs,
2118 cherrypy.request.headers,
2119 )
tiernof27c79b2018-03-12 17:08:42 +01002120 if not completed:
2121 cherrypy.response.headers["Transaction-Id"] = id
rshri2d386cb2024-07-05 14:35:51 +00002122 elif item in (
2123 "app_profiles",
2124 "resource_profiles",
2125 "infra_controller_profiles",
2126 "infra_config_profiles",
2127 ):
2128 op_id = self.engine.edit(
2129 engine_session, engine_topic, _id, item, indata, kwargs
2130 )
yshah53cc9eb2024-07-05 13:06:31 +00002131 elif topic == "oka_packages" and method == "PATCH":
2132 if kwargs:
2133 op_id = self.engine.edit_item(
2134 engine_session, engine_topic, _id, None, kwargs
2135 )
2136 if indata:
yshahffcac5f2024-08-19 12:49:07 +00002137 if isinstance(indata, dict):
yshah53cc9eb2024-07-05 13:06:31 +00002138 op_id = self.engine.edit_item(
2139 engine_session, engine_topic, _id, indata, kwargs
2140 )
2141 else:
2142 cherrypy.request.headers["method"] = cherrypy.request.method
2143 completed = self.engine.upload_content(
2144 engine_session,
2145 engine_topic,
2146 _id,
2147 indata,
2148 {},
2149 cherrypy.request.headers,
2150 )
2151 if not completed:
2152 cherrypy.response.headers["Transaction-Id"] = id
2153 elif topic == "oka_packages" and method == "PUT":
2154 if indata:
2155 cherrypy.request.headers["method"] = cherrypy.request.method
2156 completed = self.engine.upload_content(
2157 engine_session,
2158 engine_topic,
2159 _id,
2160 indata,
2161 {},
2162 cherrypy.request.headers,
2163 )
2164 if not completed:
2165 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01002166 else:
garciadeblas4568a372021-03-24 09:19:48 +01002167 op_id = self.engine.edit_item(
2168 engine_session, engine_topic, _id, indata, kwargs
2169 )
tiernobdebce92019-07-01 15:36:49 +00002170
2171 if op_id:
2172 cherrypy.response.status = HTTPStatus.ACCEPTED.value
2173 outdata = {"op_id": op_id}
2174 else:
2175 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
2176 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01002177 else:
garciadeblas4568a372021-03-24 09:19:48 +01002178 raise NbiException(
2179 "Method {} not allowed".format(method),
2180 HTTPStatus.METHOD_NOT_ALLOWED,
2181 )
tiernoa6bb45d2019-06-14 09:45:39 +00002182
2183 # if Role information changes, it is needed to reload the information of roles
2184 if topic == "roles" and method != "GET":
2185 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01002186
garciadeblas4568a372021-03-24 09:19:48 +01002187 if (
2188 topic == "projects"
2189 and method == "DELETE"
2190 or topic in ["users", "roles"]
2191 and method in ["PUT", "PATCH", "DELETE"]
2192 ):
delacruzramoad682a52019-12-10 16:26:34 +01002193 self.authenticator.remove_token_from_cache()
2194
garciadeblasf53612b2024-07-12 14:44:37 +02002195 cef_event(
2196 cef_logger,
2197 {
2198 "name": "User Operation",
2199 "sourceUserName": token_info.get("username"),
2200 },
2201 )
2202 if topic == "ns_instances_content" and url_id:
2203 nsName = (
2204 outdata.get("name") if method == "GET" else outdata.get("nsName")
2205 )
elumalai7802ff82023-04-24 20:38:32 +05302206 cef_event(
2207 cef_logger,
2208 {
garciadeblasf53612b2024-07-12 14:44:37 +02002209 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2210 log_mapping[method],
2211 topic,
2212 nsName,
2213 outdata.get("id"),
2214 token_info.get("project_name"),
2215 ),
2216 },
2217 )
2218 cherrypy.log("{}".format(cef_logger))
2219 elif topic == "ns_instances_content" and method == "POST":
2220 cef_event(
2221 cef_logger,
2222 {
2223 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2224 log_mapping[method],
2225 topic,
2226 outdata.get("nsName"),
2227 outdata.get("id"),
2228 token_info.get("project_name"),
2229 ),
2230 },
2231 )
2232 cherrypy.log("{}".format(cef_logger))
2233 elif topic in ("ns_instances", "vnf_instances") and item:
2234 cef_event(
2235 cef_logger,
2236 {
2237 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2238 log_mapping[method],
2239 topic,
2240 outdata.get("nsName"),
2241 url_id,
2242 token_info.get("project_name"),
2243 ),
2244 },
2245 )
2246 cherrypy.log("{}".format(cef_logger))
2247 elif item is not None:
2248 cef_event(
2249 cef_logger,
2250 {
elumalai7802ff82023-04-24 20:38:32 +05302251 "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format(
2252 item,
2253 topic,
2254 url_id,
2255 token_info.get("project_name"),
2256 ),
2257 },
2258 )
2259 cherrypy.log("{}".format(cef_logger))
2260 else:
2261 cef_event(
2262 cef_logger,
2263 {
elumalai7802ff82023-04-24 20:38:32 +05302264 "message": "{} {} {}, Project={} Outcome=Success".format(
2265 log_mapping[method],
2266 topic,
2267 url_id,
2268 token_info.get("project_name"),
2269 ),
2270 },
2271 )
2272 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002273 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02002274 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01002275 if isinstance(
2276 e,
2277 (
2278 NbiException,
2279 EngineException,
2280 DbException,
2281 FsException,
2282 MsgException,
2283 AuthException,
2284 ValidationError,
2285 AuthconnException,
2286 ),
2287 ):
tiernob24258a2018-10-04 18:39:49 +02002288 http_code_value = cherrypy.response.status = e.http_code.value
2289 http_code_name = e.http_code.name
2290 cherrypy.log("Exception {}".format(e))
2291 else:
garciadeblas4568a372021-03-24 09:19:48 +01002292 http_code_value = (
2293 cherrypy.response.status
2294 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01002295 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02002296 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02002297 if hasattr(outdata, "close"): # is an open file
2298 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02002299 error_text = str(e)
2300 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02002301 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02002302 try:
tiernocc103432018-10-19 14:10:35 +02002303 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01002304 self.engine.db.set_one(
2305 rollback_item["topic"],
2306 {"_id": rollback_item["_id"]},
2307 rollback_item["content"],
2308 fail_on_empty=False,
2309 )
preethika.p329b8182020-04-22 12:25:39 +05302310 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01002311 self.engine.db.del_list(
2312 rollback_item["topic"],
2313 rollback_item["filter"],
garciadeblas4568a372021-03-24 09:19:48 +01002314 )
tiernocc103432018-10-19 14:10:35 +02002315 else:
garciadeblas4568a372021-03-24 09:19:48 +01002316 self.engine.db.del_one(
2317 rollback_item["topic"],
2318 {"_id": rollback_item["_id"]},
2319 fail_on_empty=False,
2320 )
tierno3ace63c2018-05-03 17:51:43 +02002321 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01002322 rollback_error_text = "Rollback Exception {}: {}".format(
2323 rollback_item, e2
2324 )
tiernob24258a2018-10-04 18:39:49 +02002325 cherrypy.log(rollback_error_text)
2326 error_text += ". " + rollback_error_text
2327 # if isinstance(e, MsgException):
2328 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
2329 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01002330 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02002331 "code": http_code_name,
2332 "status": http_code_value,
2333 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01002334 }
elumalai7802ff82023-04-24 20:38:32 +05302335 if item is not None and token_info is not None:
2336 cef_event(
2337 cef_logger,
2338 {
2339 "name": "User Operation",
2340 "sourceUserName": token_info.get("username", None),
2341 "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format(
2342 item,
2343 topic,
2344 url_id,
2345 token_info.get("project_name", None),
2346 ),
2347 "severity": "2",
2348 },
2349 )
2350 cherrypy.log("{}".format(cef_logger))
2351 elif token_info is not None:
2352 cef_event(
2353 cef_logger,
2354 {
2355 "name": "User Operation",
2356 "sourceUserName": token_info.get("username", None),
2357 "message": "{} {} {}, Project={} Outcome=Failure".format(
2358 item,
2359 topic,
2360 url_id,
2361 token_info.get("project_name", None),
2362 ),
2363 "severity": "2",
2364 },
2365 )
2366 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002367 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01002368 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00002369 finally:
2370 if token_info:
2371 self._format_login(token_info)
2372 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
2373 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
2374 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01002375 cherrypy.request.login += ";{}={}".format(
2376 logging_id, outdata[logging_id][:36]
2377 )
tiernoc94c3df2018-02-09 15:38:54 +01002378
2379
tiernoc94c3df2018-02-09 15:38:54 +01002380def _start_service():
2381 """
2382 Callback function called when cherrypy.engine starts
2383 Override configuration with env variables
2384 Set database, storage, message configuration
2385 Init database with admin/admin user password
2386 """
tierno932499c2019-01-28 17:28:10 +00002387 global nbi_server
2388 global subscription_thread
elumalai7802ff82023-04-24 20:38:32 +05302389 global cef_logger
Rahulc72bc8e2023-12-05 11:54:38 +00002390 global current_backend
tiernoc94c3df2018-02-09 15:38:54 +01002391 cherrypy.log.error("Starting osm_nbi")
2392 # update general cherrypy configuration
2393 update_dict = {}
2394
garciadeblas4568a372021-03-24 09:19:48 +01002395 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01002396 for k, v in environ.items():
garciadeblas6d83f8f2023-06-19 22:34:49 +02002397 if k == "OSMNBI_USER_MANAGEMENT":
garciadeblas7a0dace2024-09-17 18:00:50 +02002398 feature_state = v.lower() == "true"
garciadeblas6d83f8f2023-06-19 22:34:49 +02002399 engine_config["authentication"]["user_management"] = feature_state
Adurtid68526d2023-11-14 11:14:53 +00002400 elif k == "OSMNBI_PWD_EXPIRE_DAYS":
2401 pwd_expire_days = int(v)
2402 engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days
2403 elif k == "OSMNBI_MAX_PWD_ATTEMPT":
2404 max_pwd_attempt = int(v)
2405 engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt
2406 elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS":
2407 account_expire_days = int(v)
2408 engine_config["authentication"]["account_expire_days"] = account_expire_days
tiernoc94c3df2018-02-09 15:38:54 +01002409 if not k.startswith("OSMNBI_"):
2410 continue
tiernoe1281182018-05-22 12:24:36 +02002411 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01002412 if not k2:
2413 continue
2414 try:
2415 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01002416 if k == "OSMNBI_STATIC_DIR":
2417 engine_config["/static"]["tools.staticdir.dir"] = v
2418 engine_config["/static"]["tools.staticdir.on"] = True
2419 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
2420 update_dict["server.socket_port"] = int(v)
2421 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
2422 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02002423 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01002424 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002425 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02002426 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002427 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01002428 engine_config[k1][k2] = int(v)
2429 else:
2430 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002431
tiernoc94c3df2018-02-09 15:38:54 +01002432 except ValueError as e:
2433 cherrypy.log.error("Ignoring environ '{}': " + str(e))
2434 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01002435 cherrypy.log(
2436 "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
2437 )
tiernoc94c3df2018-02-09 15:38:54 +01002438
2439 if update_dict:
2440 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02002441 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01002442
2443 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01002444 log_format_simple = (
2445 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
2446 )
2447 log_formatter_simple = logging.Formatter(
2448 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
2449 )
tiernoc94c3df2018-02-09 15:38:54 +01002450 logger_server = logging.getLogger("cherrypy.error")
2451 logger_access = logging.getLogger("cherrypy.access")
2452 logger_cherry = logging.getLogger("cherrypy")
2453 logger_nbi = logging.getLogger("nbi")
2454
tiernof5298be2018-05-16 14:43:57 +02002455 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01002456 file_handler = logging.handlers.RotatingFileHandler(
2457 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
2458 )
tiernoc94c3df2018-02-09 15:38:54 +01002459 file_handler.setFormatter(log_formatter_simple)
2460 logger_cherry.addHandler(file_handler)
2461 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02002462 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01002463 for format_, logger in {
2464 "nbi.server %(filename)s:%(lineno)s": logger_server,
2465 "nbi.access %(filename)s:%(lineno)s": logger_access,
2466 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
2467 }.items():
tiernob24258a2018-10-04 18:39:49 +02002468 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01002469 log_formatter_cherry = logging.Formatter(
2470 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
2471 )
tiernob24258a2018-10-04 18:39:49 +02002472 str_handler = logging.StreamHandler()
2473 str_handler.setFormatter(log_formatter_cherry)
2474 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01002475
tiernof5298be2018-05-16 14:43:57 +02002476 if engine_config["global"].get("log.level"):
2477 logger_cherry.setLevel(engine_config["global"]["log.level"])
2478 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01002479
2480 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01002481 for k1, logname in {
2482 "message": "nbi.msg",
2483 "database": "nbi.db",
2484 "storage": "nbi.fs",
2485 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01002486 engine_config[k1]["logger_name"] = logname
2487 logger_module = logging.getLogger(logname)
2488 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01002489 file_handler = logging.handlers.RotatingFileHandler(
2490 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
2491 )
tiernoc94c3df2018-02-09 15:38:54 +01002492 file_handler.setFormatter(log_formatter_simple)
2493 logger_module.addHandler(file_handler)
2494 if "loglevel" in engine_config[k1]:
2495 logger_module.setLevel(engine_config[k1]["loglevel"])
2496 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01002497 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
2498 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
2499 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
2500 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
2501 target_version=auth_database_version
2502 )
tiernobee508e2019-01-21 11:21:49 +00002503
elumalai7802ff82023-04-24 20:38:32 +05302504 cef_logger = cef_event_builder(engine_config["authentication"])
2505
tierno932499c2019-01-28 17:28:10 +00002506 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01002507 subscription_thread = SubscriptionThread(
2508 config=engine_config, engine=nbi_server.engine
2509 )
tierno932499c2019-01-28 17:28:10 +00002510 subscription_thread.start()
2511 # Do not capture except SubscriptionException
2512
tiernob2e48bd2020-02-04 15:47:18 +00002513 backend = engine_config["authentication"]["backend"]
Rahulc72bc8e2023-12-05 11:54:38 +00002514 current_backend = backend
garciadeblas4568a372021-03-24 09:19:48 +01002515 cherrypy.log.error(
2516 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
2517 nbi_version, nbi_version_date, backend
2518 )
2519 )
tiernoc94c3df2018-02-09 15:38:54 +01002520
2521
2522def _stop_service():
2523 """
2524 Callback function called when cherrypy.engine stops
2525 TODO: Ending database connections.
2526 """
tierno932499c2019-01-28 17:28:10 +00002527 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01002528 if subscription_thread:
2529 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00002530 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01002531 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01002532 cherrypy.log.error("Stopping osm_nbi")
2533
tierno2236d202018-05-16 19:05:16 +02002534
tiernof5298be2018-05-16 14:43:57 +02002535def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00002536 global nbi_server
tierno932499c2019-01-28 17:28:10 +00002537 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01002538 cherrypy.engine.subscribe("start", _start_service)
2539 cherrypy.engine.subscribe("stop", _stop_service)
2540 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02002541
2542
2543def usage():
garciadeblas4568a372021-03-24 09:19:48 +01002544 print(
2545 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02002546 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
2547 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01002548 """.format(
2549 sys.argv[0]
2550 )
2551 )
tierno2236d202018-05-16 19:05:16 +02002552 # --log-socket-host HOST: send logs to this host")
2553 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002554
2555
garciadeblas4568a372021-03-24 09:19:48 +01002556if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002557 try:
2558 # load parameters and configuration
2559 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2560 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2561 config_file = None
2562 for o, a in opts:
2563 if o in ("-h", "--help"):
2564 usage()
2565 sys.exit()
2566 elif o in ("-c", "--config"):
2567 config_file = a
2568 # elif o == "--log-socket-port":
2569 # log_socket_port = a
2570 # elif o == "--log-socket-host":
2571 # log_socket_host = a
2572 # elif o == "--log-file":
2573 # log_file = a
2574 else:
2575 assert False, "Unhandled option"
2576 if config_file:
2577 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002578 print(
2579 "configuration file '{}' that not exist".format(config_file),
2580 file=sys.stderr,
2581 )
tiernof5298be2018-05-16 14:43:57 +02002582 exit(1)
2583 else:
garciadeblas4568a372021-03-24 09:19:48 +01002584 for config_file in (
2585 __file__[: __file__.rfind(".")] + ".cfg",
2586 "./nbi.cfg",
2587 "/etc/osm/nbi.cfg",
2588 ):
tiernof5298be2018-05-16 14:43:57 +02002589 if path.isfile(config_file):
2590 break
2591 else:
garciadeblas4568a372021-03-24 09:19:48 +01002592 print(
2593 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2594 file=sys.stderr,
2595 )
tiernof5298be2018-05-16 14:43:57 +02002596 exit(1)
2597 nbi(config_file)
2598 except getopt.GetoptError as e:
2599 print(str(e), file=sys.stderr)
2600 # usage()
2601 exit(1)