blob: 1c8b035a14f425d63c77a86d4e54a3701740d168 [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 )
jeganbe1a3df2024-06-04 12:05:19 +00001239 if outdata.get("email") or outdata.get("otp") == "invalid":
1240 return self._format_out(outdata, token_info)
garciadeblasf2af4a12023-01-24 16:56:54 +01001241 cherrypy.session["Authorization"] = outdata["_id"] # pylint: disable=E1101
tiernoa5035702019-07-29 08:54:42 +00001242 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1243 # for logging
1244 self._format_login(token_info)
jeganbe1a3df2024-06-04 12:05:19 +00001245 if outdata.get("otp") == "valid":
1246 outdata = {
1247 "id": outdata["id"],
1248 "message": "valid_otp",
1249 "user_id": outdata["user_id"],
1250 }
selvi.ja9a1fc82022-04-04 06:54:30 +00001251 # password expiry check
jeganbe1a3df2024-06-04 12:05:19 +00001252 elif self.authenticator.check_password_expiry(outdata):
garciadeblasf2af4a12023-01-24 16:56:54 +01001253 outdata = {
1254 "id": outdata["id"],
1255 "message": "change_password",
1256 "user_id": outdata["user_id"],
1257 }
tiernoa5035702019-07-29 08:54:42 +00001258 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1259 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
elumalai7802ff82023-04-24 20:38:32 +05301260 cef_event(
1261 cef_logger,
1262 {
1263 "name": "User Login",
1264 "sourceUserName": token_info.get("username"),
1265 "message": "User Logged In, Project={} Outcome=Success".format(
1266 token_info.get("project_name")
1267 ),
1268 },
1269 )
1270 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001271 elif method == "DELETE":
1272 if not token_id and "id" in kwargs:
1273 token_id = kwargs["id"]
1274 elif not token_id:
1275 token_info = self.authenticator.authorize()
1276 # for logging
1277 self._format_login(token_info)
1278 token_id = token_info["_id"]
Rahulc72bc8e2023-12-05 11:54:38 +00001279 if current_backend != "keystone":
1280 token_details = self.engine.db.get_one("tokens", {"_id": token_id})
1281 current_user = token_details.get("username")
1282 current_project = token_details.get("project_name")
1283 else:
1284 current_user = "keystone backend"
1285 current_project = "keystone backend"
tiernoa5035702019-07-29 08:54:42 +00001286 outdata = self.authenticator.del_token(token_id)
1287 token_info = None
garciadeblasf2af4a12023-01-24 16:56:54 +01001288 cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101
elumalai7802ff82023-04-24 20:38:32 +05301289 cef_event(
1290 cef_logger,
1291 {
1292 "name": "User Logout",
1293 "sourceUserName": current_user,
1294 "message": "User Logged Out, Project={} Outcome=Success".format(
1295 current_project
1296 ),
1297 },
1298 )
1299 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001300 # cherrypy.response.cookie["Authorization"] = token_id
1301 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1302 else:
garciadeblas4568a372021-03-24 09:19:48 +01001303 raise NbiException(
1304 "Method {} not allowed for token".format(method),
1305 HTTPStatus.METHOD_NOT_ALLOWED,
1306 )
tiernoa5035702019-07-29 08:54:42 +00001307 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001308
1309 @cherrypy.expose
1310 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001311 if not cherrypy.config.get("server.enable_test") or (
1312 isinstance(cherrypy.config["server.enable_test"], str)
1313 and cherrypy.config["server.enable_test"].lower() == "false"
1314 ):
tierno4836bac2020-01-15 14:41:48 +00001315 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1316 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001317 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001318 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001319 return (
1320 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1321 "sleep/<time>\nmessage/topic\n</pre></html>"
1322 )
tiernof27c79b2018-03-12 17:08:42 +01001323
1324 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001325 try:
1326 # self.engine.load_dbase(cherrypy.request.app.config)
garciadeblasf2af4a12023-01-24 16:56:54 +01001327 pid = self.authenticator.create_admin_project()
1328 self.authenticator.create_admin_user(pid)
tiernoc94c3df2018-02-09 15:38:54 +01001329 return "Done. User 'admin', password 'admin' created"
1330 except Exception:
1331 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1332 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001333 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001334 return cherrypy.lib.static.serve_file(
1335 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1336 "text/plain",
1337 "attachment",
1338 )
tiernof27c79b2018-03-12 17:08:42 +01001339 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001340 f_path = (
1341 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1342 )
tiernof27c79b2018-03-12 17:08:42 +01001343 f = open(f_path, "r")
1344 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001345 return f
tierno55945e72018-04-06 16:40:27 +02001346
tiernof27c79b2018-03-12 17:08:42 +01001347 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001348 deleted_info = self.engine.db.del_list(args[1], kwargs)
1349 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1350 elif len(args) and args[0] == "fs-clear":
1351 if len(args) >= 2:
1352 folders = (args[1],)
1353 else:
1354 folders = self.engine.fs.dir_ls(".")
1355 for folder in folders:
1356 self.engine.fs.file_delete(folder)
1357 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001358 elif args and args[0] == "login":
1359 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001360 cherrypy.response.headers[
1361 "WWW-Authenticate"
1362 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001363 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1364 elif args and args[0] == "login2":
1365 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001366 cherrypy.response.headers[
1367 "WWW-Authenticate"
1368 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001369 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1370 elif args and args[0] == "sleep":
1371 sleep_time = 5
1372 try:
1373 sleep_time = int(args[1])
1374 except Exception:
1375 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1376 return self._format_out("Database already initialized")
1377 thread_info = cherrypy.thread_data
1378 print(thread_info)
1379 time.sleep(sleep_time)
1380 # thread_info
1381 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001382 main_topic = args[1]
1383 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001384 try:
garciadeblas4568a372021-03-24 09:19:48 +01001385 if cherrypy.request.method == "POST":
garciadeblas4cd875d2023-02-14 19:05:34 +01001386 to_send = yaml.safe_load(cherrypy.request.body)
tierno55945e72018-04-06 16:40:27 +02001387 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001388 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001389 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001390 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001391 for k, v in kwargs.items():
garciadeblas4cd875d2023-02-14 19:05:34 +01001392 v_dict = yaml.safe_load(v)
tiernof1509b22020-05-12 14:32:37 +00001393 self.engine.msg.write(main_topic, k, v_dict)
1394 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001395 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001396 return_text += "Error: " + str(e)
1397 return_text += "</pre></html>\n"
1398 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001399
1400 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001401 "<html><pre>\nheaders:\n args: {}\n".format(args)
1402 + " kwargs: {}\n".format(kwargs)
1403 + " headers: {}\n".format(cherrypy.request.headers)
1404 + " path_info: {}\n".format(cherrypy.request.path_info)
1405 + " query_string: {}\n".format(cherrypy.request.query_string)
garciadeblasf2af4a12023-01-24 16:56:54 +01001406 + " session: {}\n".format(cherrypy.session) # pylint: disable=E1101
garciadeblas4568a372021-03-24 09:19:48 +01001407 + " cookie: {}\n".format(cherrypy.request.cookie)
1408 + " method: {}\n".format(cherrypy.request.method)
garciadeblasf2af4a12023-01-24 16:56:54 +01001409 + " session: {}\n".format(
1410 cherrypy.session.get("fieldname") # pylint: disable=E1101
1411 )
garciadeblas4568a372021-03-24 09:19:48 +01001412 + " body:\n"
1413 )
tiernoc94c3df2018-02-09 15:38:54 +01001414 return_text += " length: {}\n".format(cherrypy.request.body.length)
1415 if cherrypy.request.body.length:
1416 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001417 str(
1418 cherrypy.request.body.read(
1419 int(cherrypy.request.headers.get("Content-Length", 0))
1420 )
1421 )
1422 )
tiernoc94c3df2018-02-09 15:38:54 +01001423 if thread_info:
1424 return_text += "thread: {}\n".format(thread_info)
1425 return_text += "</pre></html>"
1426 return return_text
1427
tierno701018c2019-06-25 11:13:14 +00001428 @staticmethod
1429 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001430 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001431 raise NbiException(
1432 "URL must contain at least 'main_topic/version/topic'",
1433 HTTPStatus.METHOD_NOT_ALLOWED,
1434 )
tiernof27c79b2018-03-12 17:08:42 +01001435
tierno701018c2019-06-25 11:13:14 +00001436 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001437 for arg in args:
1438 if arg is None:
1439 break
1440 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001441 raise NbiException(
1442 "URL contains unexpected extra items '{}'".format(arg),
1443 HTTPStatus.METHOD_NOT_ALLOWED,
1444 )
tiernof27c79b2018-03-12 17:08:42 +01001445
1446 if arg in reference:
1447 reference = reference[arg]
1448 elif "<ID>" in reference:
1449 reference = reference["<ID>"]
1450 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001451 # if there is content
1452 if reference["*"]:
1453 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001454 break
1455 else:
garciadeblas4568a372021-03-24 09:19:48 +01001456 raise NbiException(
1457 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1458 )
tiernof27c79b2018-03-12 17:08:42 +01001459 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001460 raise NbiException(
1461 "Method {} not supported yet for this URL".format(method),
1462 HTTPStatus.NOT_IMPLEMENTED,
1463 )
tierno2236d202018-05-16 19:05:16 +02001464 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001465 raise NbiException(
1466 "Method {} not supported for this URL".format(method),
1467 HTTPStatus.METHOD_NOT_ALLOWED,
1468 )
tierno701018c2019-06-25 11:13:14 +00001469 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001470
1471 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001472 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001473 """
1474 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001475 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001476 :param version:
tiernob24258a2018-10-04 18:39:49 +02001477 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001478 :param id:
1479 :return: None
1480 """
1481 # 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 +01001482 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1483 main_topic, version, topic, id
1484 )
tiernof27c79b2018-03-12 17:08:42 +01001485 return
1486
tierno65ca36d2019-02-12 19:27:52 +01001487 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001488 def _extract_query_string_operations(kwargs, method):
1489 """
1490
1491 :param kwargs:
1492 :return:
1493 """
1494 query_string_operations = []
1495 if kwargs:
1496 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1497 if qs in kwargs and kwargs[qs].lower() != "false":
1498 query_string_operations.append(qs.lower() + ":" + method.lower())
1499 return query_string_operations
1500
1501 @staticmethod
1502 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001503 """
1504 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1505 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001506 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001507 :param kwargs: query string input.
1508 :param method: http method: GET, POSST, PUT, ...
1509 :param _id:
1510 :return: admin_query dictionary with keys:
1511 public: True, False or None
1512 force: True or False
1513 project_id: tuple with projects used for accessing an element
1514 set_project: tuple with projects that a created element will belong to
1515 method: show, list, delete, write
1516 """
garciadeblas4568a372021-03-24 09:19:48 +01001517 admin_query = {
1518 "force": False,
1519 "project_id": (token_info["project_id"],),
1520 "username": token_info["username"],
Adurti76d4b762024-05-07 06:04:37 +00001521 "user_id": token_info["user_id"],
garciadeblas4568a372021-03-24 09:19:48 +01001522 "admin": token_info["admin"],
37177091c0322024-11-01 08:55:59 +00001523 "admin_show": token_info["admin_show"],
garciadeblas4568a372021-03-24 09:19:48 +01001524 "public": None,
1525 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1526 }
tierno65ca36d2019-02-12 19:27:52 +01001527 if kwargs:
1528 # FORCE
1529 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001530 if (
1531 kwargs["FORCE"].lower() != "false"
1532 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001533 admin_query["force"] = True
1534 del kwargs["FORCE"]
1535 # PUBLIC
1536 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001537 if (
1538 kwargs["PUBLIC"].lower() != "false"
1539 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001540 admin_query["public"] = True
1541 else:
1542 admin_query["public"] = False
1543 del kwargs["PUBLIC"]
1544 # ADMIN
1545 if "ADMIN" in kwargs:
1546 behave_as = kwargs.pop("ADMIN")
1547 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001548 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001549 raise NbiException(
1550 "Only admin projects can use 'ADMIN' query string",
1551 HTTPStatus.UNAUTHORIZED,
1552 )
1553 if (
1554 not behave_as or behave_as.lower() == "true"
1555 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001556 admin_query["project_id"] = ()
1557 elif isinstance(behave_as, (list, tuple)):
1558 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001559 else: # isinstance(behave_as, str)
1560 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001561 if "SET_PROJECT" in kwargs:
1562 set_project = kwargs.pop("SET_PROJECT")
1563 if not set_project:
1564 admin_query["set_project"] = list(admin_query["project_id"])
1565 else:
1566 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001567 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001568 if admin_query["project_id"]:
1569 for p in set_project:
1570 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001571 raise NbiException(
1572 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1573 "'ADMIN='{p}'".format(p=p),
1574 HTTPStatus.UNAUTHORIZED,
1575 )
tierno65ca36d2019-02-12 19:27:52 +01001576 admin_query["set_project"] = set_project
1577
1578 # PROJECT_READ
1579 # if "PROJECT_READ" in kwargs:
1580 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001581 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001582 if method == "GET":
1583 if _id:
1584 admin_query["method"] = "show"
1585 else:
1586 admin_query["method"] = "list"
1587 elif method == "DELETE":
1588 admin_query["method"] = "delete"
1589 else:
1590 admin_query["method"] = "write"
1591 return admin_query
1592
tiernoc94c3df2018-02-09 15:38:54 +01001593 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001594 def default(
1595 self,
1596 main_topic=None,
1597 version=None,
1598 topic=None,
1599 _id=None,
1600 item=None,
1601 *args,
1602 **kwargs
1603 ):
tierno701018c2019-06-25 11:13:14 +00001604 token_info = None
selvi.j9919bbc2023-04-26 12:22:13 +00001605 outdata = {}
tiernof27c79b2018-03-12 17:08:42 +01001606 _format = None
tierno0f98af52018-03-19 10:28:22 +01001607 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001608 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001609 rollback = []
tierno701018c2019-06-25 11:13:14 +00001610 engine_session = None
elumalai7802ff82023-04-24 20:38:32 +05301611 url_id = ""
1612 log_mapping = {
1613 "POST": "Creating",
1614 "GET": "Fetching",
1615 "DELETE": "Deleting",
1616 "PUT": "Updating",
1617 "PATCH": "Updating",
1618 }
tiernoc94c3df2018-02-09 15:38:54 +01001619 try:
tiernob24258a2018-10-04 18:39:49 +02001620 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001621 raise NbiException(
1622 "URL must contain at least 'main_topic/version/topic'",
1623 HTTPStatus.METHOD_NOT_ALLOWED,
1624 )
1625 if main_topic not in (
1626 "admin",
1627 "vnfpkgm",
1628 "nsd",
1629 "nslcm",
1630 "pdu",
1631 "nst",
1632 "nsilcm",
1633 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001634 "vnflcm",
rshri2d386cb2024-07-05 14:35:51 +00001635 "k8scluster",
yshah53cc9eb2024-07-05 13:06:31 +00001636 "ksu",
1637 "oka",
garciadeblas4568a372021-03-24 09:19:48 +01001638 ):
1639 raise NbiException(
1640 "URL main_topic '{}' not supported".format(main_topic),
1641 HTTPStatus.METHOD_NOT_ALLOWED,
1642 )
1643 if version != "v1":
1644 raise NbiException(
1645 "URL version '{}' not supported".format(version),
1646 HTTPStatus.METHOD_NOT_ALLOWED,
1647 )
elumalai7802ff82023-04-24 20:38:32 +05301648 if _id is not None:
1649 url_id = _id
tiernoc94c3df2018-02-09 15:38:54 +01001650
garciadeblas4568a372021-03-24 09:19:48 +01001651 if (
1652 kwargs
1653 and "METHOD" in kwargs
1654 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1655 ):
tiernof27c79b2018-03-12 17:08:42 +01001656 method = kwargs.pop("METHOD")
1657 else:
1658 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001659
garciadeblas4568a372021-03-24 09:19:48 +01001660 role_permission = self._check_valid_url_method(
1661 method, main_topic, version, topic, _id, item, *args
1662 )
1663 query_string_operations = self._extract_query_string_operations(
1664 kwargs, method
1665 )
tiernob24258a2018-10-04 18:39:49 +02001666 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001667 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001668 token_info = self.authenticator.authorize(
1669 role_permission, query_string_operations, _id
1670 )
tierno12eac3c2020-03-19 23:22:08 +00001671 if main_topic == "admin" and topic == "domains":
1672 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001673 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001674 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001675 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301676
vijay.r35ef2f72019-04-30 17:55:49 +05301677 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001678 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001679
tiernob24258a2018-10-04 18:39:49 +02001680 if main_topic == "nsd":
1681 engine_topic = "nsds"
kayal2001f71c2e82024-06-25 15:26:24 +05301682 if topic == "ns_config_template":
1683 engine_topic = "nsconfigtemps"
tiernob24258a2018-10-04 18:39:49 +02001684 elif main_topic == "vnfpkgm":
1685 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001686 if topic == "vnfpkg_op_occs":
1687 engine_topic = "vnfpkgops"
1688 if topic == "vnf_packages" and item == "action":
1689 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001690 elif main_topic == "nslcm":
1691 engine_topic = "nsrs"
1692 if topic == "ns_lcm_op_occs":
1693 engine_topic = "nslcmops"
1694 if topic == "vnfrs" or topic == "vnf_instances":
1695 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001696 elif main_topic == "vnflcm":
1697 if topic == "vnf_lcm_op_occs":
1698 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001699 elif main_topic == "nst":
1700 engine_topic = "nsts"
1701 elif main_topic == "nsilcm":
1702 engine_topic = "nsis"
1703 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001704 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001705 elif main_topic == "pdu":
1706 engine_topic = "pdus"
rshri2d386cb2024-07-05 14:35:51 +00001707 elif main_topic == "k8scluster":
1708 engine_topic = "k8s"
1709 if topic == "clusters" and _id == "register" or item == "deregister":
1710 engine_topic = "k8sops"
1711 elif topic == "infra_controller_profiles":
1712 engine_topic = "infras_cont"
1713 elif topic == "infra_config_profiles":
1714 engine_topic = "infras_conf"
1715 elif topic == "resource_profiles":
1716 engine_topic = "resources"
1717 elif topic == "app_profiles":
1718 engine_topic = "apps"
yshah53cc9eb2024-07-05 13:06:31 +00001719 elif main_topic == "k8scluster" and item in (
1720 "upgrade",
1721 "get_creds",
1722 "scale",
1723 ):
1724 engine_topic = "k8s"
1725 elif main_topic == "ksu" and engine_topic in ("ksus", "clone", "move"):
1726 engine_topic = "ksus"
garciadeblas4568a372021-03-24 09:19:48 +01001727 if (
1728 engine_topic == "vims"
1729 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001730 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001731
preethika.p329b8182020-04-22 12:25:39 +05301732 if topic == "subscriptions":
1733 engine_topic = main_topic + "_" + topic
1734
tiernoc94c3df2018-02-09 15:38:54 +01001735 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001736 if item in (
1737 "nsd_content",
1738 "package_content",
1739 "artifacts",
1740 "vnfd",
1741 "nsd",
1742 "nst",
1743 "nst_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301744 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001745 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001746 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001747 path = "$DESCRIPTOR"
1748 elif args:
1749 path = args
tiernob24258a2018-10-04 18:39:49 +02001750 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001751 path = ()
1752 else:
1753 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001754 file, _format = self.engine.get_file(
1755 engine_session,
1756 engine_topic,
1757 _id,
1758 path,
1759 cherrypy.request.headers.get("Accept"),
1760 )
tiernof27c79b2018-03-12 17:08:42 +01001761 outdata = file
1762 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001763 outdata = self.engine.get_item_list(
1764 engine_session, engine_topic, kwargs, api_req=True
1765 )
rshri2d386cb2024-07-05 14:35:51 +00001766 elif topic == "clusters" and item in (
1767 "infra_controller_profiles",
1768 "infra_config_profiles",
1769 "app_profiles",
1770 "resource_profiles",
1771 ):
1772 profile = item
1773 filter_q = None
1774 outdata = self.engine.get_one_item(
1775 engine_session,
1776 engine_topic,
1777 _id,
1778 profile,
1779 filter_q,
1780 api_req=True,
1781 )
yshah53cc9eb2024-07-05 13:06:31 +00001782 elif topic == "clusters" and item == "get_creds":
1783 outdata = self.engine.get_cluster_info(
1784 engine_session, engine_topic, _id, item
1785 )
tiernoc94c3df2018-02-09 15:38:54 +01001786 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301787 if item == "reports":
1788 # TODO check that project_id (_id in this context) has permissions
1789 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301790 filter_q = None
1791 if "vcaStatusRefresh" in kwargs:
1792 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
garciadeblasf2af4a12023-01-24 16:56:54 +01001793 outdata = self.engine.get_item(
1794 engine_session, engine_topic, _id, filter_q, True
1795 )
delacruzramo271d2002019-12-02 21:00:37 +01001796
tiernof27c79b2018-03-12 17:08:42 +01001797 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001798 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001799 if topic in (
1800 "ns_descriptors_content",
1801 "vnf_packages_content",
1802 "netslice_templates_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301803 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001804 ):
tiernof27c79b2018-03-12 17:08:42 +01001805 _id = cherrypy.request.headers.get("Transaction-Id")
yshah53cc9eb2024-07-05 13:06:31 +00001806
tiernof27c79b2018-03-12 17:08:42 +01001807 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001808 _id, _ = self.engine.new_item(
1809 rollback,
1810 engine_session,
1811 engine_topic,
1812 {},
1813 None,
1814 cherrypy.request.headers,
1815 )
1816 completed = self.engine.upload_content(
1817 engine_session,
1818 engine_topic,
1819 _id,
1820 indata,
1821 kwargs,
1822 cherrypy.request.headers,
1823 )
tiernof27c79b2018-03-12 17:08:42 +01001824 if completed:
tiernob24258a2018-10-04 18:39:49 +02001825 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001826 else:
1827 cherrypy.response.headers["Transaction-Id"] = _id
1828 outdata = {"id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001829 elif topic == "oka_packages":
1830 _id = cherrypy.request.headers.get("Transaction-Id")
1831
1832 if not _id:
1833 _id, _ = self.engine.new_item(
1834 rollback,
1835 engine_session,
1836 engine_topic,
1837 {},
1838 kwargs,
1839 cherrypy.request.headers,
1840 )
1841 cherrypy.request.headers["method"] = cherrypy.request.method
1842 if indata:
1843 completed = self.engine.upload_content(
1844 engine_session,
1845 engine_topic,
1846 _id,
1847 indata,
1848 None,
1849 cherrypy.request.headers,
1850 )
1851 if completed:
1852 self._set_location_header(main_topic, version, topic, _id)
1853 else:
1854 cherrypy.response.headers["Transaction-Id"] = _id
1855 outdata = {"_id": _id}
tiernob24258a2018-10-04 18:39:49 +02001856 elif topic == "ns_instances_content":
1857 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001858 _id, _ = self.engine.new_item(
1859 rollback, engine_session, engine_topic, indata, kwargs
1860 )
tiernob24258a2018-10-04 18:39:49 +02001861 # creates nslcmop
1862 indata["lcmOperationType"] = "instantiate"
1863 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001864 nslcmop_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001865 rollback, engine_session, "nslcmops", indata, None
1866 )
tiernob24258a2018-10-04 18:39:49 +02001867 self._set_location_header(main_topic, version, topic, _id)
garciadeblasf53612b2024-07-12 14:44:37 +02001868 outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName}
adurti3af50952024-05-31 11:36:57 +05301869 elif topic == "ns_instances_terminate":
1870 if indata.get("ns_ids"):
1871 for ns_id in indata.get("ns_ids"):
1872 nslcmop_desc = {
1873 "lcmOperationType": "terminate",
1874 "nsInstanceId": ns_id,
1875 "autoremove": indata.get("autoremove")
1876 if "autoremove" in indata
1877 else True,
1878 }
1879 op_id, _, _ = self.engine.new_item(
1880 rollback,
1881 engine_session,
1882 "nslcmops",
1883 nslcmop_desc,
1884 kwargs,
1885 )
1886 if not op_id:
1887 _ = self.engine.del_item(
1888 engine_session, engine_topic, ns_id
1889 )
1890 outdata = {"ns_ids": indata.get("ns_ids")}
1891 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001892 elif topic == "ns_instances" and item:
1893 indata["lcmOperationType"] = item
1894 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001895 _id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001896 rollback, engine_session, "nslcmops", indata, kwargs
1897 )
1898 self._set_location_header(
1899 main_topic, version, "ns_lcm_op_occs", _id
1900 )
garciadeblasf53612b2024-07-12 14:44:37 +02001901 outdata = {"id": _id, "nsName": nsName}
tierno65acb4d2018-04-06 16:42:40 +02001902 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001903 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001904 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001905 _id, _ = self.engine.new_item(
1906 rollback, engine_session, engine_topic, indata, kwargs
1907 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001908 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001909 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001910 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001911 nsilcmop_id, _ = self.engine.new_item(
1912 rollback, engine_session, "nsilcmops", indata, kwargs
1913 )
kuuse078f55e2019-05-16 19:24:21 +02001914 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001915 elif topic == "netslice_instances" and item:
1916 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001917 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001918 _id, _ = self.engine.new_item(
1919 rollback, engine_session, "nsilcmops", indata, kwargs
1920 )
1921 self._set_location_header(
1922 main_topic, version, "nsi_lcm_op_occs", _id
1923 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001924 outdata = {"id": _id}
1925 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001926 elif topic == "vnf_packages" and item == "action":
1927 indata["lcmOperationType"] = item
1928 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001929 _id, _ = self.engine.new_item(
1930 rollback, engine_session, "vnfpkgops", indata, kwargs
1931 )
1932 self._set_location_header(
1933 main_topic, version, "vnfpkg_op_occs", _id
1934 )
delacruzramo271d2002019-12-02 21:00:37 +01001935 outdata = {"id": _id}
1936 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301937 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001938 _id, _ = self.engine.new_item(
1939 rollback, engine_session, engine_topic, indata, kwargs
1940 )
preethika.p329b8182020-04-22 12:25:39 +05301941 self._set_location_header(main_topic, version, topic, _id)
1942 link = {}
1943 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001944 outdata = {
1945 "id": _id,
1946 "filter": indata["filter"],
1947 "callbackUri": indata["CallbackUri"],
1948 "_links": link,
1949 }
preethika.p329b8182020-04-22 12:25:39 +05301950 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001951 elif topic == "vnf_instances" and item:
1952 indata["lcmOperationType"] = item
1953 indata["vnfInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001954 _id, nsName, _ = self.engine.new_item(
garciadeblasf2af4a12023-01-24 16:56:54 +01001955 rollback, engine_session, "vnflcmops", indata, kwargs
1956 )
1957 self._set_location_header(
1958 main_topic, version, "vnf_lcm_op_occs", _id
1959 )
garciadeblasf53612b2024-07-12 14:44:37 +02001960 outdata = {"id": _id, "nsName": nsName}
almagiae47b9132022-05-17 14:12:22 +02001961 cherrypy.response.status = HTTPStatus.ACCEPTED.value
Gabriel Cuba84a60df2023-10-30 14:01:54 -05001962 elif topic == "ns_lcm_op_occs" and item == "cancel":
1963 indata["nsLcmOpOccId"] = _id
1964 self.engine.cancel_item(
1965 rollback, engine_session, "nslcmops", indata, None
1966 )
1967 self._set_location_header(main_topic, version, topic, _id)
1968 cherrypy.response.status = HTTPStatus.ACCEPTED.value
rshri2d386cb2024-07-05 14:35:51 +00001969 elif topic == "clusters" and _id == "register":
1970 # To register a cluster
1971 _id, _ = self.engine.add_item(
1972 rollback, engine_session, engine_topic, indata, kwargs
1973 )
1974 self._set_location_header(main_topic, version, topic, _id)
1975 outdata = {"id": _id}
1976 elif (
1977 topic
1978 in (
1979 "clusters",
1980 "infra_controller_profiles",
1981 "infra_config_profiles",
1982 "app_profiles",
1983 "resource_profiles",
1984 )
1985 and item is None
1986 ):
1987 # creates cluster, infra_controller_profiles, app_profiles, infra_config_profiles, and resource_profiles
1988 _id, _ = self.engine.new_item(
1989 rollback, engine_session, engine_topic, indata, kwargs
1990 )
1991 self._set_location_header(main_topic, version, topic, _id)
1992 outdata = {"_id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001993 elif topic == "ksus" and item:
1994 if item == "clone":
1995 _id = self.engine.clone(
1996 rollback,
1997 engine_session,
1998 engine_topic,
1999 _id,
2000 indata,
2001 kwargs,
2002 cherrypy.request.headers,
2003 )
2004 self._set_location_header(main_topic, version, topic, _id)
2005 outdata = {"id": _id}
2006 if item == "move":
2007 op_id = self.engine.move_ksu(
2008 engine_session, engine_topic, _id, indata, kwargs
2009 )
2010 outdata = {"op_id": op_id}
2011 elif topic == "ksus" and _id == "delete":
2012 op_id = self.engine.delete_ksu(
2013 engine_session, engine_topic, _id, indata
2014 )
2015 outdata = {"op_id": op_id}
2016 elif topic == "ksus" and _id == "update":
2017 op_id = self.engine.edit_item(
2018 engine_session, engine_topic, _id, indata, kwargs
2019 )
2020 outdata = {"op_id": op_id}
2021 elif topic == "clusters" and item in ("upgrade", "scale"):
2022 op_id = self.engine.update_cluster(
2023 engine_session, engine_topic, _id, item, indata
2024 )
2025 outdata = {"op_id": op_id}
tiernof27c79b2018-03-12 17:08:42 +01002026 else:
garciadeblas4568a372021-03-24 09:19:48 +01002027 _id, op_id = self.engine.new_item(
2028 rollback,
2029 engine_session,
2030 engine_topic,
2031 indata,
2032 kwargs,
2033 cherrypy.request.headers,
2034 )
tiernob24258a2018-10-04 18:39:49 +02002035 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01002036 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00002037 if op_id:
2038 outdata["op_id"] = op_id
2039 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02002040 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02002041
tiernoc94c3df2018-02-09 15:38:54 +01002042 elif method == "DELETE":
2043 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01002044 outdata = self.engine.del_item_list(
2045 engine_session, engine_topic, kwargs
2046 )
tierno09c073e2018-04-26 13:36:48 +02002047 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01002048 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00002049 # for NS NSI generate an operation
2050 op_id = None
tierno701018c2019-06-25 11:13:14 +00002051 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02002052 nslcmop_desc = {
2053 "lcmOperationType": "terminate",
2054 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002055 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02002056 }
garciadeblasf53612b2024-07-12 14:44:37 +02002057 op_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01002058 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
2059 )
tierno22577432020-04-08 15:16:57 +00002060 if op_id:
garciadeblasf53612b2024-07-12 14:44:37 +02002061 outdata = {"_id": op_id, "nsName": nsName}
garciadeblas4568a372021-03-24 09:19:48 +01002062 elif (
2063 topic == "netslice_instances_content"
2064 and not engine_session["force"]
2065 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02002066 nsilcmop_desc = {
2067 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02002068 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002069 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02002070 }
garciadeblas4568a372021-03-24 09:19:48 +01002071 op_id, _ = self.engine.new_item(
2072 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
2073 )
tierno22577432020-04-08 15:16:57 +00002074 if op_id:
2075 outdata = {"_id": op_id}
rshri2d386cb2024-07-05 14:35:51 +00002076 elif topic == "clusters" and item == "deregister":
2077 if not op_id:
2078 op_id = self.engine.remove(
2079 engine_session, engine_topic, _id
2080 )
2081 if op_id:
2082 outdata = {"_id": op_id}
2083 cherrypy.response.status = (
2084 HTTPStatus.ACCEPTED.value
2085 if op_id
2086 else HTTPStatus.NO_CONTENT.value
2087 )
yshah53cc9eb2024-07-05 13:06:31 +00002088 elif topic == "ksus":
2089 op_id = self.engine.delete_ksu(
2090 engine_session, engine_topic, _id, indata
2091 )
2092 outdata = {"op_id": op_id}
tierno22577432020-04-08 15:16:57 +00002093 # if there is not any deletion in process, delete
rshri2d386cb2024-07-05 14:35:51 +00002094 elif not op_id:
tierno22577432020-04-08 15:16:57 +00002095 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2096 if op_id:
rshri2d386cb2024-07-05 14:35:51 +00002097 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01002098 cherrypy.response.status = (
2099 HTTPStatus.ACCEPTED.value
2100 if op_id
2101 else HTTPStatus.NO_CONTENT.value
2102 )
tierno09c073e2018-04-26 13:36:48 +02002103
tierno7ae10112018-05-18 14:36:02 +02002104 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00002105 op_id = None
tierno701018c2019-06-25 11:13:14 +00002106 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01002107 raise NbiException(
2108 "Nothing to update. Provide payload and/or query string",
2109 HTTPStatus.BAD_REQUEST,
2110 )
2111 if (
kayal2001f71c2e82024-06-25 15:26:24 +05302112 item
2113 in (
2114 "nsd_content",
2115 "package_content",
2116 "nst_content",
2117 "template_content",
2118 )
garciadeblas4568a372021-03-24 09:19:48 +01002119 and method == "PUT"
2120 ):
2121 completed = self.engine.upload_content(
2122 engine_session,
2123 engine_topic,
2124 _id,
2125 indata,
2126 kwargs,
2127 cherrypy.request.headers,
2128 )
tiernof27c79b2018-03-12 17:08:42 +01002129 if not completed:
2130 cherrypy.response.headers["Transaction-Id"] = id
rshri2d386cb2024-07-05 14:35:51 +00002131 elif item in (
2132 "app_profiles",
2133 "resource_profiles",
2134 "infra_controller_profiles",
2135 "infra_config_profiles",
2136 ):
2137 op_id = self.engine.edit(
2138 engine_session, engine_topic, _id, item, indata, kwargs
2139 )
yshah53cc9eb2024-07-05 13:06:31 +00002140 elif topic == "oka_packages" and method == "PATCH":
2141 if kwargs:
2142 op_id = self.engine.edit_item(
2143 engine_session, engine_topic, _id, None, kwargs
2144 )
2145 if indata:
yshahffcac5f2024-08-19 12:49:07 +00002146 if isinstance(indata, dict):
yshah53cc9eb2024-07-05 13:06:31 +00002147 op_id = self.engine.edit_item(
2148 engine_session, engine_topic, _id, indata, kwargs
2149 )
2150 else:
2151 cherrypy.request.headers["method"] = cherrypy.request.method
2152 completed = self.engine.upload_content(
2153 engine_session,
2154 engine_topic,
2155 _id,
2156 indata,
2157 {},
2158 cherrypy.request.headers,
2159 )
2160 if not completed:
2161 cherrypy.response.headers["Transaction-Id"] = id
2162 elif topic == "oka_packages" and method == "PUT":
2163 if indata:
2164 cherrypy.request.headers["method"] = cherrypy.request.method
2165 completed = self.engine.upload_content(
2166 engine_session,
2167 engine_topic,
2168 _id,
2169 indata,
2170 {},
2171 cherrypy.request.headers,
2172 )
2173 if not completed:
2174 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01002175 else:
garciadeblas4568a372021-03-24 09:19:48 +01002176 op_id = self.engine.edit_item(
2177 engine_session, engine_topic, _id, indata, kwargs
2178 )
tiernobdebce92019-07-01 15:36:49 +00002179
2180 if op_id:
2181 cherrypy.response.status = HTTPStatus.ACCEPTED.value
2182 outdata = {"op_id": op_id}
2183 else:
2184 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
2185 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01002186 else:
garciadeblas4568a372021-03-24 09:19:48 +01002187 raise NbiException(
2188 "Method {} not allowed".format(method),
2189 HTTPStatus.METHOD_NOT_ALLOWED,
2190 )
tiernoa6bb45d2019-06-14 09:45:39 +00002191
2192 # if Role information changes, it is needed to reload the information of roles
2193 if topic == "roles" and method != "GET":
2194 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01002195
garciadeblas4568a372021-03-24 09:19:48 +01002196 if (
2197 topic == "projects"
2198 and method == "DELETE"
2199 or topic in ["users", "roles"]
2200 and method in ["PUT", "PATCH", "DELETE"]
2201 ):
delacruzramoad682a52019-12-10 16:26:34 +01002202 self.authenticator.remove_token_from_cache()
2203
garciadeblasf53612b2024-07-12 14:44:37 +02002204 cef_event(
2205 cef_logger,
2206 {
2207 "name": "User Operation",
2208 "sourceUserName": token_info.get("username"),
2209 },
2210 )
2211 if topic == "ns_instances_content" and url_id:
2212 nsName = (
2213 outdata.get("name") if method == "GET" else outdata.get("nsName")
2214 )
elumalai7802ff82023-04-24 20:38:32 +05302215 cef_event(
2216 cef_logger,
2217 {
garciadeblasf53612b2024-07-12 14:44:37 +02002218 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2219 log_mapping[method],
2220 topic,
2221 nsName,
2222 outdata.get("id"),
2223 token_info.get("project_name"),
2224 ),
2225 },
2226 )
2227 cherrypy.log("{}".format(cef_logger))
2228 elif topic == "ns_instances_content" and method == "POST":
2229 cef_event(
2230 cef_logger,
2231 {
2232 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2233 log_mapping[method],
2234 topic,
2235 outdata.get("nsName"),
2236 outdata.get("id"),
2237 token_info.get("project_name"),
2238 ),
2239 },
2240 )
2241 cherrypy.log("{}".format(cef_logger))
2242 elif topic in ("ns_instances", "vnf_instances") and item:
2243 cef_event(
2244 cef_logger,
2245 {
2246 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2247 log_mapping[method],
2248 topic,
2249 outdata.get("nsName"),
2250 url_id,
2251 token_info.get("project_name"),
2252 ),
2253 },
2254 )
2255 cherrypy.log("{}".format(cef_logger))
2256 elif item is not None:
2257 cef_event(
2258 cef_logger,
2259 {
elumalai7802ff82023-04-24 20:38:32 +05302260 "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format(
2261 item,
2262 topic,
2263 url_id,
2264 token_info.get("project_name"),
2265 ),
2266 },
2267 )
2268 cherrypy.log("{}".format(cef_logger))
2269 else:
2270 cef_event(
2271 cef_logger,
2272 {
elumalai7802ff82023-04-24 20:38:32 +05302273 "message": "{} {} {}, Project={} Outcome=Success".format(
2274 log_mapping[method],
2275 topic,
2276 url_id,
2277 token_info.get("project_name"),
2278 ),
2279 },
2280 )
2281 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002282 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02002283 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01002284 if isinstance(
2285 e,
2286 (
2287 NbiException,
2288 EngineException,
2289 DbException,
2290 FsException,
2291 MsgException,
2292 AuthException,
2293 ValidationError,
2294 AuthconnException,
2295 ),
2296 ):
tiernob24258a2018-10-04 18:39:49 +02002297 http_code_value = cherrypy.response.status = e.http_code.value
2298 http_code_name = e.http_code.name
2299 cherrypy.log("Exception {}".format(e))
2300 else:
garciadeblas4568a372021-03-24 09:19:48 +01002301 http_code_value = (
2302 cherrypy.response.status
2303 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01002304 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02002305 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02002306 if hasattr(outdata, "close"): # is an open file
2307 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02002308 error_text = str(e)
2309 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02002310 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02002311 try:
tiernocc103432018-10-19 14:10:35 +02002312 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01002313 self.engine.db.set_one(
2314 rollback_item["topic"],
2315 {"_id": rollback_item["_id"]},
2316 rollback_item["content"],
2317 fail_on_empty=False,
2318 )
preethika.p329b8182020-04-22 12:25:39 +05302319 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01002320 self.engine.db.del_list(
2321 rollback_item["topic"],
2322 rollback_item["filter"],
garciadeblas4568a372021-03-24 09:19:48 +01002323 )
tiernocc103432018-10-19 14:10:35 +02002324 else:
garciadeblas4568a372021-03-24 09:19:48 +01002325 self.engine.db.del_one(
2326 rollback_item["topic"],
2327 {"_id": rollback_item["_id"]},
2328 fail_on_empty=False,
2329 )
tierno3ace63c2018-05-03 17:51:43 +02002330 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01002331 rollback_error_text = "Rollback Exception {}: {}".format(
2332 rollback_item, e2
2333 )
tiernob24258a2018-10-04 18:39:49 +02002334 cherrypy.log(rollback_error_text)
2335 error_text += ". " + rollback_error_text
2336 # if isinstance(e, MsgException):
2337 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
2338 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01002339 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02002340 "code": http_code_name,
2341 "status": http_code_value,
2342 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01002343 }
elumalai7802ff82023-04-24 20:38:32 +05302344 if item is not None and token_info is not None:
2345 cef_event(
2346 cef_logger,
2347 {
2348 "name": "User Operation",
2349 "sourceUserName": token_info.get("username", None),
2350 "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format(
2351 item,
2352 topic,
2353 url_id,
2354 token_info.get("project_name", None),
2355 ),
2356 "severity": "2",
2357 },
2358 )
2359 cherrypy.log("{}".format(cef_logger))
2360 elif token_info is not None:
2361 cef_event(
2362 cef_logger,
2363 {
2364 "name": "User Operation",
2365 "sourceUserName": token_info.get("username", None),
2366 "message": "{} {} {}, Project={} Outcome=Failure".format(
2367 item,
2368 topic,
2369 url_id,
2370 token_info.get("project_name", None),
2371 ),
2372 "severity": "2",
2373 },
2374 )
2375 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002376 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01002377 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00002378 finally:
2379 if token_info:
2380 self._format_login(token_info)
2381 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
2382 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
2383 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01002384 cherrypy.request.login += ";{}={}".format(
2385 logging_id, outdata[logging_id][:36]
2386 )
tiernoc94c3df2018-02-09 15:38:54 +01002387
2388
tiernoc94c3df2018-02-09 15:38:54 +01002389def _start_service():
2390 """
2391 Callback function called when cherrypy.engine starts
2392 Override configuration with env variables
2393 Set database, storage, message configuration
2394 Init database with admin/admin user password
2395 """
tierno932499c2019-01-28 17:28:10 +00002396 global nbi_server
2397 global subscription_thread
elumalai7802ff82023-04-24 20:38:32 +05302398 global cef_logger
Rahulc72bc8e2023-12-05 11:54:38 +00002399 global current_backend
tiernoc94c3df2018-02-09 15:38:54 +01002400 cherrypy.log.error("Starting osm_nbi")
2401 # update general cherrypy configuration
2402 update_dict = {}
2403
garciadeblas4568a372021-03-24 09:19:48 +01002404 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01002405 for k, v in environ.items():
garciadeblas6d83f8f2023-06-19 22:34:49 +02002406 if k == "OSMNBI_USER_MANAGEMENT":
garciadeblas7a0dace2024-09-17 18:00:50 +02002407 feature_state = v.lower() == "true"
garciadeblas6d83f8f2023-06-19 22:34:49 +02002408 engine_config["authentication"]["user_management"] = feature_state
Adurtid68526d2023-11-14 11:14:53 +00002409 elif k == "OSMNBI_PWD_EXPIRE_DAYS":
2410 pwd_expire_days = int(v)
2411 engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days
2412 elif k == "OSMNBI_MAX_PWD_ATTEMPT":
2413 max_pwd_attempt = int(v)
2414 engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt
2415 elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS":
2416 account_expire_days = int(v)
2417 engine_config["authentication"]["account_expire_days"] = account_expire_days
jeganbe1a3df2024-06-04 12:05:19 +00002418 elif k == "OSMNBI_SMTP_SERVER":
2419 engine_config["authentication"]["smtp_server"] = v
2420 engine_config["authentication"]["all"] = environ
2421 elif k == "OSMNBI_SMTP_PORT":
2422 port = int(v)
2423 engine_config["authentication"]["smtp_port"] = port
2424 elif k == "OSMNBI_SENDER_EMAIL":
2425 engine_config["authentication"]["sender_email"] = v
2426 elif k == "OSMNBI_EMAIL_PASSWORD":
2427 engine_config["authentication"]["sender_password"] = v
2428 elif k == "OSMNBI_OTP_RETRY_COUNT":
2429 otp_retry_count = int(v)
2430 engine_config["authentication"]["retry_count"] = otp_retry_count
2431 elif k == "OSMNBI_OTP_EXPIRY_TIME":
2432 otp_expiry_time = int(v)
2433 engine_config["authentication"]["otp_expiry_time"] = otp_expiry_time
tiernoc94c3df2018-02-09 15:38:54 +01002434 if not k.startswith("OSMNBI_"):
2435 continue
tiernoe1281182018-05-22 12:24:36 +02002436 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01002437 if not k2:
2438 continue
2439 try:
2440 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01002441 if k == "OSMNBI_STATIC_DIR":
2442 engine_config["/static"]["tools.staticdir.dir"] = v
2443 engine_config["/static"]["tools.staticdir.on"] = True
2444 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
2445 update_dict["server.socket_port"] = int(v)
2446 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
2447 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02002448 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01002449 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002450 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02002451 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002452 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01002453 engine_config[k1][k2] = int(v)
2454 else:
2455 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002456
tiernoc94c3df2018-02-09 15:38:54 +01002457 except ValueError as e:
2458 cherrypy.log.error("Ignoring environ '{}': " + str(e))
2459 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01002460 cherrypy.log(
2461 "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
2462 )
tiernoc94c3df2018-02-09 15:38:54 +01002463
2464 if update_dict:
2465 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02002466 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01002467
2468 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01002469 log_format_simple = (
2470 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
2471 )
2472 log_formatter_simple = logging.Formatter(
2473 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
2474 )
tiernoc94c3df2018-02-09 15:38:54 +01002475 logger_server = logging.getLogger("cherrypy.error")
2476 logger_access = logging.getLogger("cherrypy.access")
2477 logger_cherry = logging.getLogger("cherrypy")
2478 logger_nbi = logging.getLogger("nbi")
2479
tiernof5298be2018-05-16 14:43:57 +02002480 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01002481 file_handler = logging.handlers.RotatingFileHandler(
2482 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
2483 )
tiernoc94c3df2018-02-09 15:38:54 +01002484 file_handler.setFormatter(log_formatter_simple)
2485 logger_cherry.addHandler(file_handler)
2486 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02002487 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01002488 for format_, logger in {
2489 "nbi.server %(filename)s:%(lineno)s": logger_server,
2490 "nbi.access %(filename)s:%(lineno)s": logger_access,
2491 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
2492 }.items():
tiernob24258a2018-10-04 18:39:49 +02002493 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01002494 log_formatter_cherry = logging.Formatter(
2495 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
2496 )
tiernob24258a2018-10-04 18:39:49 +02002497 str_handler = logging.StreamHandler()
2498 str_handler.setFormatter(log_formatter_cherry)
2499 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01002500
tiernof5298be2018-05-16 14:43:57 +02002501 if engine_config["global"].get("log.level"):
2502 logger_cherry.setLevel(engine_config["global"]["log.level"])
2503 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01002504
2505 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01002506 for k1, logname in {
2507 "message": "nbi.msg",
2508 "database": "nbi.db",
2509 "storage": "nbi.fs",
2510 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01002511 engine_config[k1]["logger_name"] = logname
2512 logger_module = logging.getLogger(logname)
2513 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01002514 file_handler = logging.handlers.RotatingFileHandler(
2515 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
2516 )
tiernoc94c3df2018-02-09 15:38:54 +01002517 file_handler.setFormatter(log_formatter_simple)
2518 logger_module.addHandler(file_handler)
2519 if "loglevel" in engine_config[k1]:
2520 logger_module.setLevel(engine_config[k1]["loglevel"])
2521 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01002522 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
2523 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
2524 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
2525 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
2526 target_version=auth_database_version
2527 )
tiernobee508e2019-01-21 11:21:49 +00002528
elumalai7802ff82023-04-24 20:38:32 +05302529 cef_logger = cef_event_builder(engine_config["authentication"])
2530
tierno932499c2019-01-28 17:28:10 +00002531 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01002532 subscription_thread = SubscriptionThread(
2533 config=engine_config, engine=nbi_server.engine
2534 )
tierno932499c2019-01-28 17:28:10 +00002535 subscription_thread.start()
2536 # Do not capture except SubscriptionException
2537
tiernob2e48bd2020-02-04 15:47:18 +00002538 backend = engine_config["authentication"]["backend"]
Rahulc72bc8e2023-12-05 11:54:38 +00002539 current_backend = backend
garciadeblas4568a372021-03-24 09:19:48 +01002540 cherrypy.log.error(
2541 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
2542 nbi_version, nbi_version_date, backend
2543 )
2544 )
tiernoc94c3df2018-02-09 15:38:54 +01002545
2546
2547def _stop_service():
2548 """
2549 Callback function called when cherrypy.engine stops
2550 TODO: Ending database connections.
2551 """
tierno932499c2019-01-28 17:28:10 +00002552 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01002553 if subscription_thread:
2554 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00002555 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01002556 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01002557 cherrypy.log.error("Stopping osm_nbi")
2558
tierno2236d202018-05-16 19:05:16 +02002559
tiernof5298be2018-05-16 14:43:57 +02002560def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00002561 global nbi_server
tierno932499c2019-01-28 17:28:10 +00002562 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01002563 cherrypy.engine.subscribe("start", _start_service)
2564 cherrypy.engine.subscribe("stop", _stop_service)
2565 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02002566
2567
2568def usage():
garciadeblas4568a372021-03-24 09:19:48 +01002569 print(
2570 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02002571 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
2572 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01002573 """.format(
2574 sys.argv[0]
2575 )
2576 )
tierno2236d202018-05-16 19:05:16 +02002577 # --log-socket-host HOST: send logs to this host")
2578 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002579
2580
garciadeblas4568a372021-03-24 09:19:48 +01002581if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002582 try:
2583 # load parameters and configuration
2584 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2585 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2586 config_file = None
2587 for o, a in opts:
2588 if o in ("-h", "--help"):
2589 usage()
2590 sys.exit()
2591 elif o in ("-c", "--config"):
2592 config_file = a
2593 # elif o == "--log-socket-port":
2594 # log_socket_port = a
2595 # elif o == "--log-socket-host":
2596 # log_socket_host = a
2597 # elif o == "--log-file":
2598 # log_file = a
2599 else:
2600 assert False, "Unhandled option"
2601 if config_file:
2602 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002603 print(
2604 "configuration file '{}' that not exist".format(config_file),
2605 file=sys.stderr,
2606 )
tiernof5298be2018-05-16 14:43:57 +02002607 exit(1)
2608 else:
garciadeblas4568a372021-03-24 09:19:48 +01002609 for config_file in (
2610 __file__[: __file__.rfind(".")] + ".cfg",
2611 "./nbi.cfg",
2612 "/etc/osm/nbi.cfg",
2613 ):
tiernof5298be2018-05-16 14:43:57 +02002614 if path.isfile(config_file):
2615 break
2616 else:
garciadeblas4568a372021-03-24 09:19:48 +01002617 print(
2618 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2619 file=sys.stderr,
2620 )
tiernof5298be2018-05-16 14:43:57 +02002621 exit(1)
2622 nbi(config_file)
2623 except getopt.GetoptError as e:
2624 print(str(e), file=sys.stderr)
2625 # usage()
2626 exit(1)