blob: 368074abfe81927722fcda63b53b53b71059e2e3 [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 },
shahithyab9eb4142024-10-17 05:51:39 +0000714 "get_creds_file": {
715 "METHODS": ("GET",),
716 "ROLE_PERMISSION": "k8scluster:id:get_creds_file:",
717 "<ID>": {
718 "METHODS": ("GET",),
719 "ROLE_PERMISSION": "k8scluster:id:get_creds_file:id",
720 },
721 },
yshah53cc9eb2024-07-05 13:06:31 +0000722 "scale": {
723 "METHODS": ("POST",),
724 "ROLE_PERMISSION": "k8scluster:id:scale:",
725 },
726 "upgrade": {
727 "METHODS": ("POST",),
728 "ROLE_PERMISSION": "k8scluster:id:upgrade:",
729 },
rshri2d386cb2024-07-05 14:35:51 +0000730 },
731 "register": {
732 "METHODS": ("POST",),
733 "ROLE_PERMISSION": "k8scluster:register:",
734 },
735 },
736 "app_profiles": {
737 "METHODS": ("POST", "GET"),
738 "ROLE_PERMISSION": "k8scluster:app_profiles:",
739 "<ID>": {
740 "METHODS": ("GET", "PATCH", "DELETE"),
741 "ROLE_PERMISSION": "k8scluster:app_profiles:id:",
742 },
743 },
744 "infra_controller_profiles": {
745 "METHODS": ("POST", "GET"),
746 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:",
747 "<ID>": {
748 "METHODS": ("GET", "PATCH", "DELETE"),
749 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:id:",
750 },
751 },
752 "infra_config_profiles": {
753 "METHODS": ("POST", "GET"),
754 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:",
755 "<ID>": {
756 "METHODS": ("GET", "PATCH", "DELETE"),
757 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:id:",
758 },
759 },
760 "resource_profiles": {
761 "METHODS": ("POST", "GET"),
762 "ROLE_PERMISSION": "k8scluster:resource_profiles:",
763 "<ID>": {
764 "METHODS": ("GET", "PATCH", "DELETE"),
765 "ROLE_PERMISSION": "k8scluster:resource_profiles:id:",
766 },
767 },
768 }
769 },
yshah53cc9eb2024-07-05 13:06:31 +0000770 "ksu": {
771 "v1": {
772 "ksus": {
773 "METHODS": ("GET", "POST"),
774 "ROLE_PERMISSION": "ksu:",
775 "<ID>": {
776 "METHODS": ("GET", "PATCH", "DELETE"),
777 "ROLE_PERMISSION": "ksu:id:",
778 "clone": {
779 "METHODS": ("POST",),
780 "ROLE_PERMISSION": "ksu:id:clone:",
781 },
782 "move": {
783 "METHODS": ("POST",),
784 "ROLE_PERMISSION": "ksu:id:move:",
785 },
786 },
787 "update": {
788 "METHODS": ("POST",),
789 "ROLE_PERMISSION": "ksu:",
790 },
791 "delete": {
792 "METHODS": ("POST",),
793 "ROLE_PERMISSION": "ksu:",
794 },
795 },
796 }
797 },
798 "oka": {
799 "v1": {
800 "oka_packages": {
801 "METHODS": ("GET", "POST"),
802 "ROLE_PERMISSION": "oka_pkg:",
803 "<ID>": {
804 "METHODS": ("GET", "PATCH", "DELETE", "PUT"),
805 "ROLE_PERMISSION": "oka_pkg:id:",
806 },
807 }
808 }
809 },
tierno701018c2019-06-25 11:13:14 +0000810}
811
tiernoc94c3df2018-02-09 15:38:54 +0100812
813class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100814 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
815 Exception.__init__(self, message)
816 self.http_code = http_code
817
818
819class Server(object):
820 instance = 0
821 # to decode bytes to str
822 reader = getreader("utf-8")
823
824 def __init__(self):
825 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000826 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100827 self.engine = Engine(self.authenticator)
yshah53cc9eb2024-07-05 13:06:31 +0000828 self.logger = logging.getLogger("nbi.server")
tiernoc94c3df2018-02-09 15:38:54 +0100829
tiernoc94c3df2018-02-09 15:38:54 +0100830 def _format_in(self, kwargs):
garciadeblasf2af4a12023-01-24 16:56:54 +0100831 error_text = "" # error_text must be initialized outside try
tiernoc94c3df2018-02-09 15:38:54 +0100832 try:
833 indata = None
834 if cherrypy.request.body.length:
835 error_text = "Invalid input format "
836
837 if "Content-Type" in cherrypy.request.headers:
838 if "application/json" in cherrypy.request.headers["Content-Type"]:
839 error_text = "Invalid json format "
840 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100841 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100842 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
843 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100844 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100845 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100846 elif (
847 "application/binary" in cherrypy.request.headers["Content-Type"]
848 or "application/gzip"
849 in cherrypy.request.headers["Content-Type"]
850 or "application/zip" in cherrypy.request.headers["Content-Type"]
851 or "text/plain" in cherrypy.request.headers["Content-Type"]
852 ):
tiernof27c79b2018-03-12 17:08:42 +0100853 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100854 elif (
855 "multipart/form-data"
856 in cherrypy.request.headers["Content-Type"]
857 ):
yshah53cc9eb2024-07-05 13:06:31 +0000858 if (
859 "descriptor_file" in kwargs
860 or "package" in kwargs
861 and "name" in kwargs
862 ):
863 filecontent = ""
864 if "descriptor_file" in kwargs:
865 filecontent = kwargs.pop("descriptor_file")
866 if "package" in kwargs:
867 filecontent = kwargs.pop("package")
tiernoc94c3df2018-02-09 15:38:54 +0100868 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100869 raise NbiException(
870 "empty file or content", HTTPStatus.BAD_REQUEST
871 )
yshah53cc9eb2024-07-05 13:06:31 +0000872 indata = filecontent
873 if filecontent.content_type.value:
874 cherrypy.request.headers[
875 "Content-Type"
876 ] = filecontent.content_type.value
877 elif "package" in kwargs:
878 filecontent = kwargs.pop("package")
879 if not filecontent.file:
880 raise NbiException(
881 "empty file or content", HTTPStatus.BAD_REQUEST
882 )
883 indata = filecontent
tiernoc94c3df2018-02-09 15:38:54 +0100884 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100885 cherrypy.request.headers[
886 "Content-Type"
887 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100888 else:
889 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
890 # "Only 'Content-Type' of type 'application/json' or
891 # 'application/yaml' for input format are available")
892 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100893 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100894 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100895 else:
896 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100897 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100898 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100899 if not indata:
900 indata = {}
tiernoc94c3df2018-02-09 15:38:54 +0100901 format_yaml = False
902 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
903 format_yaml = True
904
905 for k, v in kwargs.items():
906 if isinstance(v, str):
907 if v == "":
908 kwargs[k] = None
909 elif format_yaml:
910 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100911 kwargs[k] = yaml.safe_load(v)
tiernoe1281182018-05-22 12:24:36 +0200912 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100913 pass
garciadeblas4568a372021-03-24 09:19:48 +0100914 elif (
915 k.endswith(".gt")
916 or k.endswith(".lt")
917 or k.endswith(".gte")
918 or k.endswith(".lte")
919 ):
tiernoc94c3df2018-02-09 15:38:54 +0100920 try:
921 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200922 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100923 try:
924 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200925 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100926 pass
927 elif v.find(",") > 0:
928 kwargs[k] = v.split(",")
929 elif isinstance(v, (list, tuple)):
930 for index in range(0, len(v)):
931 if v[index] == "":
932 v[index] = None
933 elif format_yaml:
934 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100935 v[index] = yaml.safe_load(v[index])
tiernoe1281182018-05-22 12:24:36 +0200936 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100937 pass
938
tiernof27c79b2018-03-12 17:08:42 +0100939 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100940 except (ValueError, yaml.YAMLError) as exc:
941 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
942 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100943 raise NbiException(
944 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
945 )
tiernob92094f2018-05-11 13:44:22 +0200946 except Exception as exc:
947 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100948
949 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000950 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100951 """
952 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100953 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000954 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000955 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100956 :return: None
957 """
tierno0f98af52018-03-19 10:28:22 +0100958 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100959 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100960 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100961 return html.format(
962 data, cherrypy.request, cherrypy.response, token_info
963 )
tierno09c073e2018-04-26 13:36:48 +0200964 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100965 return
966 elif hasattr(data, "read"): # file object
967 if _format:
968 cherrypy.response.headers["Content-Type"] = _format
969 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100970 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100971 else:
garciadeblas4568a372021-03-24 09:19:48 +0100972 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100973 # TODO check that cherrypy close file. If not implement pending things to close per thread next
974 return data
tierno0f98af52018-03-19 10:28:22 +0100975 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000976 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100977 return html.format(
978 data, cherrypy.request, cherrypy.response, token_info
979 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000980 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100981 pass
garciadeblas4568a372021-03-24 09:19:48 +0100982 elif "application/json" in accept or (
983 cherrypy.response.status and cherrypy.response.status >= 300
984 ):
985 cherrypy.response.headers[
986 "Content-Type"
987 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000988 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100989 return a.encode("utf8")
990 cherrypy.response.headers["Content-Type"] = "application/yaml"
991 return yaml.safe_dump(
992 data,
993 explicit_start=True,
994 indent=4,
995 default_flow_style=False,
996 tags=False,
997 encoding="utf-8",
998 allow_unicode=True,
999 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +01001000
1001 @cherrypy.expose
1002 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +00001003 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001004 try:
1005 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +00001006 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +01001007 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +01001008 else:
garciadeblas4568a372021-03-24 09:19:48 +01001009 raise cherrypy.HTTPError(
1010 HTTPStatus.METHOD_NOT_ALLOWED.value,
1011 "Method {} not allowed for tokens".format(cherrypy.request.method),
1012 )
tiernoc94c3df2018-02-09 15:38:54 +01001013
tierno701018c2019-06-25 11:13:14 +00001014 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001015
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001016 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +00001017 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +01001018 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +00001019 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001020
1021 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +02001022 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +02001023 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +02001024 try:
1025 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001026 raise NbiException(
1027 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
1028 )
tierno55945e72018-04-06 16:40:27 +02001029 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001030 raise NbiException(
1031 "Invalid URL or query string for version",
1032 HTTPStatus.METHOD_NOT_ALLOWED,
1033 )
tierno9c630112019-08-29 14:21:41 +00001034 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +00001035 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
1036 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +02001037 except NbiException as e:
1038 cherrypy.response.status = e.http_code.value
1039 problem_details = {
1040 "code": e.http_code.name,
1041 "status": e.http_code.value,
1042 "detail": str(e),
1043 }
1044 return self._format_out(problem_details, None)
1045
tierno12eac3c2020-03-19 23:22:08 +00001046 def domain(self):
1047 try:
1048 domains = {
garciadeblas4568a372021-03-24 09:19:48 +01001049 "user_domain_name": cherrypy.tree.apps["/osm"]
1050 .config["authentication"]
1051 .get("user_domain_name"),
1052 "project_domain_name": cherrypy.tree.apps["/osm"]
1053 .config["authentication"]
1054 .get("project_domain_name"),
1055 }
tierno12eac3c2020-03-19 23:22:08 +00001056 return self._format_out(domains)
1057 except NbiException as e:
1058 cherrypy.response.status = e.http_code.value
1059 problem_details = {
1060 "code": e.http_code.name,
1061 "status": e.http_code.value,
1062 "detail": str(e),
1063 }
1064 return self._format_out(problem_details, None)
1065
tiernoa5035702019-07-29 08:54:42 +00001066 @staticmethod
1067 def _format_login(token_info):
1068 """
1069 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
1070 log this information
1071 :param token_info: Dictionary with token content
1072 :return: None
1073 """
1074 cherrypy.request.login = token_info.get("username", "-")
1075 if token_info.get("project_name"):
1076 cherrypy.request.login += "/" + token_info["project_name"]
1077 if token_info.get("id"):
1078 cherrypy.request.login += ";session=" + token_info["id"][0:12]
1079
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001080 # NS Fault Management
1081 @cherrypy.expose
garciadeblasf2af4a12023-01-24 16:56:54 +01001082 def nsfm(
1083 self,
1084 version=None,
1085 topic=None,
1086 uuid=None,
1087 project_name=None,
1088 ns_id=None,
1089 *args,
1090 **kwargs
1091 ):
1092 if topic == "alarms":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001093 try:
1094 method = cherrypy.request.method
garciadeblasf2af4a12023-01-24 16:56:54 +01001095 role_permission = self._check_valid_url_method(
1096 method, "nsfm", version, topic, None, None, *args
1097 )
1098 query_string_operations = self._extract_query_string_operations(
1099 kwargs, method
1100 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001101
garciadeblasf2af4a12023-01-24 16:56:54 +01001102 self.authenticator.authorize(
1103 role_permission, query_string_operations, None
1104 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001105
1106 # to handle get request
garciadeblasf2af4a12023-01-24 16:56:54 +01001107 if cherrypy.request.method == "GET":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001108 # if request is on basis of uuid
garciadeblasf2af4a12023-01-24 16:56:54 +01001109 if uuid and uuid != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001110 try:
1111 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
garciadeblasf2af4a12023-01-24 16:56:54 +01001112 alarm_action = self.engine.db.get_one(
1113 "alarms_action", {"uuid": uuid}
1114 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001115 alarm.update(alarm_action)
garciadeblasf2af4a12023-01-24 16:56:54 +01001116 vnf = self.engine.db.get_one(
1117 "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}
1118 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001119 alarm["vnf-id"] = vnf["_id"]
1120 return self._format_out(str(alarm))
1121 except Exception:
1122 return self._format_out("Please provide valid alarm uuid")
garciadeblasf2af4a12023-01-24 16:56:54 +01001123 elif ns_id and ns_id != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001124 # if request is on basis of ns_id
1125 try:
garciadeblasf2af4a12023-01-24 16:56:54 +01001126 alarms = self.engine.db.get_list(
1127 "alarms", {"tags.ns_id": ns_id}
1128 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001129 for alarm in alarms:
garciadeblasf2af4a12023-01-24 16:56:54 +01001130 alarm_action = self.engine.db.get_one(
1131 "alarms_action", {"uuid": alarm["uuid"]}
1132 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001133 alarm.update(alarm_action)
1134 return self._format_out(str(alarms))
1135 except Exception:
1136 return self._format_out("Please provide valid ns id")
1137 else:
1138 # to return only alarm which are related to given project
garciadeblasf2af4a12023-01-24 16:56:54 +01001139 project = self.engine.db.get_one(
1140 "projects", {"name": project_name}
1141 )
1142 project_id = project.get("_id")
1143 ns_list = self.engine.db.get_list(
1144 "nsrs", {"_admin.projects_read": project_id}
1145 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001146 ns_ids = []
1147 for ns in ns_list:
1148 ns_ids.append(ns.get("_id"))
1149 alarms = self.engine.db.get_list("alarms")
garciadeblasf2af4a12023-01-24 16:56:54 +01001150 alarm_list = [
1151 alarm
1152 for alarm in alarms
1153 if alarm["tags"]["ns_id"] in ns_ids
1154 ]
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001155 for alrm in alarm_list:
garciadeblasf2af4a12023-01-24 16:56:54 +01001156 action = self.engine.db.get_one(
1157 "alarms_action", {"uuid": alrm.get("uuid")}
1158 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001159 alrm.update(action)
1160 return self._format_out(str(alarm_list))
1161 # to handle patch request for alarm update
garciadeblasf2af4a12023-01-24 16:56:54 +01001162 elif cherrypy.request.method == "PATCH":
garciadeblas4cd875d2023-02-14 19:05:34 +01001163 data = yaml.safe_load(cherrypy.request.body)
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001164 try:
1165 # check if uuid is valid
1166 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
1167 except Exception:
1168 return self._format_out("Please provide valid alarm uuid.")
1169 if data.get("is_enable") is not None:
1170 if data.get("is_enable"):
garciadeblasf2af4a12023-01-24 16:56:54 +01001171 alarm_status = "ok"
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001172 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001173 alarm_status = "disabled"
1174 self.engine.db.set_one(
1175 "alarms",
1176 {"uuid": data.get("uuid")},
1177 {"alarm_status": alarm_status},
1178 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001179 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001180 self.engine.db.set_one(
1181 "alarms",
1182 {"uuid": data.get("uuid")},
1183 {"threshold": data.get("threshold")},
1184 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001185 return self._format_out("Alarm updated")
1186 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01001187 if isinstance(
1188 e,
1189 (
1190 NbiException,
1191 EngineException,
1192 DbException,
1193 FsException,
1194 MsgException,
1195 AuthException,
1196 ValidationError,
1197 AuthconnException,
1198 ),
1199 ):
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001200 http_code_value = cherrypy.response.status = e.http_code.value
1201 http_code_name = e.http_code.name
1202 cherrypy.log("Exception {}".format(e))
1203 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001204 http_code_value = (
1205 cherrypy.response.status
1206 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001207 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
1208 http_code_name = HTTPStatus.BAD_REQUEST.name
1209 problem_details = {
1210 "code": http_code_name,
1211 "status": http_code_value,
1212 "detail": str(e),
1213 }
1214 return self._format_out(problem_details)
1215
tierno55945e72018-04-06 16:40:27 +02001216 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +01001217 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +00001218 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001219 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +01001220 indata = self._format_in(kwargs)
1221 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001222 raise NbiException(
1223 "Expected application/yaml or application/json Content-Type",
1224 HTTPStatus.BAD_REQUEST,
1225 )
tiernoa5035702019-07-29 08:54:42 +00001226
1227 if method == "GET":
1228 token_info = self.authenticator.authorize()
1229 # for logging
1230 self._format_login(token_info)
1231 if token_id:
1232 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +01001233 else:
tiernoa5035702019-07-29 08:54:42 +00001234 outdata = self.authenticator.get_token_list(token_info)
1235 elif method == "POST":
1236 try:
1237 token_info = self.authenticator.authorize()
1238 except Exception:
1239 token_info = None
1240 if kwargs:
1241 indata.update(kwargs)
1242 # This is needed to log the user when authentication fails
1243 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +01001244 outdata = token_info = self.authenticator.new_token(
1245 token_info, indata, cherrypy.request.remote
1246 )
jeganbe1a3df2024-06-04 12:05:19 +00001247 if outdata.get("email") or outdata.get("otp") == "invalid":
1248 return self._format_out(outdata, token_info)
garciadeblasf2af4a12023-01-24 16:56:54 +01001249 cherrypy.session["Authorization"] = outdata["_id"] # pylint: disable=E1101
tiernoa5035702019-07-29 08:54:42 +00001250 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1251 # for logging
1252 self._format_login(token_info)
jeganbe1a3df2024-06-04 12:05:19 +00001253 if outdata.get("otp") == "valid":
1254 outdata = {
1255 "id": outdata["id"],
1256 "message": "valid_otp",
1257 "user_id": outdata["user_id"],
1258 }
selvi.ja9a1fc82022-04-04 06:54:30 +00001259 # password expiry check
jeganbe1a3df2024-06-04 12:05:19 +00001260 elif self.authenticator.check_password_expiry(outdata):
garciadeblasf2af4a12023-01-24 16:56:54 +01001261 outdata = {
1262 "id": outdata["id"],
1263 "message": "change_password",
1264 "user_id": outdata["user_id"],
1265 }
tiernoa5035702019-07-29 08:54:42 +00001266 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1267 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
elumalai7802ff82023-04-24 20:38:32 +05301268 cef_event(
1269 cef_logger,
1270 {
1271 "name": "User Login",
1272 "sourceUserName": token_info.get("username"),
1273 "message": "User Logged In, Project={} Outcome=Success".format(
1274 token_info.get("project_name")
1275 ),
1276 },
1277 )
1278 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001279 elif method == "DELETE":
1280 if not token_id and "id" in kwargs:
1281 token_id = kwargs["id"]
1282 elif not token_id:
1283 token_info = self.authenticator.authorize()
1284 # for logging
1285 self._format_login(token_info)
1286 token_id = token_info["_id"]
Rahulc72bc8e2023-12-05 11:54:38 +00001287 if current_backend != "keystone":
1288 token_details = self.engine.db.get_one("tokens", {"_id": token_id})
1289 current_user = token_details.get("username")
1290 current_project = token_details.get("project_name")
1291 else:
1292 current_user = "keystone backend"
1293 current_project = "keystone backend"
tiernoa5035702019-07-29 08:54:42 +00001294 outdata = self.authenticator.del_token(token_id)
1295 token_info = None
garciadeblasf2af4a12023-01-24 16:56:54 +01001296 cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101
elumalai7802ff82023-04-24 20:38:32 +05301297 cef_event(
1298 cef_logger,
1299 {
1300 "name": "User Logout",
1301 "sourceUserName": current_user,
1302 "message": "User Logged Out, Project={} Outcome=Success".format(
1303 current_project
1304 ),
1305 },
1306 )
1307 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001308 # cherrypy.response.cookie["Authorization"] = token_id
1309 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1310 else:
garciadeblas4568a372021-03-24 09:19:48 +01001311 raise NbiException(
1312 "Method {} not allowed for token".format(method),
1313 HTTPStatus.METHOD_NOT_ALLOWED,
1314 )
tiernoa5035702019-07-29 08:54:42 +00001315 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001316
1317 @cherrypy.expose
1318 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001319 if not cherrypy.config.get("server.enable_test") or (
1320 isinstance(cherrypy.config["server.enable_test"], str)
1321 and cherrypy.config["server.enable_test"].lower() == "false"
1322 ):
tierno4836bac2020-01-15 14:41:48 +00001323 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1324 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001325 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001326 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001327 return (
1328 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1329 "sleep/<time>\nmessage/topic\n</pre></html>"
1330 )
tiernof27c79b2018-03-12 17:08:42 +01001331
1332 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001333 try:
1334 # self.engine.load_dbase(cherrypy.request.app.config)
garciadeblasf2af4a12023-01-24 16:56:54 +01001335 pid = self.authenticator.create_admin_project()
1336 self.authenticator.create_admin_user(pid)
tiernoc94c3df2018-02-09 15:38:54 +01001337 return "Done. User 'admin', password 'admin' created"
1338 except Exception:
1339 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1340 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001341 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001342 return cherrypy.lib.static.serve_file(
1343 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1344 "text/plain",
1345 "attachment",
1346 )
tiernof27c79b2018-03-12 17:08:42 +01001347 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001348 f_path = (
1349 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1350 )
tiernof27c79b2018-03-12 17:08:42 +01001351 f = open(f_path, "r")
1352 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001353 return f
tierno55945e72018-04-06 16:40:27 +02001354
tiernof27c79b2018-03-12 17:08:42 +01001355 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001356 deleted_info = self.engine.db.del_list(args[1], kwargs)
1357 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1358 elif len(args) and args[0] == "fs-clear":
1359 if len(args) >= 2:
1360 folders = (args[1],)
1361 else:
1362 folders = self.engine.fs.dir_ls(".")
1363 for folder in folders:
1364 self.engine.fs.file_delete(folder)
1365 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001366 elif args and args[0] == "login":
1367 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001368 cherrypy.response.headers[
1369 "WWW-Authenticate"
1370 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001371 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1372 elif args and args[0] == "login2":
1373 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001374 cherrypy.response.headers[
1375 "WWW-Authenticate"
1376 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001377 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1378 elif args and args[0] == "sleep":
1379 sleep_time = 5
1380 try:
1381 sleep_time = int(args[1])
1382 except Exception:
1383 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1384 return self._format_out("Database already initialized")
1385 thread_info = cherrypy.thread_data
1386 print(thread_info)
1387 time.sleep(sleep_time)
1388 # thread_info
1389 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001390 main_topic = args[1]
1391 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001392 try:
garciadeblas4568a372021-03-24 09:19:48 +01001393 if cherrypy.request.method == "POST":
garciadeblas4cd875d2023-02-14 19:05:34 +01001394 to_send = yaml.safe_load(cherrypy.request.body)
tierno55945e72018-04-06 16:40:27 +02001395 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001396 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001397 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001398 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001399 for k, v in kwargs.items():
garciadeblas4cd875d2023-02-14 19:05:34 +01001400 v_dict = yaml.safe_load(v)
tiernof1509b22020-05-12 14:32:37 +00001401 self.engine.msg.write(main_topic, k, v_dict)
1402 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001403 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001404 return_text += "Error: " + str(e)
1405 return_text += "</pre></html>\n"
1406 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001407
1408 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001409 "<html><pre>\nheaders:\n args: {}\n".format(args)
1410 + " kwargs: {}\n".format(kwargs)
1411 + " headers: {}\n".format(cherrypy.request.headers)
1412 + " path_info: {}\n".format(cherrypy.request.path_info)
1413 + " query_string: {}\n".format(cherrypy.request.query_string)
garciadeblasf2af4a12023-01-24 16:56:54 +01001414 + " session: {}\n".format(cherrypy.session) # pylint: disable=E1101
garciadeblas4568a372021-03-24 09:19:48 +01001415 + " cookie: {}\n".format(cherrypy.request.cookie)
1416 + " method: {}\n".format(cherrypy.request.method)
garciadeblasf2af4a12023-01-24 16:56:54 +01001417 + " session: {}\n".format(
1418 cherrypy.session.get("fieldname") # pylint: disable=E1101
1419 )
garciadeblas4568a372021-03-24 09:19:48 +01001420 + " body:\n"
1421 )
tiernoc94c3df2018-02-09 15:38:54 +01001422 return_text += " length: {}\n".format(cherrypy.request.body.length)
1423 if cherrypy.request.body.length:
1424 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001425 str(
1426 cherrypy.request.body.read(
1427 int(cherrypy.request.headers.get("Content-Length", 0))
1428 )
1429 )
1430 )
tiernoc94c3df2018-02-09 15:38:54 +01001431 if thread_info:
1432 return_text += "thread: {}\n".format(thread_info)
1433 return_text += "</pre></html>"
1434 return return_text
1435
tierno701018c2019-06-25 11:13:14 +00001436 @staticmethod
1437 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001438 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001439 raise NbiException(
1440 "URL must contain at least 'main_topic/version/topic'",
1441 HTTPStatus.METHOD_NOT_ALLOWED,
1442 )
tiernof27c79b2018-03-12 17:08:42 +01001443
tierno701018c2019-06-25 11:13:14 +00001444 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001445 for arg in args:
1446 if arg is None:
1447 break
1448 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001449 raise NbiException(
1450 "URL contains unexpected extra items '{}'".format(arg),
1451 HTTPStatus.METHOD_NOT_ALLOWED,
1452 )
tiernof27c79b2018-03-12 17:08:42 +01001453
1454 if arg in reference:
1455 reference = reference[arg]
1456 elif "<ID>" in reference:
1457 reference = reference["<ID>"]
1458 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001459 # if there is content
1460 if reference["*"]:
1461 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001462 break
1463 else:
garciadeblas4568a372021-03-24 09:19:48 +01001464 raise NbiException(
1465 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1466 )
tiernof27c79b2018-03-12 17:08:42 +01001467 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001468 raise NbiException(
1469 "Method {} not supported yet for this URL".format(method),
1470 HTTPStatus.NOT_IMPLEMENTED,
1471 )
tierno2236d202018-05-16 19:05:16 +02001472 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001473 raise NbiException(
1474 "Method {} not supported for this URL".format(method),
1475 HTTPStatus.METHOD_NOT_ALLOWED,
1476 )
tierno701018c2019-06-25 11:13:14 +00001477 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001478
1479 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001480 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001481 """
1482 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001483 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001484 :param version:
tiernob24258a2018-10-04 18:39:49 +02001485 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001486 :param id:
1487 :return: None
1488 """
1489 # 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 +01001490 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1491 main_topic, version, topic, id
1492 )
tiernof27c79b2018-03-12 17:08:42 +01001493 return
1494
tierno65ca36d2019-02-12 19:27:52 +01001495 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001496 def _extract_query_string_operations(kwargs, method):
1497 """
1498
1499 :param kwargs:
1500 :return:
1501 """
1502 query_string_operations = []
1503 if kwargs:
1504 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1505 if qs in kwargs and kwargs[qs].lower() != "false":
1506 query_string_operations.append(qs.lower() + ":" + method.lower())
1507 return query_string_operations
1508
1509 @staticmethod
1510 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001511 """
1512 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1513 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001514 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001515 :param kwargs: query string input.
1516 :param method: http method: GET, POSST, PUT, ...
1517 :param _id:
1518 :return: admin_query dictionary with keys:
1519 public: True, False or None
1520 force: True or False
1521 project_id: tuple with projects used for accessing an element
1522 set_project: tuple with projects that a created element will belong to
1523 method: show, list, delete, write
1524 """
garciadeblas4568a372021-03-24 09:19:48 +01001525 admin_query = {
1526 "force": False,
1527 "project_id": (token_info["project_id"],),
1528 "username": token_info["username"],
Adurti76d4b762024-05-07 06:04:37 +00001529 "user_id": token_info["user_id"],
garciadeblas4568a372021-03-24 09:19:48 +01001530 "admin": token_info["admin"],
37177091c0322024-11-01 08:55:59 +00001531 "admin_show": token_info["admin_show"],
garciadeblas4568a372021-03-24 09:19:48 +01001532 "public": None,
1533 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1534 }
tierno65ca36d2019-02-12 19:27:52 +01001535 if kwargs:
1536 # FORCE
1537 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001538 if (
1539 kwargs["FORCE"].lower() != "false"
1540 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001541 admin_query["force"] = True
1542 del kwargs["FORCE"]
1543 # PUBLIC
1544 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001545 if (
1546 kwargs["PUBLIC"].lower() != "false"
1547 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001548 admin_query["public"] = True
1549 else:
1550 admin_query["public"] = False
1551 del kwargs["PUBLIC"]
1552 # ADMIN
1553 if "ADMIN" in kwargs:
1554 behave_as = kwargs.pop("ADMIN")
1555 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001556 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001557 raise NbiException(
1558 "Only admin projects can use 'ADMIN' query string",
1559 HTTPStatus.UNAUTHORIZED,
1560 )
1561 if (
1562 not behave_as or behave_as.lower() == "true"
1563 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001564 admin_query["project_id"] = ()
1565 elif isinstance(behave_as, (list, tuple)):
1566 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001567 else: # isinstance(behave_as, str)
1568 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001569 if "SET_PROJECT" in kwargs:
1570 set_project = kwargs.pop("SET_PROJECT")
1571 if not set_project:
1572 admin_query["set_project"] = list(admin_query["project_id"])
1573 else:
1574 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001575 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001576 if admin_query["project_id"]:
1577 for p in set_project:
1578 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001579 raise NbiException(
1580 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1581 "'ADMIN='{p}'".format(p=p),
1582 HTTPStatus.UNAUTHORIZED,
1583 )
tierno65ca36d2019-02-12 19:27:52 +01001584 admin_query["set_project"] = set_project
1585
1586 # PROJECT_READ
1587 # if "PROJECT_READ" in kwargs:
1588 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001589 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001590 if method == "GET":
1591 if _id:
1592 admin_query["method"] = "show"
1593 else:
1594 admin_query["method"] = "list"
1595 elif method == "DELETE":
1596 admin_query["method"] = "delete"
1597 else:
1598 admin_query["method"] = "write"
1599 return admin_query
1600
tiernoc94c3df2018-02-09 15:38:54 +01001601 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001602 def default(
1603 self,
1604 main_topic=None,
1605 version=None,
1606 topic=None,
1607 _id=None,
1608 item=None,
1609 *args,
1610 **kwargs
1611 ):
tierno701018c2019-06-25 11:13:14 +00001612 token_info = None
selvi.j9919bbc2023-04-26 12:22:13 +00001613 outdata = {}
tiernof27c79b2018-03-12 17:08:42 +01001614 _format = None
tierno0f98af52018-03-19 10:28:22 +01001615 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001616 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001617 rollback = []
tierno701018c2019-06-25 11:13:14 +00001618 engine_session = None
elumalai7802ff82023-04-24 20:38:32 +05301619 url_id = ""
1620 log_mapping = {
1621 "POST": "Creating",
1622 "GET": "Fetching",
1623 "DELETE": "Deleting",
1624 "PUT": "Updating",
1625 "PATCH": "Updating",
1626 }
tiernoc94c3df2018-02-09 15:38:54 +01001627 try:
tiernob24258a2018-10-04 18:39:49 +02001628 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001629 raise NbiException(
1630 "URL must contain at least 'main_topic/version/topic'",
1631 HTTPStatus.METHOD_NOT_ALLOWED,
1632 )
1633 if main_topic not in (
1634 "admin",
1635 "vnfpkgm",
1636 "nsd",
1637 "nslcm",
1638 "pdu",
1639 "nst",
1640 "nsilcm",
1641 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001642 "vnflcm",
rshri2d386cb2024-07-05 14:35:51 +00001643 "k8scluster",
yshah53cc9eb2024-07-05 13:06:31 +00001644 "ksu",
1645 "oka",
garciadeblas4568a372021-03-24 09:19:48 +01001646 ):
1647 raise NbiException(
1648 "URL main_topic '{}' not supported".format(main_topic),
1649 HTTPStatus.METHOD_NOT_ALLOWED,
1650 )
1651 if version != "v1":
1652 raise NbiException(
1653 "URL version '{}' not supported".format(version),
1654 HTTPStatus.METHOD_NOT_ALLOWED,
1655 )
elumalai7802ff82023-04-24 20:38:32 +05301656 if _id is not None:
1657 url_id = _id
tiernoc94c3df2018-02-09 15:38:54 +01001658
garciadeblas4568a372021-03-24 09:19:48 +01001659 if (
1660 kwargs
1661 and "METHOD" in kwargs
1662 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1663 ):
tiernof27c79b2018-03-12 17:08:42 +01001664 method = kwargs.pop("METHOD")
1665 else:
1666 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001667
garciadeblas4568a372021-03-24 09:19:48 +01001668 role_permission = self._check_valid_url_method(
1669 method, main_topic, version, topic, _id, item, *args
1670 )
1671 query_string_operations = self._extract_query_string_operations(
1672 kwargs, method
1673 )
tiernob24258a2018-10-04 18:39:49 +02001674 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001675 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001676 token_info = self.authenticator.authorize(
1677 role_permission, query_string_operations, _id
1678 )
tierno12eac3c2020-03-19 23:22:08 +00001679 if main_topic == "admin" and topic == "domains":
1680 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001681 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001682 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001683 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301684
vijay.r35ef2f72019-04-30 17:55:49 +05301685 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001686 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001687
tiernob24258a2018-10-04 18:39:49 +02001688 if main_topic == "nsd":
1689 engine_topic = "nsds"
kayal2001f71c2e82024-06-25 15:26:24 +05301690 if topic == "ns_config_template":
1691 engine_topic = "nsconfigtemps"
tiernob24258a2018-10-04 18:39:49 +02001692 elif main_topic == "vnfpkgm":
1693 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001694 if topic == "vnfpkg_op_occs":
1695 engine_topic = "vnfpkgops"
1696 if topic == "vnf_packages" and item == "action":
1697 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001698 elif main_topic == "nslcm":
1699 engine_topic = "nsrs"
1700 if topic == "ns_lcm_op_occs":
1701 engine_topic = "nslcmops"
1702 if topic == "vnfrs" or topic == "vnf_instances":
1703 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001704 elif main_topic == "vnflcm":
1705 if topic == "vnf_lcm_op_occs":
1706 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001707 elif main_topic == "nst":
1708 engine_topic = "nsts"
1709 elif main_topic == "nsilcm":
1710 engine_topic = "nsis"
1711 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001712 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001713 elif main_topic == "pdu":
1714 engine_topic = "pdus"
rshri2d386cb2024-07-05 14:35:51 +00001715 elif main_topic == "k8scluster":
1716 engine_topic = "k8s"
1717 if topic == "clusters" and _id == "register" or item == "deregister":
1718 engine_topic = "k8sops"
1719 elif topic == "infra_controller_profiles":
1720 engine_topic = "infras_cont"
1721 elif topic == "infra_config_profiles":
1722 engine_topic = "infras_conf"
1723 elif topic == "resource_profiles":
1724 engine_topic = "resources"
1725 elif topic == "app_profiles":
1726 engine_topic = "apps"
yshah53cc9eb2024-07-05 13:06:31 +00001727 elif main_topic == "k8scluster" and item in (
1728 "upgrade",
1729 "get_creds",
1730 "scale",
1731 ):
1732 engine_topic = "k8s"
1733 elif main_topic == "ksu" and engine_topic in ("ksus", "clone", "move"):
1734 engine_topic = "ksus"
garciadeblas4568a372021-03-24 09:19:48 +01001735 if (
1736 engine_topic == "vims"
1737 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001738 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001739
preethika.p329b8182020-04-22 12:25:39 +05301740 if topic == "subscriptions":
1741 engine_topic = main_topic + "_" + topic
1742
tiernoc94c3df2018-02-09 15:38:54 +01001743 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001744 if item in (
1745 "nsd_content",
1746 "package_content",
1747 "artifacts",
1748 "vnfd",
1749 "nsd",
1750 "nst",
1751 "nst_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301752 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001753 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001754 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001755 path = "$DESCRIPTOR"
1756 elif args:
1757 path = args
tiernob24258a2018-10-04 18:39:49 +02001758 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001759 path = ()
1760 else:
1761 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001762 file, _format = self.engine.get_file(
1763 engine_session,
1764 engine_topic,
1765 _id,
1766 path,
1767 cherrypy.request.headers.get("Accept"),
1768 )
tiernof27c79b2018-03-12 17:08:42 +01001769 outdata = file
1770 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001771 outdata = self.engine.get_item_list(
1772 engine_session, engine_topic, kwargs, api_req=True
1773 )
rshri2d386cb2024-07-05 14:35:51 +00001774 elif topic == "clusters" and item in (
1775 "infra_controller_profiles",
1776 "infra_config_profiles",
1777 "app_profiles",
1778 "resource_profiles",
1779 ):
1780 profile = item
1781 filter_q = None
1782 outdata = self.engine.get_one_item(
1783 engine_session,
1784 engine_topic,
1785 _id,
1786 profile,
1787 filter_q,
1788 api_req=True,
1789 )
shahithyab9eb4142024-10-17 05:51:39 +00001790 elif (
1791 topic == "clusters"
1792 and item == "get_creds_file"
1793 or item == "get_creds"
1794 ):
1795 if item == "get_creds_file":
1796 op_id = args[0]
1797 file, _format = self.engine.get_cluster_creds_file(
1798 engine_session, engine_topic, _id, item, op_id
1799 )
1800 outdata = file
1801 if item == "get_creds":
1802 op_id = self.engine.get_cluster_creds(
1803 engine_session, engine_topic, _id, item
1804 )
1805 outdata = {"op_id": op_id}
tiernoc94c3df2018-02-09 15:38:54 +01001806 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301807 if item == "reports":
1808 # TODO check that project_id (_id in this context) has permissions
1809 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301810 filter_q = None
1811 if "vcaStatusRefresh" in kwargs:
1812 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
garciadeblasf2af4a12023-01-24 16:56:54 +01001813 outdata = self.engine.get_item(
1814 engine_session, engine_topic, _id, filter_q, True
1815 )
delacruzramo271d2002019-12-02 21:00:37 +01001816
tiernof27c79b2018-03-12 17:08:42 +01001817 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001818 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001819 if topic in (
1820 "ns_descriptors_content",
1821 "vnf_packages_content",
1822 "netslice_templates_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301823 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001824 ):
tiernof27c79b2018-03-12 17:08:42 +01001825 _id = cherrypy.request.headers.get("Transaction-Id")
yshah53cc9eb2024-07-05 13:06:31 +00001826
tiernof27c79b2018-03-12 17:08:42 +01001827 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001828 _id, _ = self.engine.new_item(
1829 rollback,
1830 engine_session,
1831 engine_topic,
1832 {},
1833 None,
1834 cherrypy.request.headers,
1835 )
1836 completed = self.engine.upload_content(
1837 engine_session,
1838 engine_topic,
1839 _id,
1840 indata,
1841 kwargs,
1842 cherrypy.request.headers,
1843 )
tiernof27c79b2018-03-12 17:08:42 +01001844 if completed:
tiernob24258a2018-10-04 18:39:49 +02001845 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001846 else:
1847 cherrypy.response.headers["Transaction-Id"] = _id
1848 outdata = {"id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001849 elif topic == "oka_packages":
1850 _id = cherrypy.request.headers.get("Transaction-Id")
1851
1852 if not _id:
1853 _id, _ = self.engine.new_item(
1854 rollback,
1855 engine_session,
1856 engine_topic,
1857 {},
1858 kwargs,
1859 cherrypy.request.headers,
1860 )
1861 cherrypy.request.headers["method"] = cherrypy.request.method
1862 if indata:
1863 completed = self.engine.upload_content(
1864 engine_session,
1865 engine_topic,
1866 _id,
1867 indata,
1868 None,
1869 cherrypy.request.headers,
1870 )
1871 if completed:
1872 self._set_location_header(main_topic, version, topic, _id)
1873 else:
1874 cherrypy.response.headers["Transaction-Id"] = _id
yshahe08aff12024-11-07 09:32:22 +00001875 outdata = {"_id": _id, "id": _id}
tiernob24258a2018-10-04 18:39:49 +02001876 elif topic == "ns_instances_content":
1877 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001878 _id, _ = self.engine.new_item(
1879 rollback, engine_session, engine_topic, indata, kwargs
1880 )
tiernob24258a2018-10-04 18:39:49 +02001881 # creates nslcmop
1882 indata["lcmOperationType"] = "instantiate"
1883 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001884 nslcmop_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001885 rollback, engine_session, "nslcmops", indata, None
1886 )
tiernob24258a2018-10-04 18:39:49 +02001887 self._set_location_header(main_topic, version, topic, _id)
garciadeblasf53612b2024-07-12 14:44:37 +02001888 outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName}
adurti3af50952024-05-31 11:36:57 +05301889 elif topic == "ns_instances_terminate":
1890 if indata.get("ns_ids"):
1891 for ns_id in indata.get("ns_ids"):
1892 nslcmop_desc = {
1893 "lcmOperationType": "terminate",
1894 "nsInstanceId": ns_id,
shahithyab9eb4142024-10-17 05:51:39 +00001895 "autoremove": (
1896 indata.get("autoremove")
1897 if "autoremove" in indata
1898 else True
1899 ),
adurti3af50952024-05-31 11:36:57 +05301900 }
1901 op_id, _, _ = self.engine.new_item(
1902 rollback,
1903 engine_session,
1904 "nslcmops",
1905 nslcmop_desc,
1906 kwargs,
1907 )
1908 if not op_id:
1909 _ = self.engine.del_item(
1910 engine_session, engine_topic, ns_id
1911 )
1912 outdata = {"ns_ids": indata.get("ns_ids")}
1913 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001914 elif topic == "ns_instances" and item:
1915 indata["lcmOperationType"] = item
1916 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001917 _id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001918 rollback, engine_session, "nslcmops", indata, kwargs
1919 )
1920 self._set_location_header(
1921 main_topic, version, "ns_lcm_op_occs", _id
1922 )
garciadeblasf53612b2024-07-12 14:44:37 +02001923 outdata = {"id": _id, "nsName": nsName}
tierno65acb4d2018-04-06 16:42:40 +02001924 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001925 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001926 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001927 _id, _ = self.engine.new_item(
1928 rollback, engine_session, engine_topic, indata, kwargs
1929 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001930 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001931 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001932 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001933 nsilcmop_id, _ = self.engine.new_item(
1934 rollback, engine_session, "nsilcmops", indata, kwargs
1935 )
kuuse078f55e2019-05-16 19:24:21 +02001936 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001937 elif topic == "netslice_instances" and item:
1938 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001939 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001940 _id, _ = self.engine.new_item(
1941 rollback, engine_session, "nsilcmops", indata, kwargs
1942 )
1943 self._set_location_header(
1944 main_topic, version, "nsi_lcm_op_occs", _id
1945 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001946 outdata = {"id": _id}
1947 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001948 elif topic == "vnf_packages" and item == "action":
1949 indata["lcmOperationType"] = item
1950 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001951 _id, _ = self.engine.new_item(
1952 rollback, engine_session, "vnfpkgops", indata, kwargs
1953 )
1954 self._set_location_header(
1955 main_topic, version, "vnfpkg_op_occs", _id
1956 )
delacruzramo271d2002019-12-02 21:00:37 +01001957 outdata = {"id": _id}
1958 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301959 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001960 _id, _ = self.engine.new_item(
1961 rollback, engine_session, engine_topic, indata, kwargs
1962 )
preethika.p329b8182020-04-22 12:25:39 +05301963 self._set_location_header(main_topic, version, topic, _id)
1964 link = {}
1965 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001966 outdata = {
1967 "id": _id,
1968 "filter": indata["filter"],
1969 "callbackUri": indata["CallbackUri"],
1970 "_links": link,
1971 }
preethika.p329b8182020-04-22 12:25:39 +05301972 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001973 elif topic == "vnf_instances" and item:
1974 indata["lcmOperationType"] = item
1975 indata["vnfInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001976 _id, nsName, _ = self.engine.new_item(
garciadeblasf2af4a12023-01-24 16:56:54 +01001977 rollback, engine_session, "vnflcmops", indata, kwargs
1978 )
1979 self._set_location_header(
1980 main_topic, version, "vnf_lcm_op_occs", _id
1981 )
garciadeblasf53612b2024-07-12 14:44:37 +02001982 outdata = {"id": _id, "nsName": nsName}
almagiae47b9132022-05-17 14:12:22 +02001983 cherrypy.response.status = HTTPStatus.ACCEPTED.value
Gabriel Cuba84a60df2023-10-30 14:01:54 -05001984 elif topic == "ns_lcm_op_occs" and item == "cancel":
1985 indata["nsLcmOpOccId"] = _id
1986 self.engine.cancel_item(
1987 rollback, engine_session, "nslcmops", indata, None
1988 )
1989 self._set_location_header(main_topic, version, topic, _id)
1990 cherrypy.response.status = HTTPStatus.ACCEPTED.value
rshri2d386cb2024-07-05 14:35:51 +00001991 elif topic == "clusters" and _id == "register":
1992 # To register a cluster
1993 _id, _ = self.engine.add_item(
1994 rollback, engine_session, engine_topic, indata, kwargs
1995 )
1996 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00001997 outdata = {"_id": _id, "id": _id}
rshri2d386cb2024-07-05 14:35:51 +00001998 elif (
1999 topic
2000 in (
2001 "clusters",
2002 "infra_controller_profiles",
2003 "infra_config_profiles",
2004 "app_profiles",
2005 "resource_profiles",
2006 )
2007 and item is None
2008 ):
2009 # creates cluster, infra_controller_profiles, app_profiles, infra_config_profiles, and resource_profiles
2010 _id, _ = self.engine.new_item(
2011 rollback, engine_session, engine_topic, indata, kwargs
2012 )
2013 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002014 outdata = {"_id": _id, "id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00002015 elif topic == "ksus" and item:
2016 if item == "clone":
2017 _id = self.engine.clone(
2018 rollback,
2019 engine_session,
2020 engine_topic,
2021 _id,
2022 indata,
2023 kwargs,
2024 cherrypy.request.headers,
2025 )
2026 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002027 outdata = {"_id": _id, "id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00002028 if item == "move":
2029 op_id = self.engine.move_ksu(
2030 engine_session, engine_topic, _id, indata, kwargs
2031 )
2032 outdata = {"op_id": op_id}
2033 elif topic == "ksus" and _id == "delete":
2034 op_id = self.engine.delete_ksu(
2035 engine_session, engine_topic, _id, indata
2036 )
2037 outdata = {"op_id": op_id}
2038 elif topic == "ksus" and _id == "update":
2039 op_id = self.engine.edit_item(
2040 engine_session, engine_topic, _id, indata, kwargs
2041 )
2042 outdata = {"op_id": op_id}
2043 elif topic == "clusters" and item in ("upgrade", "scale"):
2044 op_id = self.engine.update_cluster(
2045 engine_session, engine_topic, _id, item, indata
2046 )
2047 outdata = {"op_id": op_id}
tiernof27c79b2018-03-12 17:08:42 +01002048 else:
garciadeblas4568a372021-03-24 09:19:48 +01002049 _id, op_id = self.engine.new_item(
2050 rollback,
2051 engine_session,
2052 engine_topic,
2053 indata,
2054 kwargs,
2055 cherrypy.request.headers,
2056 )
tiernob24258a2018-10-04 18:39:49 +02002057 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002058 outdata = {"_id": _id, "id": _id}
tiernobdebce92019-07-01 15:36:49 +00002059 if op_id:
2060 outdata["op_id"] = op_id
2061 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02002062 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02002063
tiernoc94c3df2018-02-09 15:38:54 +01002064 elif method == "DELETE":
2065 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01002066 outdata = self.engine.del_item_list(
2067 engine_session, engine_topic, kwargs
2068 )
tierno09c073e2018-04-26 13:36:48 +02002069 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01002070 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00002071 # for NS NSI generate an operation
2072 op_id = None
tierno701018c2019-06-25 11:13:14 +00002073 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02002074 nslcmop_desc = {
2075 "lcmOperationType": "terminate",
2076 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002077 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02002078 }
garciadeblasf53612b2024-07-12 14:44:37 +02002079 op_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01002080 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
2081 )
tierno22577432020-04-08 15:16:57 +00002082 if op_id:
garciadeblasf53612b2024-07-12 14:44:37 +02002083 outdata = {"_id": op_id, "nsName": nsName}
garciadeblas4568a372021-03-24 09:19:48 +01002084 elif (
2085 topic == "netslice_instances_content"
2086 and not engine_session["force"]
2087 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02002088 nsilcmop_desc = {
2089 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02002090 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002091 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02002092 }
garciadeblas4568a372021-03-24 09:19:48 +01002093 op_id, _ = self.engine.new_item(
2094 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
2095 )
tierno22577432020-04-08 15:16:57 +00002096 if op_id:
2097 outdata = {"_id": op_id}
rshri2d386cb2024-07-05 14:35:51 +00002098 elif topic == "clusters" and item == "deregister":
2099 if not op_id:
2100 op_id = self.engine.remove(
2101 engine_session, engine_topic, _id
2102 )
2103 if op_id:
2104 outdata = {"_id": op_id}
2105 cherrypy.response.status = (
2106 HTTPStatus.ACCEPTED.value
2107 if op_id
2108 else HTTPStatus.NO_CONTENT.value
2109 )
yshah53cc9eb2024-07-05 13:06:31 +00002110 elif topic == "ksus":
2111 op_id = self.engine.delete_ksu(
2112 engine_session, engine_topic, _id, indata
2113 )
2114 outdata = {"op_id": op_id}
tierno22577432020-04-08 15:16:57 +00002115 # if there is not any deletion in process, delete
rshri2d386cb2024-07-05 14:35:51 +00002116 elif not op_id:
tierno22577432020-04-08 15:16:57 +00002117 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2118 if op_id:
rshri2d386cb2024-07-05 14:35:51 +00002119 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01002120 cherrypy.response.status = (
2121 HTTPStatus.ACCEPTED.value
2122 if op_id
2123 else HTTPStatus.NO_CONTENT.value
2124 )
tierno09c073e2018-04-26 13:36:48 +02002125
tierno7ae10112018-05-18 14:36:02 +02002126 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00002127 op_id = None
tierno701018c2019-06-25 11:13:14 +00002128 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01002129 raise NbiException(
2130 "Nothing to update. Provide payload and/or query string",
2131 HTTPStatus.BAD_REQUEST,
2132 )
2133 if (
kayal2001f71c2e82024-06-25 15:26:24 +05302134 item
2135 in (
2136 "nsd_content",
2137 "package_content",
2138 "nst_content",
2139 "template_content",
2140 )
garciadeblas4568a372021-03-24 09:19:48 +01002141 and method == "PUT"
2142 ):
2143 completed = self.engine.upload_content(
2144 engine_session,
2145 engine_topic,
2146 _id,
2147 indata,
2148 kwargs,
2149 cherrypy.request.headers,
2150 )
tiernof27c79b2018-03-12 17:08:42 +01002151 if not completed:
2152 cherrypy.response.headers["Transaction-Id"] = id
rshri2d386cb2024-07-05 14:35:51 +00002153 elif item in (
2154 "app_profiles",
2155 "resource_profiles",
2156 "infra_controller_profiles",
2157 "infra_config_profiles",
2158 ):
2159 op_id = self.engine.edit(
2160 engine_session, engine_topic, _id, item, indata, kwargs
2161 )
yshah53cc9eb2024-07-05 13:06:31 +00002162 elif topic == "oka_packages" and method == "PATCH":
2163 if kwargs:
2164 op_id = self.engine.edit_item(
2165 engine_session, engine_topic, _id, None, kwargs
2166 )
2167 if indata:
yshahffcac5f2024-08-19 12:49:07 +00002168 if isinstance(indata, dict):
yshah53cc9eb2024-07-05 13:06:31 +00002169 op_id = self.engine.edit_item(
2170 engine_session, engine_topic, _id, indata, kwargs
2171 )
2172 else:
2173 cherrypy.request.headers["method"] = cherrypy.request.method
2174 completed = self.engine.upload_content(
2175 engine_session,
2176 engine_topic,
2177 _id,
2178 indata,
2179 {},
2180 cherrypy.request.headers,
2181 )
2182 if not completed:
2183 cherrypy.response.headers["Transaction-Id"] = id
2184 elif topic == "oka_packages" and method == "PUT":
2185 if indata:
2186 cherrypy.request.headers["method"] = cherrypy.request.method
2187 completed = self.engine.upload_content(
2188 engine_session,
2189 engine_topic,
2190 _id,
2191 indata,
2192 {},
2193 cherrypy.request.headers,
2194 )
2195 if not completed:
2196 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01002197 else:
garciadeblas4568a372021-03-24 09:19:48 +01002198 op_id = self.engine.edit_item(
2199 engine_session, engine_topic, _id, indata, kwargs
2200 )
tiernobdebce92019-07-01 15:36:49 +00002201
2202 if op_id:
2203 cherrypy.response.status = HTTPStatus.ACCEPTED.value
2204 outdata = {"op_id": op_id}
2205 else:
2206 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
2207 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01002208 else:
garciadeblas4568a372021-03-24 09:19:48 +01002209 raise NbiException(
2210 "Method {} not allowed".format(method),
2211 HTTPStatus.METHOD_NOT_ALLOWED,
2212 )
tiernoa6bb45d2019-06-14 09:45:39 +00002213
2214 # if Role information changes, it is needed to reload the information of roles
2215 if topic == "roles" and method != "GET":
2216 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01002217
garciadeblas4568a372021-03-24 09:19:48 +01002218 if (
2219 topic == "projects"
2220 and method == "DELETE"
2221 or topic in ["users", "roles"]
2222 and method in ["PUT", "PATCH", "DELETE"]
2223 ):
delacruzramoad682a52019-12-10 16:26:34 +01002224 self.authenticator.remove_token_from_cache()
2225
garciadeblasf53612b2024-07-12 14:44:37 +02002226 cef_event(
2227 cef_logger,
2228 {
2229 "name": "User Operation",
2230 "sourceUserName": token_info.get("username"),
2231 },
2232 )
2233 if topic == "ns_instances_content" and url_id:
2234 nsName = (
2235 outdata.get("name") if method == "GET" else outdata.get("nsName")
2236 )
elumalai7802ff82023-04-24 20:38:32 +05302237 cef_event(
2238 cef_logger,
2239 {
garciadeblasf53612b2024-07-12 14:44:37 +02002240 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2241 log_mapping[method],
2242 topic,
2243 nsName,
2244 outdata.get("id"),
2245 token_info.get("project_name"),
2246 ),
2247 },
2248 )
2249 cherrypy.log("{}".format(cef_logger))
2250 elif topic == "ns_instances_content" and method == "POST":
2251 cef_event(
2252 cef_logger,
2253 {
2254 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2255 log_mapping[method],
2256 topic,
2257 outdata.get("nsName"),
2258 outdata.get("id"),
2259 token_info.get("project_name"),
2260 ),
2261 },
2262 )
2263 cherrypy.log("{}".format(cef_logger))
2264 elif topic in ("ns_instances", "vnf_instances") and item:
2265 cef_event(
2266 cef_logger,
2267 {
2268 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2269 log_mapping[method],
2270 topic,
2271 outdata.get("nsName"),
2272 url_id,
2273 token_info.get("project_name"),
2274 ),
2275 },
2276 )
2277 cherrypy.log("{}".format(cef_logger))
2278 elif item is not None:
2279 cef_event(
2280 cef_logger,
2281 {
elumalai7802ff82023-04-24 20:38:32 +05302282 "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format(
2283 item,
2284 topic,
2285 url_id,
2286 token_info.get("project_name"),
2287 ),
2288 },
2289 )
2290 cherrypy.log("{}".format(cef_logger))
2291 else:
2292 cef_event(
2293 cef_logger,
2294 {
elumalai7802ff82023-04-24 20:38:32 +05302295 "message": "{} {} {}, Project={} Outcome=Success".format(
2296 log_mapping[method],
2297 topic,
2298 url_id,
2299 token_info.get("project_name"),
2300 ),
2301 },
2302 )
2303 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002304 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02002305 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01002306 if isinstance(
2307 e,
2308 (
2309 NbiException,
2310 EngineException,
2311 DbException,
2312 FsException,
2313 MsgException,
2314 AuthException,
2315 ValidationError,
2316 AuthconnException,
2317 ),
2318 ):
tiernob24258a2018-10-04 18:39:49 +02002319 http_code_value = cherrypy.response.status = e.http_code.value
2320 http_code_name = e.http_code.name
2321 cherrypy.log("Exception {}".format(e))
2322 else:
garciadeblas4568a372021-03-24 09:19:48 +01002323 http_code_value = (
2324 cherrypy.response.status
2325 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01002326 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02002327 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02002328 if hasattr(outdata, "close"): # is an open file
2329 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02002330 error_text = str(e)
2331 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02002332 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02002333 try:
tiernocc103432018-10-19 14:10:35 +02002334 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01002335 self.engine.db.set_one(
2336 rollback_item["topic"],
2337 {"_id": rollback_item["_id"]},
2338 rollback_item["content"],
2339 fail_on_empty=False,
2340 )
preethika.p329b8182020-04-22 12:25:39 +05302341 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01002342 self.engine.db.del_list(
2343 rollback_item["topic"],
2344 rollback_item["filter"],
garciadeblas4568a372021-03-24 09:19:48 +01002345 )
tiernocc103432018-10-19 14:10:35 +02002346 else:
garciadeblas4568a372021-03-24 09:19:48 +01002347 self.engine.db.del_one(
2348 rollback_item["topic"],
2349 {"_id": rollback_item["_id"]},
2350 fail_on_empty=False,
2351 )
tierno3ace63c2018-05-03 17:51:43 +02002352 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01002353 rollback_error_text = "Rollback Exception {}: {}".format(
2354 rollback_item, e2
2355 )
tiernob24258a2018-10-04 18:39:49 +02002356 cherrypy.log(rollback_error_text)
2357 error_text += ". " + rollback_error_text
2358 # if isinstance(e, MsgException):
2359 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
2360 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01002361 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02002362 "code": http_code_name,
2363 "status": http_code_value,
2364 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01002365 }
elumalai7802ff82023-04-24 20:38:32 +05302366 if item is not None and token_info is not None:
2367 cef_event(
2368 cef_logger,
2369 {
2370 "name": "User Operation",
2371 "sourceUserName": token_info.get("username", None),
2372 "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format(
2373 item,
2374 topic,
2375 url_id,
2376 token_info.get("project_name", None),
2377 ),
2378 "severity": "2",
2379 },
2380 )
2381 cherrypy.log("{}".format(cef_logger))
2382 elif token_info is not None:
2383 cef_event(
2384 cef_logger,
2385 {
2386 "name": "User Operation",
2387 "sourceUserName": token_info.get("username", None),
2388 "message": "{} {} {}, Project={} Outcome=Failure".format(
2389 item,
2390 topic,
2391 url_id,
2392 token_info.get("project_name", None),
2393 ),
2394 "severity": "2",
2395 },
2396 )
2397 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002398 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01002399 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00002400 finally:
2401 if token_info:
2402 self._format_login(token_info)
2403 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
2404 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
2405 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01002406 cherrypy.request.login += ";{}={}".format(
2407 logging_id, outdata[logging_id][:36]
2408 )
tiernoc94c3df2018-02-09 15:38:54 +01002409
2410
tiernoc94c3df2018-02-09 15:38:54 +01002411def _start_service():
2412 """
2413 Callback function called when cherrypy.engine starts
2414 Override configuration with env variables
2415 Set database, storage, message configuration
2416 Init database with admin/admin user password
2417 """
tierno932499c2019-01-28 17:28:10 +00002418 global nbi_server
2419 global subscription_thread
elumalai7802ff82023-04-24 20:38:32 +05302420 global cef_logger
Rahulc72bc8e2023-12-05 11:54:38 +00002421 global current_backend
tiernoc94c3df2018-02-09 15:38:54 +01002422 cherrypy.log.error("Starting osm_nbi")
2423 # update general cherrypy configuration
2424 update_dict = {}
2425
garciadeblas4568a372021-03-24 09:19:48 +01002426 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01002427 for k, v in environ.items():
garciadeblas6d83f8f2023-06-19 22:34:49 +02002428 if k == "OSMNBI_USER_MANAGEMENT":
garciadeblas7a0dace2024-09-17 18:00:50 +02002429 feature_state = v.lower() == "true"
garciadeblas6d83f8f2023-06-19 22:34:49 +02002430 engine_config["authentication"]["user_management"] = feature_state
Adurtid68526d2023-11-14 11:14:53 +00002431 elif k == "OSMNBI_PWD_EXPIRE_DAYS":
2432 pwd_expire_days = int(v)
2433 engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days
2434 elif k == "OSMNBI_MAX_PWD_ATTEMPT":
2435 max_pwd_attempt = int(v)
2436 engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt
2437 elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS":
2438 account_expire_days = int(v)
2439 engine_config["authentication"]["account_expire_days"] = account_expire_days
jeganbe1a3df2024-06-04 12:05:19 +00002440 elif k == "OSMNBI_SMTP_SERVER":
2441 engine_config["authentication"]["smtp_server"] = v
2442 engine_config["authentication"]["all"] = environ
2443 elif k == "OSMNBI_SMTP_PORT":
2444 port = int(v)
2445 engine_config["authentication"]["smtp_port"] = port
2446 elif k == "OSMNBI_SENDER_EMAIL":
2447 engine_config["authentication"]["sender_email"] = v
2448 elif k == "OSMNBI_EMAIL_PASSWORD":
2449 engine_config["authentication"]["sender_password"] = v
2450 elif k == "OSMNBI_OTP_RETRY_COUNT":
2451 otp_retry_count = int(v)
2452 engine_config["authentication"]["retry_count"] = otp_retry_count
2453 elif k == "OSMNBI_OTP_EXPIRY_TIME":
2454 otp_expiry_time = int(v)
2455 engine_config["authentication"]["otp_expiry_time"] = otp_expiry_time
tiernoc94c3df2018-02-09 15:38:54 +01002456 if not k.startswith("OSMNBI_"):
2457 continue
tiernoe1281182018-05-22 12:24:36 +02002458 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01002459 if not k2:
2460 continue
2461 try:
2462 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01002463 if k == "OSMNBI_STATIC_DIR":
2464 engine_config["/static"]["tools.staticdir.dir"] = v
2465 engine_config["/static"]["tools.staticdir.on"] = True
2466 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
2467 update_dict["server.socket_port"] = int(v)
2468 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
2469 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02002470 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01002471 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002472 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02002473 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002474 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01002475 engine_config[k1][k2] = int(v)
2476 else:
2477 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002478
tiernoc94c3df2018-02-09 15:38:54 +01002479 except ValueError as e:
2480 cherrypy.log.error("Ignoring environ '{}': " + str(e))
2481 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01002482 cherrypy.log(
2483 "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
2484 )
tiernoc94c3df2018-02-09 15:38:54 +01002485
2486 if update_dict:
2487 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02002488 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01002489
2490 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01002491 log_format_simple = (
2492 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
2493 )
2494 log_formatter_simple = logging.Formatter(
2495 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
2496 )
tiernoc94c3df2018-02-09 15:38:54 +01002497 logger_server = logging.getLogger("cherrypy.error")
2498 logger_access = logging.getLogger("cherrypy.access")
2499 logger_cherry = logging.getLogger("cherrypy")
2500 logger_nbi = logging.getLogger("nbi")
2501
tiernof5298be2018-05-16 14:43:57 +02002502 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01002503 file_handler = logging.handlers.RotatingFileHandler(
2504 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
2505 )
tiernoc94c3df2018-02-09 15:38:54 +01002506 file_handler.setFormatter(log_formatter_simple)
2507 logger_cherry.addHandler(file_handler)
2508 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02002509 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01002510 for format_, logger in {
2511 "nbi.server %(filename)s:%(lineno)s": logger_server,
2512 "nbi.access %(filename)s:%(lineno)s": logger_access,
2513 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
2514 }.items():
tiernob24258a2018-10-04 18:39:49 +02002515 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01002516 log_formatter_cherry = logging.Formatter(
2517 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
2518 )
tiernob24258a2018-10-04 18:39:49 +02002519 str_handler = logging.StreamHandler()
2520 str_handler.setFormatter(log_formatter_cherry)
2521 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01002522
tiernof5298be2018-05-16 14:43:57 +02002523 if engine_config["global"].get("log.level"):
2524 logger_cherry.setLevel(engine_config["global"]["log.level"])
2525 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01002526
2527 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01002528 for k1, logname in {
2529 "message": "nbi.msg",
2530 "database": "nbi.db",
2531 "storage": "nbi.fs",
2532 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01002533 engine_config[k1]["logger_name"] = logname
2534 logger_module = logging.getLogger(logname)
2535 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01002536 file_handler = logging.handlers.RotatingFileHandler(
2537 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
2538 )
tiernoc94c3df2018-02-09 15:38:54 +01002539 file_handler.setFormatter(log_formatter_simple)
2540 logger_module.addHandler(file_handler)
2541 if "loglevel" in engine_config[k1]:
2542 logger_module.setLevel(engine_config[k1]["loglevel"])
2543 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01002544 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
2545 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
2546 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
2547 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
2548 target_version=auth_database_version
2549 )
tiernobee508e2019-01-21 11:21:49 +00002550
elumalai7802ff82023-04-24 20:38:32 +05302551 cef_logger = cef_event_builder(engine_config["authentication"])
2552
tierno932499c2019-01-28 17:28:10 +00002553 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01002554 subscription_thread = SubscriptionThread(
2555 config=engine_config, engine=nbi_server.engine
2556 )
tierno932499c2019-01-28 17:28:10 +00002557 subscription_thread.start()
2558 # Do not capture except SubscriptionException
2559
tiernob2e48bd2020-02-04 15:47:18 +00002560 backend = engine_config["authentication"]["backend"]
Rahulc72bc8e2023-12-05 11:54:38 +00002561 current_backend = backend
garciadeblas4568a372021-03-24 09:19:48 +01002562 cherrypy.log.error(
2563 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
2564 nbi_version, nbi_version_date, backend
2565 )
2566 )
tiernoc94c3df2018-02-09 15:38:54 +01002567
2568
2569def _stop_service():
2570 """
2571 Callback function called when cherrypy.engine stops
2572 TODO: Ending database connections.
2573 """
tierno932499c2019-01-28 17:28:10 +00002574 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01002575 if subscription_thread:
2576 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00002577 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01002578 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01002579 cherrypy.log.error("Stopping osm_nbi")
2580
tierno2236d202018-05-16 19:05:16 +02002581
tiernof5298be2018-05-16 14:43:57 +02002582def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00002583 global nbi_server
tierno932499c2019-01-28 17:28:10 +00002584 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01002585 cherrypy.engine.subscribe("start", _start_service)
2586 cherrypy.engine.subscribe("stop", _stop_service)
2587 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02002588
2589
2590def usage():
garciadeblas4568a372021-03-24 09:19:48 +01002591 print(
2592 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02002593 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
2594 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01002595 """.format(
2596 sys.argv[0]
2597 )
2598 )
tierno2236d202018-05-16 19:05:16 +02002599 # --log-socket-host HOST: send logs to this host")
2600 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002601
2602
garciadeblas4568a372021-03-24 09:19:48 +01002603if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002604 try:
2605 # load parameters and configuration
2606 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2607 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2608 config_file = None
2609 for o, a in opts:
2610 if o in ("-h", "--help"):
2611 usage()
2612 sys.exit()
2613 elif o in ("-c", "--config"):
2614 config_file = a
2615 # elif o == "--log-socket-port":
2616 # log_socket_port = a
2617 # elif o == "--log-socket-host":
2618 # log_socket_host = a
2619 # elif o == "--log-file":
2620 # log_file = a
2621 else:
2622 assert False, "Unhandled option"
2623 if config_file:
2624 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002625 print(
2626 "configuration file '{}' that not exist".format(config_file),
2627 file=sys.stderr,
2628 )
tiernof5298be2018-05-16 14:43:57 +02002629 exit(1)
2630 else:
garciadeblas4568a372021-03-24 09:19:48 +01002631 for config_file in (
2632 __file__[: __file__.rfind(".")] + ".cfg",
2633 "./nbi.cfg",
2634 "/etc/osm/nbi.cfg",
2635 ):
tiernof5298be2018-05-16 14:43:57 +02002636 if path.isfile(config_file):
2637 break
2638 else:
garciadeblas4568a372021-03-24 09:19:48 +01002639 print(
2640 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2641 file=sys.stderr,
2642 )
tiernof5298be2018-05-16 14:43:57 +02002643 exit(1)
2644 nbi(config_file)
2645 except getopt.GetoptError as e:
2646 print(str(e), file=sys.stderr)
2647 # usage()
2648 exit(1)