blob: 77c009f8acd673e7252da455fe426af88fd3b346 [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>": {
yshah99122b82024-11-18 07:05:29 +0000688 "METHODS": ("GET", "PATCH", "DELETE"),
rshri2d386cb2024-07-05 14:35:51 +0000689 "ROLE_PERMISSION": "k8scluster:id:",
690 "app_profiles": {
691 "METHODS": ("PATCH", "GET"),
692 "ROLE_PERMISSION": "k8scluster:id:app_profiles:",
693 },
694 "infra_controller_profiles": {
695 "METHODS": ("PATCH", "GET"),
696 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:",
697 },
698 "infra_config_profiles": {
699 "METHODS": ("PATCH", "GET"),
700 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:",
701 },
702 "resource_profiles": {
703 "METHODS": ("PATCH", "GET"),
704 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:",
705 },
706 "deregister": {
707 "METHODS": ("DELETE",),
708 "ROLE_PERMISSION": "k8scluster:id:deregister:",
709 },
yshah53cc9eb2024-07-05 13:06:31 +0000710 "get_creds": {
711 "METHODS": ("GET",),
712 "ROLE_PERMISSION": "k8scluster:id:get_creds:",
713 },
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 },
yshah99122b82024-11-18 07:05:29 +0000722 "update": {
723 "METHODS": ("POST",),
724 "ROLE_PERMISSION": "k8scluster:id:update:",
725 },
yshah53cc9eb2024-07-05 13:06:31 +0000726 "scale": {
727 "METHODS": ("POST",),
728 "ROLE_PERMISSION": "k8scluster:id:scale:",
729 },
730 "upgrade": {
731 "METHODS": ("POST",),
732 "ROLE_PERMISSION": "k8scluster:id:upgrade:",
733 },
rshri2d386cb2024-07-05 14:35:51 +0000734 },
735 "register": {
736 "METHODS": ("POST",),
737 "ROLE_PERMISSION": "k8scluster:register:",
738 },
739 },
740 "app_profiles": {
741 "METHODS": ("POST", "GET"),
742 "ROLE_PERMISSION": "k8scluster:app_profiles:",
743 "<ID>": {
744 "METHODS": ("GET", "PATCH", "DELETE"),
745 "ROLE_PERMISSION": "k8scluster:app_profiles:id:",
746 },
747 },
748 "infra_controller_profiles": {
749 "METHODS": ("POST", "GET"),
750 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:",
751 "<ID>": {
752 "METHODS": ("GET", "PATCH", "DELETE"),
753 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:id:",
754 },
755 },
756 "infra_config_profiles": {
757 "METHODS": ("POST", "GET"),
758 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:",
759 "<ID>": {
760 "METHODS": ("GET", "PATCH", "DELETE"),
761 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:id:",
762 },
763 },
764 "resource_profiles": {
765 "METHODS": ("POST", "GET"),
766 "ROLE_PERMISSION": "k8scluster:resource_profiles:",
767 "<ID>": {
768 "METHODS": ("GET", "PATCH", "DELETE"),
769 "ROLE_PERMISSION": "k8scluster:resource_profiles:id:",
770 },
771 },
772 }
773 },
yshah53cc9eb2024-07-05 13:06:31 +0000774 "ksu": {
775 "v1": {
776 "ksus": {
777 "METHODS": ("GET", "POST"),
778 "ROLE_PERMISSION": "ksu:",
779 "<ID>": {
780 "METHODS": ("GET", "PATCH", "DELETE"),
781 "ROLE_PERMISSION": "ksu:id:",
782 "clone": {
783 "METHODS": ("POST",),
784 "ROLE_PERMISSION": "ksu:id:clone:",
785 },
786 "move": {
787 "METHODS": ("POST",),
788 "ROLE_PERMISSION": "ksu:id:move:",
789 },
790 },
791 "update": {
792 "METHODS": ("POST",),
793 "ROLE_PERMISSION": "ksu:",
794 },
795 "delete": {
796 "METHODS": ("POST",),
797 "ROLE_PERMISSION": "ksu:",
798 },
799 },
800 }
801 },
802 "oka": {
803 "v1": {
804 "oka_packages": {
805 "METHODS": ("GET", "POST"),
806 "ROLE_PERMISSION": "oka_pkg:",
807 "<ID>": {
808 "METHODS": ("GET", "PATCH", "DELETE", "PUT"),
809 "ROLE_PERMISSION": "oka_pkg:id:",
810 },
811 }
812 }
813 },
tierno701018c2019-06-25 11:13:14 +0000814}
815
tiernoc94c3df2018-02-09 15:38:54 +0100816
817class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100818 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
819 Exception.__init__(self, message)
820 self.http_code = http_code
821
822
823class Server(object):
824 instance = 0
825 # to decode bytes to str
826 reader = getreader("utf-8")
827
828 def __init__(self):
829 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000830 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100831 self.engine = Engine(self.authenticator)
yshah53cc9eb2024-07-05 13:06:31 +0000832 self.logger = logging.getLogger("nbi.server")
tiernoc94c3df2018-02-09 15:38:54 +0100833
tiernoc94c3df2018-02-09 15:38:54 +0100834 def _format_in(self, kwargs):
garciadeblasf2af4a12023-01-24 16:56:54 +0100835 error_text = "" # error_text must be initialized outside try
tiernoc94c3df2018-02-09 15:38:54 +0100836 try:
837 indata = None
838 if cherrypy.request.body.length:
839 error_text = "Invalid input format "
840
841 if "Content-Type" in cherrypy.request.headers:
842 if "application/json" in cherrypy.request.headers["Content-Type"]:
843 error_text = "Invalid json format "
844 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100845 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100846 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
847 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100848 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100849 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100850 elif (
851 "application/binary" in cherrypy.request.headers["Content-Type"]
852 or "application/gzip"
853 in cherrypy.request.headers["Content-Type"]
854 or "application/zip" in cherrypy.request.headers["Content-Type"]
855 or "text/plain" in cherrypy.request.headers["Content-Type"]
856 ):
tiernof27c79b2018-03-12 17:08:42 +0100857 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100858 elif (
859 "multipart/form-data"
860 in cherrypy.request.headers["Content-Type"]
861 ):
yshah53cc9eb2024-07-05 13:06:31 +0000862 if (
863 "descriptor_file" in kwargs
864 or "package" in kwargs
865 and "name" in kwargs
866 ):
867 filecontent = ""
868 if "descriptor_file" in kwargs:
869 filecontent = kwargs.pop("descriptor_file")
870 if "package" in kwargs:
871 filecontent = kwargs.pop("package")
tiernoc94c3df2018-02-09 15:38:54 +0100872 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100873 raise NbiException(
874 "empty file or content", HTTPStatus.BAD_REQUEST
875 )
yshah53cc9eb2024-07-05 13:06:31 +0000876 indata = filecontent
877 if filecontent.content_type.value:
878 cherrypy.request.headers[
879 "Content-Type"
880 ] = filecontent.content_type.value
881 elif "package" in kwargs:
882 filecontent = kwargs.pop("package")
883 if not filecontent.file:
884 raise NbiException(
885 "empty file or content", HTTPStatus.BAD_REQUEST
886 )
887 indata = filecontent
tiernoc94c3df2018-02-09 15:38:54 +0100888 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100889 cherrypy.request.headers[
890 "Content-Type"
891 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100892 else:
893 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
894 # "Only 'Content-Type' of type 'application/json' or
895 # 'application/yaml' for input format are available")
896 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100897 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100898 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100899 else:
900 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100901 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100902 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100903 if not indata:
904 indata = {}
tiernoc94c3df2018-02-09 15:38:54 +0100905 format_yaml = False
906 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
907 format_yaml = True
908
909 for k, v in kwargs.items():
910 if isinstance(v, str):
911 if v == "":
912 kwargs[k] = None
913 elif format_yaml:
914 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100915 kwargs[k] = yaml.safe_load(v)
tiernoe1281182018-05-22 12:24:36 +0200916 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100917 pass
garciadeblas4568a372021-03-24 09:19:48 +0100918 elif (
919 k.endswith(".gt")
920 or k.endswith(".lt")
921 or k.endswith(".gte")
922 or k.endswith(".lte")
923 ):
tiernoc94c3df2018-02-09 15:38:54 +0100924 try:
925 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200926 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100927 try:
928 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200929 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100930 pass
931 elif v.find(",") > 0:
932 kwargs[k] = v.split(",")
933 elif isinstance(v, (list, tuple)):
934 for index in range(0, len(v)):
935 if v[index] == "":
936 v[index] = None
937 elif format_yaml:
938 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100939 v[index] = yaml.safe_load(v[index])
tiernoe1281182018-05-22 12:24:36 +0200940 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100941 pass
942
tiernof27c79b2018-03-12 17:08:42 +0100943 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100944 except (ValueError, yaml.YAMLError) as exc:
945 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
946 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100947 raise NbiException(
948 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
949 )
tiernob92094f2018-05-11 13:44:22 +0200950 except Exception as exc:
951 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100952
953 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000954 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100955 """
956 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100957 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000958 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000959 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100960 :return: None
961 """
tierno0f98af52018-03-19 10:28:22 +0100962 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100963 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100964 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100965 return html.format(
966 data, cherrypy.request, cherrypy.response, token_info
967 )
tierno09c073e2018-04-26 13:36:48 +0200968 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100969 return
970 elif hasattr(data, "read"): # file object
971 if _format:
972 cherrypy.response.headers["Content-Type"] = _format
973 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100974 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100975 else:
garciadeblas4568a372021-03-24 09:19:48 +0100976 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100977 # TODO check that cherrypy close file. If not implement pending things to close per thread next
978 return data
tierno0f98af52018-03-19 10:28:22 +0100979 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000980 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100981 return html.format(
982 data, cherrypy.request, cherrypy.response, token_info
983 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000984 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100985 pass
garciadeblas4568a372021-03-24 09:19:48 +0100986 elif "application/json" in accept or (
987 cherrypy.response.status and cherrypy.response.status >= 300
988 ):
989 cherrypy.response.headers[
990 "Content-Type"
991 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000992 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100993 return a.encode("utf8")
994 cherrypy.response.headers["Content-Type"] = "application/yaml"
995 return yaml.safe_dump(
996 data,
997 explicit_start=True,
998 indent=4,
999 default_flow_style=False,
1000 tags=False,
1001 encoding="utf-8",
1002 allow_unicode=True,
1003 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +01001004
1005 @cherrypy.expose
1006 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +00001007 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001008 try:
1009 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +00001010 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +01001011 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +01001012 else:
garciadeblas4568a372021-03-24 09:19:48 +01001013 raise cherrypy.HTTPError(
1014 HTTPStatus.METHOD_NOT_ALLOWED.value,
1015 "Method {} not allowed for tokens".format(cherrypy.request.method),
1016 )
tiernoc94c3df2018-02-09 15:38:54 +01001017
tierno701018c2019-06-25 11:13:14 +00001018 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001019
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001020 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +00001021 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +01001022 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +00001023 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001024
1025 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +02001026 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +02001027 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +02001028 try:
1029 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001030 raise NbiException(
1031 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
1032 )
tierno55945e72018-04-06 16:40:27 +02001033 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001034 raise NbiException(
1035 "Invalid URL or query string for version",
1036 HTTPStatus.METHOD_NOT_ALLOWED,
1037 )
tierno9c630112019-08-29 14:21:41 +00001038 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +00001039 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
1040 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +02001041 except NbiException as e:
1042 cherrypy.response.status = e.http_code.value
1043 problem_details = {
1044 "code": e.http_code.name,
1045 "status": e.http_code.value,
1046 "detail": str(e),
1047 }
1048 return self._format_out(problem_details, None)
1049
tierno12eac3c2020-03-19 23:22:08 +00001050 def domain(self):
1051 try:
1052 domains = {
garciadeblas4568a372021-03-24 09:19:48 +01001053 "user_domain_name": cherrypy.tree.apps["/osm"]
1054 .config["authentication"]
1055 .get("user_domain_name"),
1056 "project_domain_name": cherrypy.tree.apps["/osm"]
1057 .config["authentication"]
1058 .get("project_domain_name"),
1059 }
tierno12eac3c2020-03-19 23:22:08 +00001060 return self._format_out(domains)
1061 except NbiException as e:
1062 cherrypy.response.status = e.http_code.value
1063 problem_details = {
1064 "code": e.http_code.name,
1065 "status": e.http_code.value,
1066 "detail": str(e),
1067 }
1068 return self._format_out(problem_details, None)
1069
tiernoa5035702019-07-29 08:54:42 +00001070 @staticmethod
1071 def _format_login(token_info):
1072 """
1073 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
1074 log this information
1075 :param token_info: Dictionary with token content
1076 :return: None
1077 """
1078 cherrypy.request.login = token_info.get("username", "-")
1079 if token_info.get("project_name"):
1080 cherrypy.request.login += "/" + token_info["project_name"]
1081 if token_info.get("id"):
1082 cherrypy.request.login += ";session=" + token_info["id"][0:12]
1083
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001084 # NS Fault Management
1085 @cherrypy.expose
garciadeblasf2af4a12023-01-24 16:56:54 +01001086 def nsfm(
1087 self,
1088 version=None,
1089 topic=None,
1090 uuid=None,
1091 project_name=None,
1092 ns_id=None,
1093 *args,
1094 **kwargs
1095 ):
1096 if topic == "alarms":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001097 try:
1098 method = cherrypy.request.method
garciadeblasf2af4a12023-01-24 16:56:54 +01001099 role_permission = self._check_valid_url_method(
1100 method, "nsfm", version, topic, None, None, *args
1101 )
1102 query_string_operations = self._extract_query_string_operations(
1103 kwargs, method
1104 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001105
garciadeblasf2af4a12023-01-24 16:56:54 +01001106 self.authenticator.authorize(
1107 role_permission, query_string_operations, None
1108 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001109
1110 # to handle get request
garciadeblasf2af4a12023-01-24 16:56:54 +01001111 if cherrypy.request.method == "GET":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001112 # if request is on basis of uuid
garciadeblasf2af4a12023-01-24 16:56:54 +01001113 if uuid and uuid != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001114 try:
1115 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
garciadeblasf2af4a12023-01-24 16:56:54 +01001116 alarm_action = self.engine.db.get_one(
1117 "alarms_action", {"uuid": uuid}
1118 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001119 alarm.update(alarm_action)
garciadeblasf2af4a12023-01-24 16:56:54 +01001120 vnf = self.engine.db.get_one(
1121 "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}
1122 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001123 alarm["vnf-id"] = vnf["_id"]
1124 return self._format_out(str(alarm))
1125 except Exception:
1126 return self._format_out("Please provide valid alarm uuid")
garciadeblasf2af4a12023-01-24 16:56:54 +01001127 elif ns_id and ns_id != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001128 # if request is on basis of ns_id
1129 try:
garciadeblasf2af4a12023-01-24 16:56:54 +01001130 alarms = self.engine.db.get_list(
1131 "alarms", {"tags.ns_id": ns_id}
1132 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001133 for alarm in alarms:
garciadeblasf2af4a12023-01-24 16:56:54 +01001134 alarm_action = self.engine.db.get_one(
1135 "alarms_action", {"uuid": alarm["uuid"]}
1136 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001137 alarm.update(alarm_action)
1138 return self._format_out(str(alarms))
1139 except Exception:
1140 return self._format_out("Please provide valid ns id")
1141 else:
1142 # to return only alarm which are related to given project
garciadeblasf2af4a12023-01-24 16:56:54 +01001143 project = self.engine.db.get_one(
1144 "projects", {"name": project_name}
1145 )
1146 project_id = project.get("_id")
1147 ns_list = self.engine.db.get_list(
1148 "nsrs", {"_admin.projects_read": project_id}
1149 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001150 ns_ids = []
1151 for ns in ns_list:
1152 ns_ids.append(ns.get("_id"))
1153 alarms = self.engine.db.get_list("alarms")
garciadeblasf2af4a12023-01-24 16:56:54 +01001154 alarm_list = [
1155 alarm
1156 for alarm in alarms
1157 if alarm["tags"]["ns_id"] in ns_ids
1158 ]
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001159 for alrm in alarm_list:
garciadeblasf2af4a12023-01-24 16:56:54 +01001160 action = self.engine.db.get_one(
1161 "alarms_action", {"uuid": alrm.get("uuid")}
1162 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001163 alrm.update(action)
1164 return self._format_out(str(alarm_list))
1165 # to handle patch request for alarm update
garciadeblasf2af4a12023-01-24 16:56:54 +01001166 elif cherrypy.request.method == "PATCH":
garciadeblas4cd875d2023-02-14 19:05:34 +01001167 data = yaml.safe_load(cherrypy.request.body)
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001168 try:
1169 # check if uuid is valid
1170 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
1171 except Exception:
1172 return self._format_out("Please provide valid alarm uuid.")
1173 if data.get("is_enable") is not None:
1174 if data.get("is_enable"):
garciadeblasf2af4a12023-01-24 16:56:54 +01001175 alarm_status = "ok"
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001176 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001177 alarm_status = "disabled"
1178 self.engine.db.set_one(
1179 "alarms",
1180 {"uuid": data.get("uuid")},
1181 {"alarm_status": alarm_status},
1182 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001183 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001184 self.engine.db.set_one(
1185 "alarms",
1186 {"uuid": data.get("uuid")},
1187 {"threshold": data.get("threshold")},
1188 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001189 return self._format_out("Alarm updated")
1190 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01001191 if isinstance(
1192 e,
1193 (
1194 NbiException,
1195 EngineException,
1196 DbException,
1197 FsException,
1198 MsgException,
1199 AuthException,
1200 ValidationError,
1201 AuthconnException,
1202 ),
1203 ):
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001204 http_code_value = cherrypy.response.status = e.http_code.value
1205 http_code_name = e.http_code.name
1206 cherrypy.log("Exception {}".format(e))
1207 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001208 http_code_value = (
1209 cherrypy.response.status
1210 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001211 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
1212 http_code_name = HTTPStatus.BAD_REQUEST.name
1213 problem_details = {
1214 "code": http_code_name,
1215 "status": http_code_value,
1216 "detail": str(e),
1217 }
1218 return self._format_out(problem_details)
1219
tierno55945e72018-04-06 16:40:27 +02001220 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +01001221 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +00001222 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001223 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +01001224 indata = self._format_in(kwargs)
1225 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001226 raise NbiException(
1227 "Expected application/yaml or application/json Content-Type",
1228 HTTPStatus.BAD_REQUEST,
1229 )
tiernoa5035702019-07-29 08:54:42 +00001230
1231 if method == "GET":
1232 token_info = self.authenticator.authorize()
1233 # for logging
1234 self._format_login(token_info)
1235 if token_id:
1236 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +01001237 else:
tiernoa5035702019-07-29 08:54:42 +00001238 outdata = self.authenticator.get_token_list(token_info)
1239 elif method == "POST":
1240 try:
1241 token_info = self.authenticator.authorize()
1242 except Exception:
1243 token_info = None
1244 if kwargs:
1245 indata.update(kwargs)
1246 # This is needed to log the user when authentication fails
1247 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +01001248 outdata = token_info = self.authenticator.new_token(
1249 token_info, indata, cherrypy.request.remote
1250 )
jeganbe1a3df2024-06-04 12:05:19 +00001251 if outdata.get("email") or outdata.get("otp") == "invalid":
1252 return self._format_out(outdata, token_info)
garciadeblasf2af4a12023-01-24 16:56:54 +01001253 cherrypy.session["Authorization"] = outdata["_id"] # pylint: disable=E1101
tiernoa5035702019-07-29 08:54:42 +00001254 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1255 # for logging
1256 self._format_login(token_info)
jeganbe1a3df2024-06-04 12:05:19 +00001257 if outdata.get("otp") == "valid":
1258 outdata = {
1259 "id": outdata["id"],
1260 "message": "valid_otp",
1261 "user_id": outdata["user_id"],
1262 }
selvi.ja9a1fc82022-04-04 06:54:30 +00001263 # password expiry check
jeganbe1a3df2024-06-04 12:05:19 +00001264 elif self.authenticator.check_password_expiry(outdata):
garciadeblasf2af4a12023-01-24 16:56:54 +01001265 outdata = {
1266 "id": outdata["id"],
1267 "message": "change_password",
1268 "user_id": outdata["user_id"],
1269 }
tiernoa5035702019-07-29 08:54:42 +00001270 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1271 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
elumalai7802ff82023-04-24 20:38:32 +05301272 cef_event(
1273 cef_logger,
1274 {
1275 "name": "User Login",
1276 "sourceUserName": token_info.get("username"),
1277 "message": "User Logged In, Project={} Outcome=Success".format(
1278 token_info.get("project_name")
1279 ),
1280 },
1281 )
1282 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001283 elif method == "DELETE":
1284 if not token_id and "id" in kwargs:
1285 token_id = kwargs["id"]
1286 elif not token_id:
1287 token_info = self.authenticator.authorize()
1288 # for logging
1289 self._format_login(token_info)
1290 token_id = token_info["_id"]
Rahulc72bc8e2023-12-05 11:54:38 +00001291 if current_backend != "keystone":
1292 token_details = self.engine.db.get_one("tokens", {"_id": token_id})
1293 current_user = token_details.get("username")
1294 current_project = token_details.get("project_name")
1295 else:
1296 current_user = "keystone backend"
1297 current_project = "keystone backend"
tiernoa5035702019-07-29 08:54:42 +00001298 outdata = self.authenticator.del_token(token_id)
1299 token_info = None
garciadeblasf2af4a12023-01-24 16:56:54 +01001300 cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101
elumalai7802ff82023-04-24 20:38:32 +05301301 cef_event(
1302 cef_logger,
1303 {
1304 "name": "User Logout",
1305 "sourceUserName": current_user,
1306 "message": "User Logged Out, Project={} Outcome=Success".format(
1307 current_project
1308 ),
1309 },
1310 )
1311 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001312 # cherrypy.response.cookie["Authorization"] = token_id
1313 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1314 else:
garciadeblas4568a372021-03-24 09:19:48 +01001315 raise NbiException(
1316 "Method {} not allowed for token".format(method),
1317 HTTPStatus.METHOD_NOT_ALLOWED,
1318 )
tiernoa5035702019-07-29 08:54:42 +00001319 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001320
1321 @cherrypy.expose
1322 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001323 if not cherrypy.config.get("server.enable_test") or (
1324 isinstance(cherrypy.config["server.enable_test"], str)
1325 and cherrypy.config["server.enable_test"].lower() == "false"
1326 ):
tierno4836bac2020-01-15 14:41:48 +00001327 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1328 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001329 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001330 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001331 return (
1332 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1333 "sleep/<time>\nmessage/topic\n</pre></html>"
1334 )
tiernof27c79b2018-03-12 17:08:42 +01001335
1336 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001337 try:
1338 # self.engine.load_dbase(cherrypy.request.app.config)
garciadeblasf2af4a12023-01-24 16:56:54 +01001339 pid = self.authenticator.create_admin_project()
1340 self.authenticator.create_admin_user(pid)
tiernoc94c3df2018-02-09 15:38:54 +01001341 return "Done. User 'admin', password 'admin' created"
1342 except Exception:
1343 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1344 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001345 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001346 return cherrypy.lib.static.serve_file(
1347 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1348 "text/plain",
1349 "attachment",
1350 )
tiernof27c79b2018-03-12 17:08:42 +01001351 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001352 f_path = (
1353 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1354 )
tiernof27c79b2018-03-12 17:08:42 +01001355 f = open(f_path, "r")
1356 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001357 return f
tierno55945e72018-04-06 16:40:27 +02001358
tiernof27c79b2018-03-12 17:08:42 +01001359 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001360 deleted_info = self.engine.db.del_list(args[1], kwargs)
1361 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1362 elif len(args) and args[0] == "fs-clear":
1363 if len(args) >= 2:
1364 folders = (args[1],)
1365 else:
1366 folders = self.engine.fs.dir_ls(".")
1367 for folder in folders:
1368 self.engine.fs.file_delete(folder)
1369 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001370 elif args and args[0] == "login":
1371 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001372 cherrypy.response.headers[
1373 "WWW-Authenticate"
1374 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001375 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1376 elif args and args[0] == "login2":
1377 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001378 cherrypy.response.headers[
1379 "WWW-Authenticate"
1380 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001381 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1382 elif args and args[0] == "sleep":
1383 sleep_time = 5
1384 try:
1385 sleep_time = int(args[1])
1386 except Exception:
1387 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1388 return self._format_out("Database already initialized")
1389 thread_info = cherrypy.thread_data
1390 print(thread_info)
1391 time.sleep(sleep_time)
1392 # thread_info
1393 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001394 main_topic = args[1]
1395 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001396 try:
garciadeblas4568a372021-03-24 09:19:48 +01001397 if cherrypy.request.method == "POST":
garciadeblas4cd875d2023-02-14 19:05:34 +01001398 to_send = yaml.safe_load(cherrypy.request.body)
tierno55945e72018-04-06 16:40:27 +02001399 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001400 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001401 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001402 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001403 for k, v in kwargs.items():
garciadeblas4cd875d2023-02-14 19:05:34 +01001404 v_dict = yaml.safe_load(v)
tiernof1509b22020-05-12 14:32:37 +00001405 self.engine.msg.write(main_topic, k, v_dict)
1406 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001407 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001408 return_text += "Error: " + str(e)
1409 return_text += "</pre></html>\n"
1410 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001411
1412 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001413 "<html><pre>\nheaders:\n args: {}\n".format(args)
1414 + " kwargs: {}\n".format(kwargs)
1415 + " headers: {}\n".format(cherrypy.request.headers)
1416 + " path_info: {}\n".format(cherrypy.request.path_info)
1417 + " query_string: {}\n".format(cherrypy.request.query_string)
garciadeblasf2af4a12023-01-24 16:56:54 +01001418 + " session: {}\n".format(cherrypy.session) # pylint: disable=E1101
garciadeblas4568a372021-03-24 09:19:48 +01001419 + " cookie: {}\n".format(cherrypy.request.cookie)
1420 + " method: {}\n".format(cherrypy.request.method)
garciadeblasf2af4a12023-01-24 16:56:54 +01001421 + " session: {}\n".format(
1422 cherrypy.session.get("fieldname") # pylint: disable=E1101
1423 )
garciadeblas4568a372021-03-24 09:19:48 +01001424 + " body:\n"
1425 )
tiernoc94c3df2018-02-09 15:38:54 +01001426 return_text += " length: {}\n".format(cherrypy.request.body.length)
1427 if cherrypy.request.body.length:
1428 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001429 str(
1430 cherrypy.request.body.read(
1431 int(cherrypy.request.headers.get("Content-Length", 0))
1432 )
1433 )
1434 )
tiernoc94c3df2018-02-09 15:38:54 +01001435 if thread_info:
1436 return_text += "thread: {}\n".format(thread_info)
1437 return_text += "</pre></html>"
1438 return return_text
1439
tierno701018c2019-06-25 11:13:14 +00001440 @staticmethod
1441 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001442 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001443 raise NbiException(
1444 "URL must contain at least 'main_topic/version/topic'",
1445 HTTPStatus.METHOD_NOT_ALLOWED,
1446 )
tiernof27c79b2018-03-12 17:08:42 +01001447
tierno701018c2019-06-25 11:13:14 +00001448 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001449 for arg in args:
1450 if arg is None:
1451 break
1452 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001453 raise NbiException(
1454 "URL contains unexpected extra items '{}'".format(arg),
1455 HTTPStatus.METHOD_NOT_ALLOWED,
1456 )
tiernof27c79b2018-03-12 17:08:42 +01001457
1458 if arg in reference:
1459 reference = reference[arg]
1460 elif "<ID>" in reference:
1461 reference = reference["<ID>"]
1462 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001463 # if there is content
1464 if reference["*"]:
1465 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001466 break
1467 else:
garciadeblas4568a372021-03-24 09:19:48 +01001468 raise NbiException(
1469 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1470 )
tiernof27c79b2018-03-12 17:08:42 +01001471 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001472 raise NbiException(
1473 "Method {} not supported yet for this URL".format(method),
1474 HTTPStatus.NOT_IMPLEMENTED,
1475 )
tierno2236d202018-05-16 19:05:16 +02001476 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001477 raise NbiException(
1478 "Method {} not supported for this URL".format(method),
1479 HTTPStatus.METHOD_NOT_ALLOWED,
1480 )
tierno701018c2019-06-25 11:13:14 +00001481 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001482
1483 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001484 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001485 """
1486 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001487 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001488 :param version:
tiernob24258a2018-10-04 18:39:49 +02001489 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001490 :param id:
1491 :return: None
1492 """
1493 # Use cherrypy.request.base for absoluted path and make use of request.header HOST just in case behind aNAT
garciadeblas4568a372021-03-24 09:19:48 +01001494 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1495 main_topic, version, topic, id
1496 )
tiernof27c79b2018-03-12 17:08:42 +01001497 return
1498
tierno65ca36d2019-02-12 19:27:52 +01001499 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001500 def _extract_query_string_operations(kwargs, method):
1501 """
1502
1503 :param kwargs:
1504 :return:
1505 """
1506 query_string_operations = []
1507 if kwargs:
1508 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1509 if qs in kwargs and kwargs[qs].lower() != "false":
1510 query_string_operations.append(qs.lower() + ":" + method.lower())
1511 return query_string_operations
1512
1513 @staticmethod
1514 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001515 """
1516 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1517 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001518 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001519 :param kwargs: query string input.
1520 :param method: http method: GET, POSST, PUT, ...
1521 :param _id:
1522 :return: admin_query dictionary with keys:
1523 public: True, False or None
1524 force: True or False
1525 project_id: tuple with projects used for accessing an element
1526 set_project: tuple with projects that a created element will belong to
1527 method: show, list, delete, write
1528 """
garciadeblas4568a372021-03-24 09:19:48 +01001529 admin_query = {
1530 "force": False,
1531 "project_id": (token_info["project_id"],),
1532 "username": token_info["username"],
Adurti76d4b762024-05-07 06:04:37 +00001533 "user_id": token_info["user_id"],
garciadeblas4568a372021-03-24 09:19:48 +01001534 "admin": token_info["admin"],
37177091c0322024-11-01 08:55:59 +00001535 "admin_show": token_info["admin_show"],
garciadeblas4568a372021-03-24 09:19:48 +01001536 "public": None,
1537 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1538 }
tierno65ca36d2019-02-12 19:27:52 +01001539 if kwargs:
1540 # FORCE
1541 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001542 if (
1543 kwargs["FORCE"].lower() != "false"
1544 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001545 admin_query["force"] = True
1546 del kwargs["FORCE"]
1547 # PUBLIC
1548 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001549 if (
1550 kwargs["PUBLIC"].lower() != "false"
1551 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001552 admin_query["public"] = True
1553 else:
1554 admin_query["public"] = False
1555 del kwargs["PUBLIC"]
1556 # ADMIN
1557 if "ADMIN" in kwargs:
1558 behave_as = kwargs.pop("ADMIN")
1559 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001560 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001561 raise NbiException(
1562 "Only admin projects can use 'ADMIN' query string",
1563 HTTPStatus.UNAUTHORIZED,
1564 )
1565 if (
1566 not behave_as or behave_as.lower() == "true"
1567 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001568 admin_query["project_id"] = ()
1569 elif isinstance(behave_as, (list, tuple)):
1570 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001571 else: # isinstance(behave_as, str)
1572 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001573 if "SET_PROJECT" in kwargs:
1574 set_project = kwargs.pop("SET_PROJECT")
1575 if not set_project:
1576 admin_query["set_project"] = list(admin_query["project_id"])
1577 else:
1578 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001579 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001580 if admin_query["project_id"]:
1581 for p in set_project:
1582 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001583 raise NbiException(
1584 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1585 "'ADMIN='{p}'".format(p=p),
1586 HTTPStatus.UNAUTHORIZED,
1587 )
tierno65ca36d2019-02-12 19:27:52 +01001588 admin_query["set_project"] = set_project
1589
1590 # PROJECT_READ
1591 # if "PROJECT_READ" in kwargs:
1592 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001593 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001594 if method == "GET":
1595 if _id:
1596 admin_query["method"] = "show"
1597 else:
1598 admin_query["method"] = "list"
1599 elif method == "DELETE":
1600 admin_query["method"] = "delete"
1601 else:
1602 admin_query["method"] = "write"
1603 return admin_query
1604
tiernoc94c3df2018-02-09 15:38:54 +01001605 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001606 def default(
1607 self,
1608 main_topic=None,
1609 version=None,
1610 topic=None,
1611 _id=None,
1612 item=None,
1613 *args,
1614 **kwargs
1615 ):
tierno701018c2019-06-25 11:13:14 +00001616 token_info = None
selvi.j9919bbc2023-04-26 12:22:13 +00001617 outdata = {}
tiernof27c79b2018-03-12 17:08:42 +01001618 _format = None
tierno0f98af52018-03-19 10:28:22 +01001619 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001620 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001621 rollback = []
tierno701018c2019-06-25 11:13:14 +00001622 engine_session = None
elumalai7802ff82023-04-24 20:38:32 +05301623 url_id = ""
1624 log_mapping = {
1625 "POST": "Creating",
1626 "GET": "Fetching",
1627 "DELETE": "Deleting",
1628 "PUT": "Updating",
1629 "PATCH": "Updating",
1630 }
tiernoc94c3df2018-02-09 15:38:54 +01001631 try:
tiernob24258a2018-10-04 18:39:49 +02001632 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001633 raise NbiException(
1634 "URL must contain at least 'main_topic/version/topic'",
1635 HTTPStatus.METHOD_NOT_ALLOWED,
1636 )
1637 if main_topic not in (
1638 "admin",
1639 "vnfpkgm",
1640 "nsd",
1641 "nslcm",
1642 "pdu",
1643 "nst",
1644 "nsilcm",
1645 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001646 "vnflcm",
rshri2d386cb2024-07-05 14:35:51 +00001647 "k8scluster",
yshah53cc9eb2024-07-05 13:06:31 +00001648 "ksu",
1649 "oka",
garciadeblas4568a372021-03-24 09:19:48 +01001650 ):
1651 raise NbiException(
1652 "URL main_topic '{}' not supported".format(main_topic),
1653 HTTPStatus.METHOD_NOT_ALLOWED,
1654 )
1655 if version != "v1":
1656 raise NbiException(
1657 "URL version '{}' not supported".format(version),
1658 HTTPStatus.METHOD_NOT_ALLOWED,
1659 )
elumalai7802ff82023-04-24 20:38:32 +05301660 if _id is not None:
1661 url_id = _id
tiernoc94c3df2018-02-09 15:38:54 +01001662
garciadeblas4568a372021-03-24 09:19:48 +01001663 if (
1664 kwargs
1665 and "METHOD" in kwargs
1666 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1667 ):
tiernof27c79b2018-03-12 17:08:42 +01001668 method = kwargs.pop("METHOD")
1669 else:
1670 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001671
garciadeblas4568a372021-03-24 09:19:48 +01001672 role_permission = self._check_valid_url_method(
1673 method, main_topic, version, topic, _id, item, *args
1674 )
1675 query_string_operations = self._extract_query_string_operations(
1676 kwargs, method
1677 )
tiernob24258a2018-10-04 18:39:49 +02001678 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001679 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001680 token_info = self.authenticator.authorize(
1681 role_permission, query_string_operations, _id
1682 )
tierno12eac3c2020-03-19 23:22:08 +00001683 if main_topic == "admin" and topic == "domains":
1684 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001685 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001686 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001687 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301688
vijay.r35ef2f72019-04-30 17:55:49 +05301689 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001690 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001691
tiernob24258a2018-10-04 18:39:49 +02001692 if main_topic == "nsd":
1693 engine_topic = "nsds"
kayal2001f71c2e82024-06-25 15:26:24 +05301694 if topic == "ns_config_template":
1695 engine_topic = "nsconfigtemps"
tiernob24258a2018-10-04 18:39:49 +02001696 elif main_topic == "vnfpkgm":
1697 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001698 if topic == "vnfpkg_op_occs":
1699 engine_topic = "vnfpkgops"
1700 if topic == "vnf_packages" and item == "action":
1701 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001702 elif main_topic == "nslcm":
1703 engine_topic = "nsrs"
1704 if topic == "ns_lcm_op_occs":
1705 engine_topic = "nslcmops"
1706 if topic == "vnfrs" or topic == "vnf_instances":
1707 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001708 elif main_topic == "vnflcm":
1709 if topic == "vnf_lcm_op_occs":
1710 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001711 elif main_topic == "nst":
1712 engine_topic = "nsts"
1713 elif main_topic == "nsilcm":
1714 engine_topic = "nsis"
1715 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001716 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001717 elif main_topic == "pdu":
1718 engine_topic = "pdus"
rshri2d386cb2024-07-05 14:35:51 +00001719 elif main_topic == "k8scluster":
shrinithi28d887f2025-01-08 05:27:19 +00001720 engine_topic = "cluster"
rshri2d386cb2024-07-05 14:35:51 +00001721 if topic == "clusters" and _id == "register" or item == "deregister":
shrinithi28d887f2025-01-08 05:27:19 +00001722 engine_topic = "clusterops"
rshri2d386cb2024-07-05 14:35:51 +00001723 elif topic == "infra_controller_profiles":
1724 engine_topic = "infras_cont"
1725 elif topic == "infra_config_profiles":
1726 engine_topic = "infras_conf"
1727 elif topic == "resource_profiles":
1728 engine_topic = "resources"
1729 elif topic == "app_profiles":
1730 engine_topic = "apps"
yshah53cc9eb2024-07-05 13:06:31 +00001731 elif main_topic == "k8scluster" and item in (
1732 "upgrade",
1733 "get_creds",
1734 "scale",
1735 ):
shrinithi28d887f2025-01-08 05:27:19 +00001736 engine_topic = "cluster"
yshah53cc9eb2024-07-05 13:06:31 +00001737 elif main_topic == "ksu" and engine_topic in ("ksus", "clone", "move"):
1738 engine_topic = "ksus"
garciadeblas4568a372021-03-24 09:19:48 +01001739 if (
1740 engine_topic == "vims"
1741 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001742 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001743
preethika.p329b8182020-04-22 12:25:39 +05301744 if topic == "subscriptions":
1745 engine_topic = main_topic + "_" + topic
1746
tiernoc94c3df2018-02-09 15:38:54 +01001747 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001748 if item in (
1749 "nsd_content",
1750 "package_content",
1751 "artifacts",
1752 "vnfd",
1753 "nsd",
1754 "nst",
1755 "nst_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301756 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001757 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001758 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001759 path = "$DESCRIPTOR"
1760 elif args:
1761 path = args
tiernob24258a2018-10-04 18:39:49 +02001762 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001763 path = ()
1764 else:
1765 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001766 file, _format = self.engine.get_file(
1767 engine_session,
1768 engine_topic,
1769 _id,
1770 path,
1771 cherrypy.request.headers.get("Accept"),
1772 )
tiernof27c79b2018-03-12 17:08:42 +01001773 outdata = file
shrinithi28d887f2025-01-08 05:27:19 +00001774 # elif not _id and topic != "clusters":
1775 # outdata = self.engine.get_item_list(
1776 # engine_session, engine_topic, kwargs, api_req=True
1777 # )
rshri2d386cb2024-07-05 14:35:51 +00001778 elif topic == "clusters" and item in (
1779 "infra_controller_profiles",
1780 "infra_config_profiles",
1781 "app_profiles",
1782 "resource_profiles",
1783 ):
1784 profile = item
1785 filter_q = None
1786 outdata = self.engine.get_one_item(
1787 engine_session,
1788 engine_topic,
1789 _id,
1790 profile,
1791 filter_q,
1792 api_req=True,
1793 )
shahithyab9eb4142024-10-17 05:51:39 +00001794 elif (
1795 topic == "clusters"
1796 and item == "get_creds_file"
1797 or item == "get_creds"
1798 ):
1799 if item == "get_creds_file":
1800 op_id = args[0]
1801 file, _format = self.engine.get_cluster_creds_file(
1802 engine_session, engine_topic, _id, item, op_id
1803 )
1804 outdata = file
1805 if item == "get_creds":
1806 op_id = self.engine.get_cluster_creds(
1807 engine_session, engine_topic, _id, item
1808 )
1809 outdata = {"op_id": op_id}
shrinithi28d887f2025-01-08 05:27:19 +00001810 elif topic == "clusters" and not _id:
1811 outdata = self.engine.get_item_list_cluster(
1812 engine_session, engine_topic, kwargs, api_req=True
1813 )
1814 elif not _id:
1815 outdata = self.engine.get_item_list(
1816 engine_session, engine_topic, kwargs, api_req=True
1817 )
tiernoc94c3df2018-02-09 15:38:54 +01001818 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301819 if item == "reports":
1820 # TODO check that project_id (_id in this context) has permissions
1821 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301822 filter_q = None
1823 if "vcaStatusRefresh" in kwargs:
1824 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
garciadeblasf2af4a12023-01-24 16:56:54 +01001825 outdata = self.engine.get_item(
1826 engine_session, engine_topic, _id, filter_q, True
1827 )
delacruzramo271d2002019-12-02 21:00:37 +01001828
tiernof27c79b2018-03-12 17:08:42 +01001829 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001830 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001831 if topic in (
1832 "ns_descriptors_content",
1833 "vnf_packages_content",
1834 "netslice_templates_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301835 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001836 ):
tiernof27c79b2018-03-12 17:08:42 +01001837 _id = cherrypy.request.headers.get("Transaction-Id")
yshah53cc9eb2024-07-05 13:06:31 +00001838
tiernof27c79b2018-03-12 17:08:42 +01001839 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001840 _id, _ = self.engine.new_item(
1841 rollback,
1842 engine_session,
1843 engine_topic,
1844 {},
1845 None,
1846 cherrypy.request.headers,
1847 )
1848 completed = self.engine.upload_content(
1849 engine_session,
1850 engine_topic,
1851 _id,
1852 indata,
1853 kwargs,
1854 cherrypy.request.headers,
1855 )
tiernof27c79b2018-03-12 17:08:42 +01001856 if completed:
tiernob24258a2018-10-04 18:39:49 +02001857 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001858 else:
1859 cherrypy.response.headers["Transaction-Id"] = _id
1860 outdata = {"id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001861 elif topic == "oka_packages":
1862 _id = cherrypy.request.headers.get("Transaction-Id")
1863
1864 if not _id:
1865 _id, _ = self.engine.new_item(
1866 rollback,
1867 engine_session,
1868 engine_topic,
1869 {},
1870 kwargs,
1871 cherrypy.request.headers,
1872 )
1873 cherrypy.request.headers["method"] = cherrypy.request.method
1874 if indata:
1875 completed = self.engine.upload_content(
1876 engine_session,
1877 engine_topic,
1878 _id,
1879 indata,
1880 None,
1881 cherrypy.request.headers,
1882 )
1883 if completed:
1884 self._set_location_header(main_topic, version, topic, _id)
1885 else:
1886 cherrypy.response.headers["Transaction-Id"] = _id
yshahe08aff12024-11-07 09:32:22 +00001887 outdata = {"_id": _id, "id": _id}
tiernob24258a2018-10-04 18:39:49 +02001888 elif topic == "ns_instances_content":
1889 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001890 _id, _ = self.engine.new_item(
1891 rollback, engine_session, engine_topic, indata, kwargs
1892 )
tiernob24258a2018-10-04 18:39:49 +02001893 # creates nslcmop
1894 indata["lcmOperationType"] = "instantiate"
1895 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001896 nslcmop_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001897 rollback, engine_session, "nslcmops", indata, None
1898 )
tiernob24258a2018-10-04 18:39:49 +02001899 self._set_location_header(main_topic, version, topic, _id)
garciadeblasf53612b2024-07-12 14:44:37 +02001900 outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName}
adurti3af50952024-05-31 11:36:57 +05301901 elif topic == "ns_instances_terminate":
1902 if indata.get("ns_ids"):
1903 for ns_id in indata.get("ns_ids"):
1904 nslcmop_desc = {
1905 "lcmOperationType": "terminate",
1906 "nsInstanceId": ns_id,
shahithyab9eb4142024-10-17 05:51:39 +00001907 "autoremove": (
1908 indata.get("autoremove")
1909 if "autoremove" in indata
1910 else True
1911 ),
adurti3af50952024-05-31 11:36:57 +05301912 }
1913 op_id, _, _ = self.engine.new_item(
1914 rollback,
1915 engine_session,
1916 "nslcmops",
1917 nslcmop_desc,
1918 kwargs,
1919 )
1920 if not op_id:
1921 _ = self.engine.del_item(
1922 engine_session, engine_topic, ns_id
1923 )
1924 outdata = {"ns_ids": indata.get("ns_ids")}
1925 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001926 elif topic == "ns_instances" and item:
1927 indata["lcmOperationType"] = item
1928 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001929 _id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001930 rollback, engine_session, "nslcmops", indata, kwargs
1931 )
1932 self._set_location_header(
1933 main_topic, version, "ns_lcm_op_occs", _id
1934 )
garciadeblasf53612b2024-07-12 14:44:37 +02001935 outdata = {"id": _id, "nsName": nsName}
tierno65acb4d2018-04-06 16:42:40 +02001936 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001937 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001938 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001939 _id, _ = self.engine.new_item(
1940 rollback, engine_session, engine_topic, indata, kwargs
1941 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001942 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001943 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001944 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001945 nsilcmop_id, _ = self.engine.new_item(
1946 rollback, engine_session, "nsilcmops", indata, kwargs
1947 )
kuuse078f55e2019-05-16 19:24:21 +02001948 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001949 elif topic == "netslice_instances" and item:
1950 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001951 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001952 _id, _ = self.engine.new_item(
1953 rollback, engine_session, "nsilcmops", indata, kwargs
1954 )
1955 self._set_location_header(
1956 main_topic, version, "nsi_lcm_op_occs", _id
1957 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001958 outdata = {"id": _id}
1959 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001960 elif topic == "vnf_packages" and item == "action":
1961 indata["lcmOperationType"] = item
1962 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001963 _id, _ = self.engine.new_item(
1964 rollback, engine_session, "vnfpkgops", indata, kwargs
1965 )
1966 self._set_location_header(
1967 main_topic, version, "vnfpkg_op_occs", _id
1968 )
delacruzramo271d2002019-12-02 21:00:37 +01001969 outdata = {"id": _id}
1970 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301971 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001972 _id, _ = self.engine.new_item(
1973 rollback, engine_session, engine_topic, indata, kwargs
1974 )
preethika.p329b8182020-04-22 12:25:39 +05301975 self._set_location_header(main_topic, version, topic, _id)
1976 link = {}
1977 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001978 outdata = {
1979 "id": _id,
1980 "filter": indata["filter"],
1981 "callbackUri": indata["CallbackUri"],
1982 "_links": link,
1983 }
preethika.p329b8182020-04-22 12:25:39 +05301984 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001985 elif topic == "vnf_instances" and item:
1986 indata["lcmOperationType"] = item
1987 indata["vnfInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001988 _id, nsName, _ = self.engine.new_item(
garciadeblasf2af4a12023-01-24 16:56:54 +01001989 rollback, engine_session, "vnflcmops", indata, kwargs
1990 )
1991 self._set_location_header(
1992 main_topic, version, "vnf_lcm_op_occs", _id
1993 )
garciadeblasf53612b2024-07-12 14:44:37 +02001994 outdata = {"id": _id, "nsName": nsName}
almagiae47b9132022-05-17 14:12:22 +02001995 cherrypy.response.status = HTTPStatus.ACCEPTED.value
Gabriel Cuba84a60df2023-10-30 14:01:54 -05001996 elif topic == "ns_lcm_op_occs" and item == "cancel":
1997 indata["nsLcmOpOccId"] = _id
1998 self.engine.cancel_item(
1999 rollback, engine_session, "nslcmops", indata, None
2000 )
2001 self._set_location_header(main_topic, version, topic, _id)
2002 cherrypy.response.status = HTTPStatus.ACCEPTED.value
rshri2d386cb2024-07-05 14:35:51 +00002003 elif topic == "clusters" and _id == "register":
2004 # To register a cluster
2005 _id, _ = self.engine.add_item(
2006 rollback, engine_session, engine_topic, indata, kwargs
2007 )
2008 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002009 outdata = {"_id": _id, "id": _id}
rshri2d386cb2024-07-05 14:35:51 +00002010 elif (
2011 topic
2012 in (
2013 "clusters",
2014 "infra_controller_profiles",
2015 "infra_config_profiles",
2016 "app_profiles",
2017 "resource_profiles",
2018 )
2019 and item is None
2020 ):
2021 # creates cluster, infra_controller_profiles, app_profiles, infra_config_profiles, and resource_profiles
2022 _id, _ = self.engine.new_item(
2023 rollback, engine_session, engine_topic, indata, kwargs
2024 )
2025 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002026 outdata = {"_id": _id, "id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00002027 elif topic == "ksus" and item:
2028 if item == "clone":
2029 _id = self.engine.clone(
2030 rollback,
2031 engine_session,
2032 engine_topic,
2033 _id,
2034 indata,
2035 kwargs,
2036 cherrypy.request.headers,
2037 )
2038 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002039 outdata = {"_id": _id, "id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00002040 if item == "move":
2041 op_id = self.engine.move_ksu(
2042 engine_session, engine_topic, _id, indata, kwargs
2043 )
2044 outdata = {"op_id": op_id}
2045 elif topic == "ksus" and _id == "delete":
2046 op_id = self.engine.delete_ksu(
2047 engine_session, engine_topic, _id, indata
2048 )
2049 outdata = {"op_id": op_id}
2050 elif topic == "ksus" and _id == "update":
2051 op_id = self.engine.edit_item(
2052 engine_session, engine_topic, _id, indata, kwargs
2053 )
2054 outdata = {"op_id": op_id}
yshah99122b82024-11-18 07:05:29 +00002055 elif topic == "clusters" and item in ("upgrade", "scale", "update"):
yshah53cc9eb2024-07-05 13:06:31 +00002056 op_id = self.engine.update_cluster(
2057 engine_session, engine_topic, _id, item, indata
2058 )
2059 outdata = {"op_id": op_id}
tiernof27c79b2018-03-12 17:08:42 +01002060 else:
garciadeblas4568a372021-03-24 09:19:48 +01002061 _id, op_id = self.engine.new_item(
2062 rollback,
2063 engine_session,
2064 engine_topic,
2065 indata,
2066 kwargs,
2067 cherrypy.request.headers,
2068 )
tiernob24258a2018-10-04 18:39:49 +02002069 self._set_location_header(main_topic, version, topic, _id)
yshahe08aff12024-11-07 09:32:22 +00002070 outdata = {"_id": _id, "id": _id}
tiernobdebce92019-07-01 15:36:49 +00002071 if op_id:
2072 outdata["op_id"] = op_id
2073 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02002074 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02002075
tiernoc94c3df2018-02-09 15:38:54 +01002076 elif method == "DELETE":
2077 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01002078 outdata = self.engine.del_item_list(
2079 engine_session, engine_topic, kwargs
2080 )
tierno09c073e2018-04-26 13:36:48 +02002081 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01002082 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00002083 # for NS NSI generate an operation
2084 op_id = None
tierno701018c2019-06-25 11:13:14 +00002085 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02002086 nslcmop_desc = {
2087 "lcmOperationType": "terminate",
2088 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002089 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02002090 }
garciadeblasf53612b2024-07-12 14:44:37 +02002091 op_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01002092 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
2093 )
tierno22577432020-04-08 15:16:57 +00002094 if op_id:
garciadeblasf53612b2024-07-12 14:44:37 +02002095 outdata = {"_id": op_id, "nsName": nsName}
garciadeblas4568a372021-03-24 09:19:48 +01002096 elif (
2097 topic == "netslice_instances_content"
2098 and not engine_session["force"]
2099 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02002100 nsilcmop_desc = {
2101 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02002102 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002103 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02002104 }
garciadeblas4568a372021-03-24 09:19:48 +01002105 op_id, _ = self.engine.new_item(
2106 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
2107 )
tierno22577432020-04-08 15:16:57 +00002108 if op_id:
2109 outdata = {"_id": op_id}
rshri2d386cb2024-07-05 14:35:51 +00002110 elif topic == "clusters" and item == "deregister":
2111 if not op_id:
2112 op_id = self.engine.remove(
2113 engine_session, engine_topic, _id
2114 )
2115 if op_id:
2116 outdata = {"_id": op_id}
2117 cherrypy.response.status = (
2118 HTTPStatus.ACCEPTED.value
2119 if op_id
2120 else HTTPStatus.NO_CONTENT.value
2121 )
yshah53cc9eb2024-07-05 13:06:31 +00002122 elif topic == "ksus":
2123 op_id = self.engine.delete_ksu(
2124 engine_session, engine_topic, _id, indata
2125 )
2126 outdata = {"op_id": op_id}
tierno22577432020-04-08 15:16:57 +00002127 # if there is not any deletion in process, delete
rshri2d386cb2024-07-05 14:35:51 +00002128 elif not op_id:
tierno22577432020-04-08 15:16:57 +00002129 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2130 if op_id:
rshri2d386cb2024-07-05 14:35:51 +00002131 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01002132 cherrypy.response.status = (
2133 HTTPStatus.ACCEPTED.value
2134 if op_id
2135 else HTTPStatus.NO_CONTENT.value
2136 )
tierno09c073e2018-04-26 13:36:48 +02002137
tierno7ae10112018-05-18 14:36:02 +02002138 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00002139 op_id = None
tierno701018c2019-06-25 11:13:14 +00002140 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01002141 raise NbiException(
2142 "Nothing to update. Provide payload and/or query string",
2143 HTTPStatus.BAD_REQUEST,
2144 )
2145 if (
kayal2001f71c2e82024-06-25 15:26:24 +05302146 item
2147 in (
2148 "nsd_content",
2149 "package_content",
2150 "nst_content",
2151 "template_content",
2152 )
garciadeblas4568a372021-03-24 09:19:48 +01002153 and method == "PUT"
2154 ):
2155 completed = self.engine.upload_content(
2156 engine_session,
2157 engine_topic,
2158 _id,
2159 indata,
2160 kwargs,
2161 cherrypy.request.headers,
2162 )
tiernof27c79b2018-03-12 17:08:42 +01002163 if not completed:
2164 cherrypy.response.headers["Transaction-Id"] = id
rshri2d386cb2024-07-05 14:35:51 +00002165 elif item in (
2166 "app_profiles",
2167 "resource_profiles",
2168 "infra_controller_profiles",
2169 "infra_config_profiles",
2170 ):
2171 op_id = self.engine.edit(
2172 engine_session, engine_topic, _id, item, indata, kwargs
2173 )
yshah53cc9eb2024-07-05 13:06:31 +00002174 elif topic == "oka_packages" and method == "PATCH":
2175 if kwargs:
2176 op_id = self.engine.edit_item(
2177 engine_session, engine_topic, _id, None, kwargs
2178 )
2179 if indata:
yshahffcac5f2024-08-19 12:49:07 +00002180 if isinstance(indata, dict):
yshah53cc9eb2024-07-05 13:06:31 +00002181 op_id = self.engine.edit_item(
2182 engine_session, engine_topic, _id, indata, kwargs
2183 )
2184 else:
2185 cherrypy.request.headers["method"] = cherrypy.request.method
2186 completed = self.engine.upload_content(
2187 engine_session,
2188 engine_topic,
2189 _id,
2190 indata,
2191 {},
2192 cherrypy.request.headers,
2193 )
2194 if not completed:
2195 cherrypy.response.headers["Transaction-Id"] = id
2196 elif topic == "oka_packages" and method == "PUT":
2197 if indata:
2198 cherrypy.request.headers["method"] = cherrypy.request.method
2199 completed = self.engine.upload_content(
2200 engine_session,
2201 engine_topic,
2202 _id,
2203 indata,
2204 {},
2205 cherrypy.request.headers,
2206 )
2207 if not completed:
2208 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01002209 else:
garciadeblas4568a372021-03-24 09:19:48 +01002210 op_id = self.engine.edit_item(
2211 engine_session, engine_topic, _id, indata, kwargs
2212 )
tiernobdebce92019-07-01 15:36:49 +00002213
2214 if op_id:
2215 cherrypy.response.status = HTTPStatus.ACCEPTED.value
2216 outdata = {"op_id": op_id}
2217 else:
2218 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
2219 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01002220 else:
garciadeblas4568a372021-03-24 09:19:48 +01002221 raise NbiException(
2222 "Method {} not allowed".format(method),
2223 HTTPStatus.METHOD_NOT_ALLOWED,
2224 )
tiernoa6bb45d2019-06-14 09:45:39 +00002225
2226 # if Role information changes, it is needed to reload the information of roles
2227 if topic == "roles" and method != "GET":
2228 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01002229
garciadeblas4568a372021-03-24 09:19:48 +01002230 if (
2231 topic == "projects"
2232 and method == "DELETE"
2233 or topic in ["users", "roles"]
2234 and method in ["PUT", "PATCH", "DELETE"]
2235 ):
delacruzramoad682a52019-12-10 16:26:34 +01002236 self.authenticator.remove_token_from_cache()
2237
garciadeblasf53612b2024-07-12 14:44:37 +02002238 cef_event(
2239 cef_logger,
2240 {
2241 "name": "User Operation",
2242 "sourceUserName": token_info.get("username"),
2243 },
2244 )
2245 if topic == "ns_instances_content" and url_id:
2246 nsName = (
2247 outdata.get("name") if method == "GET" else outdata.get("nsName")
2248 )
elumalai7802ff82023-04-24 20:38:32 +05302249 cef_event(
2250 cef_logger,
2251 {
garciadeblasf53612b2024-07-12 14:44:37 +02002252 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2253 log_mapping[method],
2254 topic,
2255 nsName,
2256 outdata.get("id"),
2257 token_info.get("project_name"),
2258 ),
2259 },
2260 )
2261 cherrypy.log("{}".format(cef_logger))
2262 elif topic == "ns_instances_content" and method == "POST":
2263 cef_event(
2264 cef_logger,
2265 {
2266 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2267 log_mapping[method],
2268 topic,
2269 outdata.get("nsName"),
2270 outdata.get("id"),
2271 token_info.get("project_name"),
2272 ),
2273 },
2274 )
2275 cherrypy.log("{}".format(cef_logger))
2276 elif topic in ("ns_instances", "vnf_instances") and item:
2277 cef_event(
2278 cef_logger,
2279 {
2280 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2281 log_mapping[method],
2282 topic,
2283 outdata.get("nsName"),
2284 url_id,
2285 token_info.get("project_name"),
2286 ),
2287 },
2288 )
2289 cherrypy.log("{}".format(cef_logger))
2290 elif item is not None:
2291 cef_event(
2292 cef_logger,
2293 {
elumalai7802ff82023-04-24 20:38:32 +05302294 "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format(
2295 item,
2296 topic,
2297 url_id,
2298 token_info.get("project_name"),
2299 ),
2300 },
2301 )
2302 cherrypy.log("{}".format(cef_logger))
2303 else:
2304 cef_event(
2305 cef_logger,
2306 {
elumalai7802ff82023-04-24 20:38:32 +05302307 "message": "{} {} {}, Project={} Outcome=Success".format(
2308 log_mapping[method],
2309 topic,
2310 url_id,
2311 token_info.get("project_name"),
2312 ),
2313 },
2314 )
2315 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002316 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02002317 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01002318 if isinstance(
2319 e,
2320 (
2321 NbiException,
2322 EngineException,
2323 DbException,
2324 FsException,
2325 MsgException,
2326 AuthException,
2327 ValidationError,
2328 AuthconnException,
2329 ),
2330 ):
tiernob24258a2018-10-04 18:39:49 +02002331 http_code_value = cherrypy.response.status = e.http_code.value
2332 http_code_name = e.http_code.name
2333 cherrypy.log("Exception {}".format(e))
2334 else:
garciadeblas4568a372021-03-24 09:19:48 +01002335 http_code_value = (
2336 cherrypy.response.status
2337 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01002338 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02002339 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02002340 if hasattr(outdata, "close"): # is an open file
2341 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02002342 error_text = str(e)
2343 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02002344 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02002345 try:
tiernocc103432018-10-19 14:10:35 +02002346 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01002347 self.engine.db.set_one(
2348 rollback_item["topic"],
2349 {"_id": rollback_item["_id"]},
2350 rollback_item["content"],
2351 fail_on_empty=False,
2352 )
preethika.p329b8182020-04-22 12:25:39 +05302353 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01002354 self.engine.db.del_list(
2355 rollback_item["topic"],
2356 rollback_item["filter"],
garciadeblas4568a372021-03-24 09:19:48 +01002357 )
tiernocc103432018-10-19 14:10:35 +02002358 else:
garciadeblas4568a372021-03-24 09:19:48 +01002359 self.engine.db.del_one(
2360 rollback_item["topic"],
2361 {"_id": rollback_item["_id"]},
2362 fail_on_empty=False,
2363 )
tierno3ace63c2018-05-03 17:51:43 +02002364 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01002365 rollback_error_text = "Rollback Exception {}: {}".format(
2366 rollback_item, e2
2367 )
tiernob24258a2018-10-04 18:39:49 +02002368 cherrypy.log(rollback_error_text)
2369 error_text += ". " + rollback_error_text
2370 # if isinstance(e, MsgException):
2371 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
2372 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01002373 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02002374 "code": http_code_name,
2375 "status": http_code_value,
2376 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01002377 }
elumalai7802ff82023-04-24 20:38:32 +05302378 if item is not None and token_info is not None:
2379 cef_event(
2380 cef_logger,
2381 {
2382 "name": "User Operation",
2383 "sourceUserName": token_info.get("username", None),
2384 "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format(
2385 item,
2386 topic,
2387 url_id,
2388 token_info.get("project_name", None),
2389 ),
2390 "severity": "2",
2391 },
2392 )
2393 cherrypy.log("{}".format(cef_logger))
2394 elif token_info is not None:
2395 cef_event(
2396 cef_logger,
2397 {
2398 "name": "User Operation",
2399 "sourceUserName": token_info.get("username", None),
2400 "message": "{} {} {}, Project={} Outcome=Failure".format(
2401 item,
2402 topic,
2403 url_id,
2404 token_info.get("project_name", None),
2405 ),
2406 "severity": "2",
2407 },
2408 )
2409 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002410 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01002411 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00002412 finally:
2413 if token_info:
2414 self._format_login(token_info)
2415 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
2416 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
2417 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01002418 cherrypy.request.login += ";{}={}".format(
2419 logging_id, outdata[logging_id][:36]
2420 )
tiernoc94c3df2018-02-09 15:38:54 +01002421
2422
tiernoc94c3df2018-02-09 15:38:54 +01002423def _start_service():
2424 """
2425 Callback function called when cherrypy.engine starts
2426 Override configuration with env variables
2427 Set database, storage, message configuration
2428 Init database with admin/admin user password
2429 """
tierno932499c2019-01-28 17:28:10 +00002430 global nbi_server
2431 global subscription_thread
elumalai7802ff82023-04-24 20:38:32 +05302432 global cef_logger
Rahulc72bc8e2023-12-05 11:54:38 +00002433 global current_backend
tiernoc94c3df2018-02-09 15:38:54 +01002434 cherrypy.log.error("Starting osm_nbi")
2435 # update general cherrypy configuration
2436 update_dict = {}
2437
garciadeblas4568a372021-03-24 09:19:48 +01002438 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01002439 for k, v in environ.items():
garciadeblas6d83f8f2023-06-19 22:34:49 +02002440 if k == "OSMNBI_USER_MANAGEMENT":
garciadeblas7a0dace2024-09-17 18:00:50 +02002441 feature_state = v.lower() == "true"
garciadeblas6d83f8f2023-06-19 22:34:49 +02002442 engine_config["authentication"]["user_management"] = feature_state
Adurtid68526d2023-11-14 11:14:53 +00002443 elif k == "OSMNBI_PWD_EXPIRE_DAYS":
2444 pwd_expire_days = int(v)
2445 engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days
2446 elif k == "OSMNBI_MAX_PWD_ATTEMPT":
2447 max_pwd_attempt = int(v)
2448 engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt
2449 elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS":
2450 account_expire_days = int(v)
2451 engine_config["authentication"]["account_expire_days"] = account_expire_days
jeganbe1a3df2024-06-04 12:05:19 +00002452 elif k == "OSMNBI_SMTP_SERVER":
2453 engine_config["authentication"]["smtp_server"] = v
2454 engine_config["authentication"]["all"] = environ
2455 elif k == "OSMNBI_SMTP_PORT":
2456 port = int(v)
2457 engine_config["authentication"]["smtp_port"] = port
2458 elif k == "OSMNBI_SENDER_EMAIL":
2459 engine_config["authentication"]["sender_email"] = v
2460 elif k == "OSMNBI_EMAIL_PASSWORD":
2461 engine_config["authentication"]["sender_password"] = v
2462 elif k == "OSMNBI_OTP_RETRY_COUNT":
2463 otp_retry_count = int(v)
2464 engine_config["authentication"]["retry_count"] = otp_retry_count
2465 elif k == "OSMNBI_OTP_EXPIRY_TIME":
2466 otp_expiry_time = int(v)
2467 engine_config["authentication"]["otp_expiry_time"] = otp_expiry_time
tiernoc94c3df2018-02-09 15:38:54 +01002468 if not k.startswith("OSMNBI_"):
2469 continue
tiernoe1281182018-05-22 12:24:36 +02002470 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01002471 if not k2:
2472 continue
2473 try:
2474 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01002475 if k == "OSMNBI_STATIC_DIR":
2476 engine_config["/static"]["tools.staticdir.dir"] = v
2477 engine_config["/static"]["tools.staticdir.on"] = True
2478 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
2479 update_dict["server.socket_port"] = int(v)
2480 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
2481 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02002482 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01002483 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002484 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02002485 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002486 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01002487 engine_config[k1][k2] = int(v)
2488 else:
2489 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002490
tiernoc94c3df2018-02-09 15:38:54 +01002491 except ValueError as e:
2492 cherrypy.log.error("Ignoring environ '{}': " + str(e))
2493 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01002494 cherrypy.log(
2495 "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
2496 )
tiernoc94c3df2018-02-09 15:38:54 +01002497
2498 if update_dict:
2499 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02002500 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01002501
2502 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01002503 log_format_simple = (
2504 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
2505 )
2506 log_formatter_simple = logging.Formatter(
2507 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
2508 )
tiernoc94c3df2018-02-09 15:38:54 +01002509 logger_server = logging.getLogger("cherrypy.error")
2510 logger_access = logging.getLogger("cherrypy.access")
2511 logger_cherry = logging.getLogger("cherrypy")
2512 logger_nbi = logging.getLogger("nbi")
2513
tiernof5298be2018-05-16 14:43:57 +02002514 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01002515 file_handler = logging.handlers.RotatingFileHandler(
2516 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
2517 )
tiernoc94c3df2018-02-09 15:38:54 +01002518 file_handler.setFormatter(log_formatter_simple)
2519 logger_cherry.addHandler(file_handler)
2520 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02002521 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01002522 for format_, logger in {
2523 "nbi.server %(filename)s:%(lineno)s": logger_server,
2524 "nbi.access %(filename)s:%(lineno)s": logger_access,
2525 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
2526 }.items():
tiernob24258a2018-10-04 18:39:49 +02002527 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01002528 log_formatter_cherry = logging.Formatter(
2529 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
2530 )
tiernob24258a2018-10-04 18:39:49 +02002531 str_handler = logging.StreamHandler()
2532 str_handler.setFormatter(log_formatter_cherry)
2533 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01002534
tiernof5298be2018-05-16 14:43:57 +02002535 if engine_config["global"].get("log.level"):
2536 logger_cherry.setLevel(engine_config["global"]["log.level"])
2537 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01002538
2539 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01002540 for k1, logname in {
2541 "message": "nbi.msg",
2542 "database": "nbi.db",
2543 "storage": "nbi.fs",
2544 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01002545 engine_config[k1]["logger_name"] = logname
2546 logger_module = logging.getLogger(logname)
2547 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01002548 file_handler = logging.handlers.RotatingFileHandler(
2549 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
2550 )
tiernoc94c3df2018-02-09 15:38:54 +01002551 file_handler.setFormatter(log_formatter_simple)
2552 logger_module.addHandler(file_handler)
2553 if "loglevel" in engine_config[k1]:
2554 logger_module.setLevel(engine_config[k1]["loglevel"])
2555 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01002556 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
2557 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
2558 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
2559 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
2560 target_version=auth_database_version
2561 )
tiernobee508e2019-01-21 11:21:49 +00002562
elumalai7802ff82023-04-24 20:38:32 +05302563 cef_logger = cef_event_builder(engine_config["authentication"])
2564
tierno932499c2019-01-28 17:28:10 +00002565 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01002566 subscription_thread = SubscriptionThread(
2567 config=engine_config, engine=nbi_server.engine
2568 )
tierno932499c2019-01-28 17:28:10 +00002569 subscription_thread.start()
2570 # Do not capture except SubscriptionException
2571
tiernob2e48bd2020-02-04 15:47:18 +00002572 backend = engine_config["authentication"]["backend"]
Rahulc72bc8e2023-12-05 11:54:38 +00002573 current_backend = backend
garciadeblas4568a372021-03-24 09:19:48 +01002574 cherrypy.log.error(
2575 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
2576 nbi_version, nbi_version_date, backend
2577 )
2578 )
tiernoc94c3df2018-02-09 15:38:54 +01002579
2580
2581def _stop_service():
2582 """
2583 Callback function called when cherrypy.engine stops
2584 TODO: Ending database connections.
2585 """
tierno932499c2019-01-28 17:28:10 +00002586 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01002587 if subscription_thread:
2588 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00002589 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01002590 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01002591 cherrypy.log.error("Stopping osm_nbi")
2592
tierno2236d202018-05-16 19:05:16 +02002593
tiernof5298be2018-05-16 14:43:57 +02002594def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00002595 global nbi_server
tierno932499c2019-01-28 17:28:10 +00002596 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01002597 cherrypy.engine.subscribe("start", _start_service)
2598 cherrypy.engine.subscribe("stop", _stop_service)
2599 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02002600
2601
2602def usage():
garciadeblas4568a372021-03-24 09:19:48 +01002603 print(
2604 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02002605 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
2606 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01002607 """.format(
2608 sys.argv[0]
2609 )
2610 )
tierno2236d202018-05-16 19:05:16 +02002611 # --log-socket-host HOST: send logs to this host")
2612 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002613
2614
garciadeblas4568a372021-03-24 09:19:48 +01002615if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002616 try:
2617 # load parameters and configuration
2618 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2619 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2620 config_file = None
2621 for o, a in opts:
2622 if o in ("-h", "--help"):
2623 usage()
2624 sys.exit()
2625 elif o in ("-c", "--config"):
2626 config_file = a
2627 # elif o == "--log-socket-port":
2628 # log_socket_port = a
2629 # elif o == "--log-socket-host":
2630 # log_socket_host = a
2631 # elif o == "--log-file":
2632 # log_file = a
2633 else:
2634 assert False, "Unhandled option"
2635 if config_file:
2636 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002637 print(
2638 "configuration file '{}' that not exist".format(config_file),
2639 file=sys.stderr,
2640 )
tiernof5298be2018-05-16 14:43:57 +02002641 exit(1)
2642 else:
garciadeblas4568a372021-03-24 09:19:48 +01002643 for config_file in (
2644 __file__[: __file__.rfind(".")] + ".cfg",
2645 "./nbi.cfg",
2646 "/etc/osm/nbi.cfg",
2647 ):
tiernof5298be2018-05-16 14:43:57 +02002648 if path.isfile(config_file):
2649 break
2650 else:
garciadeblas4568a372021-03-24 09:19:48 +01002651 print(
2652 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2653 file=sys.stderr,
2654 )
tiernof5298be2018-05-16 14:43:57 +02002655 exit(1)
2656 nbi(config_file)
2657 except getopt.GetoptError as e:
2658 print(str(e), file=sys.stderr)
2659 # usage()
2660 exit(1)