blob: ea7a236eda16032ac818f9287a2a97b445825d07 [file] [log] [blame]
tiernoc94c3df2018-02-09 15:38:54 +01001#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3
tiernod125caf2018-11-22 16:05:54 +00004# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
tiernoc94c3df2018-02-09 15:38:54 +010017import cherrypy
18import time
19import json
20import yaml
tierno9c630112019-08-29 14:21:41 +000021import osm_nbi.html_out as html
tiernoc94c3df2018-02-09 15:38:54 +010022import logging
tiernof5298be2018-05-16 14:43:57 +020023import logging.handlers
24import getopt
25import sys
Eduardo Sousa2f988212018-07-26 01:04:11 +010026
tierno9c630112019-08-29 14:21:41 +000027from osm_nbi.authconn import AuthException, AuthconnException
28from osm_nbi.auth import Authenticator
29from osm_nbi.engine import Engine, EngineException
30from osm_nbi.subscriptions import SubscriptionThread
elumalai7802ff82023-04-24 20:38:32 +053031from osm_nbi.utils import cef_event, cef_event_builder
tierno9c630112019-08-29 14:21:41 +000032from osm_nbi.validation import ValidationError
tiernoa8d63632018-05-10 13:12:32 +020033from osm_common.dbbase import DbException
34from osm_common.fsbase import FsException
35from osm_common.msgbase import MsgException
tiernoc94c3df2018-02-09 15:38:54 +010036from http import HTTPStatus
tiernoc94c3df2018-02-09 15:38:54 +010037from codecs import getreader
tiernof5298be2018-05-16 14:43:57 +020038from os import environ, path
tiernob2e48bd2020-02-04 15:47:18 +000039from osm_nbi import version as nbi_version, version_date as nbi_version_date
tiernoc94c3df2018-02-09 15:38:54 +010040
41__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
tiernodfe09572018-04-24 10:41:10 +020042
garciadeblas4568a372021-03-24 09:19:48 +010043__version__ = "0.1.3" # file version, not NBI version
tierno9c630112019-08-29 14:21:41 +000044version_date = "Aug 2019"
45
garciadeblas4568a372021-03-24 09:19:48 +010046database_version = "1.2"
47auth_database_version = "1.0"
48nbi_server = None # instance of Server class
tierno932499c2019-01-28 17:28:10 +000049subscription_thread = None # instance of SubscriptionThread class
elumalai7802ff82023-04-24 20:38:32 +053050cef_logger = None
yshah53cc9eb2024-07-05 13:06:31 +000051logger = logging.getLogger("nbi.nbi")
tiernoc94c3df2018-02-09 15:38:54 +010052
53"""
tiernof27c79b2018-03-12 17:08:42 +010054North Bound Interface (O: OSM specific; 5,X: SOL005 not implemented yet; O5: SOL005 implemented)
tiernoc94c3df2018-02-09 15:38:54 +010055URL: /osm GET POST PUT DELETE PATCH
garciadeblas9750c5a2018-10-15 16:20:35 +020056 /nsd/v1
tierno2236d202018-05-16 19:05:16 +020057 /ns_descriptors_content O O
58 /<nsdInfoId> O O O O
tiernoc94c3df2018-02-09 15:38:54 +010059 /ns_descriptors O5 O5
60 /<nsdInfoId> O5 O5 5
61 /nsd_content O5 O5
tiernof27c79b2018-03-12 17:08:42 +010062 /nsd O
63 /artifacts[/<artifactPath>] O
kayal2001f71c2e82024-06-25 15:26:24 +053064 /ns_config_template O O
65 /<nsConfigTemplateId> O O
66 /template_content O O
tiernoc94c3df2018-02-09 15:38:54 +010067 /pnf_descriptors 5 5
68 /<pnfdInfoId> 5 5 5
69 /pnfd_content 5 5
tiernof27c79b2018-03-12 17:08:42 +010070 /subscriptions 5 5
71 /<subscriptionId> 5 X
tiernoc94c3df2018-02-09 15:38:54 +010072
73 /vnfpkgm/v1
tierno55945e72018-04-06 16:40:27 +020074 /vnf_packages_content O O
tierno2236d202018-05-16 19:05:16 +020075 /<vnfPkgId> O O
tiernoc94c3df2018-02-09 15:38:54 +010076 /vnf_packages O5 O5
77 /<vnfPkgId> O5 O5 5
tiernoc94c3df2018-02-09 15:38:54 +010078 /package_content O5 O5
79 /upload_from_uri X
tiernof27c79b2018-03-12 17:08:42 +010080 /vnfd O5
81 /artifacts[/<artifactPath>] O5
82 /subscriptions X X
83 /<subscriptionId> X X
tiernoc94c3df2018-02-09 15:38:54 +010084
85 /nslcm/v1
tiernof27c79b2018-03-12 17:08:42 +010086 /ns_instances_content O O
tierno2236d202018-05-16 19:05:16 +020087 /<nsInstanceId> O O
tiernof27c79b2018-03-12 17:08:42 +010088 /ns_instances 5 5
tierno95692442018-05-24 18:05:28 +020089 /<nsInstanceId> O5 O5
tierno65acb4d2018-04-06 16:42:40 +020090 instantiate O5
91 terminate O5
92 action O
93 scale O5
elumalai8e3806c2022-04-28 17:26:24 +053094 migrate O
aticig544a2ae2022-04-05 09:00:17 +030095 update 05
garciadeblas0964edf2022-02-11 00:43:44 +010096 heal O5
tiernoc94c3df2018-02-09 15:38:54 +010097 /ns_lcm_op_occs 5 5
98 /<nsLcmOpOccId> 5 5 5
Gabriel Cuba84a60df2023-10-30 14:01:54 -050099 cancel 05
tiernof759d822018-06-11 18:54:54 +0200100 /vnf_instances (also vnfrs for compatibility) O
101 /<vnfInstanceId> O
tiernof27c79b2018-03-12 17:08:42 +0100102 /subscriptions 5 5
103 /<subscriptionId> 5 X
garciadeblas9750c5a2018-10-15 16:20:35 +0200104
tiernocb83c942018-09-24 17:28:13 +0200105 /pdu/v1
tierno032916c2019-03-22 13:27:12 +0000106 /pdu_descriptors O O
tiernocb83c942018-09-24 17:28:13 +0200107 /<id> O O O O
garciadeblas9750c5a2018-10-15 16:20:35 +0200108
tiernof27c79b2018-03-12 17:08:42 +0100109 /admin/v1
110 /tokens O O
tierno2236d202018-05-16 19:05:16 +0200111 /<id> O O
tiernof27c79b2018-03-12 17:08:42 +0100112 /users O O
tiernocd54a4a2018-09-12 16:40:35 +0200113 /<id> O O O O
tiernof27c79b2018-03-12 17:08:42 +0100114 /projects O O
tierno2236d202018-05-16 19:05:16 +0200115 /<id> O O
tierno55ba2e62018-12-11 17:22:22 +0000116 /vim_accounts (also vims for compatibility) O O
117 /<id> O O O
118 /wim_accounts O O
tierno2236d202018-05-16 19:05:16 +0200119 /<id> O O O
tierno0f98af52018-03-19 10:28:22 +0100120 /sdns O O
tierno2236d202018-05-16 19:05:16 +0200121 /<id> O O O
delacruzramofe598fe2019-10-23 18:25:11 +0200122 /k8sclusters O O
123 /<id> O O O
124 /k8srepos O O
125 /<id> O O
Felipe Vicensb66b0412020-05-06 10:11:00 +0200126 /osmrepos O O
127 /<id> O O
tiernoc94c3df2018-02-09 15:38:54 +0100128
garciadeblas9750c5a2018-10-15 16:20:35 +0200129 /nst/v1 O O
130 /netslice_templates_content O O
131 /<nstInfoId> O O O O
132 /netslice_templates O O
133 /<nstInfoId> O O O
134 /nst_content O O
135 /nst O
136 /artifacts[/<artifactPath>] O
137 /subscriptions X X
138 /<subscriptionId> X X
139
140 /nsilcm/v1
141 /netslice_instances_content O O
142 /<SliceInstanceId> O O
143 /netslice_instances O O
144 /<SliceInstanceId> O O
145 instantiate O
146 terminate O
147 action O
148 /nsi_lcm_op_occs O O
149 /<nsiLcmOpOccId> O O O
150 /subscriptions X X
151 /<subscriptionId> X X
152
rshri2d386cb2024-07-05 14:35:51 +0000153 /k8scluster/v1
154 /clusters O O
155 /<clustersId> O O
156 app_profiles O O
157 infra_controller_profiles O O
158 infra_config_profiles O O
159 resource_profiles O O
160 deregister O
161 /register O
162 /app_profiles O O
163 /<app_profilesId> O O O
164 /infra_controller_profiles O O
165 /<infra_controller_profilesId> O O O
166 /infra_config_profiles O O
167 /<infra_config_profilesId> O O O
168 /resource_profiles O O
169 /<resource_profilesID> O O O
170
tierno2236d202018-05-16 19:05:16 +0200171query string:
172 Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force.
tierno36ec8602018-11-02 17:27:11 +0100173 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
174 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
175 op := "eq" | "neq" (or "ne") | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
176 attrName := string
tierno2236d202018-05-16 19:05:16 +0200177 For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any
178 item of the array, that is, pass if any item of the array pass the filter.
179 It allows both ne and neq for not equal
180 TODO: 4.3.3 Attribute selectors
181 all_fields, fields=x,y,.., exclude_default, exclude_fields=x,y,...
tiernoc94c3df2018-02-09 15:38:54 +0100182 (none) … same as “exclude_default”
183 all_fields … all attributes.
tierno2236d202018-05-16 19:05:16 +0200184 fields=<list> … all attributes except all complex attributes with minimum cardinality of zero that are not
185 conditionally mandatory, and that are not provided in <list>.
186 exclude_fields=<list> … all attributes except those complex attributes with a minimum cardinality of zero that
187 are not conditionally mandatory, and that are provided in <list>.
188 exclude_default … all attributes except those complex attributes with a minimum cardinality of zero that are not
189 conditionally mandatory, and that are part of the "default exclude set" defined in the present specification for
190 the particular resource
191 exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality
192 of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the
193 present specification for the particular resource, but that are not part of <list>
tierno65ca36d2019-02-12 19:27:52 +0100194 Additionally it admits some administrator values:
195 FORCE: To force operations skipping dependency checkings
196 ADMIN: To act as an administrator or a different project
197 PUBLIC: To get public descriptors or set a descriptor as public
198 SET_PROJECT: To make a descriptor available for other project
beierlmbc5a5242022-05-17 21:25:29 -0400199
tiernoc94c3df2018-02-09 15:38:54 +0100200Header field name Reference Example Descriptions
201 Accept IETF RFC 7231 [19] application/json Content-Types that are acceptable for the response.
202 This header field shall be present if the response is expected to have a non-empty message body.
203 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the request.
204 This header field shall be present if the request has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200205 Authorization IETF RFC 7235 [22] Bearer mF_9.B5f-4.1JqM The authorization token for the request.
206 Details are specified in clause 4.5.3.
tiernoc94c3df2018-02-09 15:38:54 +0100207 Range IETF RFC 7233 [21] 1000-2000 Requested range of bytes from a file
208Header field name Reference Example Descriptions
209 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the response.
210 This header field shall be present if the response has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200211 Location IETF RFC 7231 [19] http://www.example.com/vnflcm/v1/vnf_instances/123 Used in redirection, or when a
212 new resource has been created.
tiernoc94c3df2018-02-09 15:38:54 +0100213 This header field shall be present if the response status code is 201 or 3xx.
tierno2236d202018-05-16 19:05:16 +0200214 In the present document this header field is also used if the response status code is 202 and a new resource was
215 created.
216 WWW-Authenticate IETF RFC 7235 [22] Bearer realm="example" Challenge if the corresponding HTTP request has not
217 provided authorization, or error details if the corresponding HTTP request has provided an invalid authorization
218 token.
219 Accept-Ranges IETF RFC 7233 [21] bytes Used by the Server to signal whether or not it supports ranges for
220 certain resources.
221 Content-Range IETF RFC 7233 [21] bytes 21010-47021/ 47022 Signals the byte range that is contained in the
222 response, and the total length of the file.
tiernoc94c3df2018-02-09 15:38:54 +0100223 Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT
tiernoc94c3df2018-02-09 15:38:54 +0100224"""
225
tierno701018c2019-06-25 11:13:14 +0000226valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC")
227# ^ Contains possible administrative query string words:
228# ADMIN=True(by default)|Project|Project-list: See all elements, or elements of a project
229# (not owned by my session project).
230# PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public
231# FORCE=True(by default)|False: Force edition/deletion operations
232# SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio
233
234valid_url_methods = {
235 # contains allowed URL and methods, and the role_permission name
236 "admin": {
237 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100238 "tokens": {
239 "METHODS": ("GET", "POST", "DELETE"),
240 "ROLE_PERMISSION": "tokens:",
241 "<ID>": {"METHODS": ("GET", "DELETE"), "ROLE_PERMISSION": "tokens:id:"},
242 },
243 "users": {
244 "METHODS": ("GET", "POST"),
245 "ROLE_PERMISSION": "users:",
246 "<ID>": {
247 "METHODS": ("GET", "DELETE", "PATCH"),
248 "ROLE_PERMISSION": "users:id:",
249 },
250 },
251 "projects": {
252 "METHODS": ("GET", "POST"),
253 "ROLE_PERMISSION": "projects:",
254 "<ID>": {
255 "METHODS": ("GET", "DELETE", "PATCH"),
256 "ROLE_PERMISSION": "projects:id:",
257 },
258 },
259 "roles": {
260 "METHODS": ("GET", "POST"),
261 "ROLE_PERMISSION": "roles:",
262 "<ID>": {
263 "METHODS": ("GET", "DELETE", "PATCH"),
264 "ROLE_PERMISSION": "roles:id:",
265 },
266 },
267 "vims": {
268 "METHODS": ("GET", "POST"),
269 "ROLE_PERMISSION": "vims:",
270 "<ID>": {
271 "METHODS": ("GET", "DELETE", "PATCH"),
272 "ROLE_PERMISSION": "vims:id:",
273 },
274 },
275 "vim_accounts": {
276 "METHODS": ("GET", "POST"),
277 "ROLE_PERMISSION": "vim_accounts:",
278 "<ID>": {
279 "METHODS": ("GET", "DELETE", "PATCH"),
280 "ROLE_PERMISSION": "vim_accounts:id:",
281 },
282 },
283 "wim_accounts": {
284 "METHODS": ("GET", "POST"),
285 "ROLE_PERMISSION": "wim_accounts:",
286 "<ID>": {
287 "METHODS": ("GET", "DELETE", "PATCH"),
288 "ROLE_PERMISSION": "wim_accounts:id:",
289 },
290 },
291 "sdns": {
292 "METHODS": ("GET", "POST"),
293 "ROLE_PERMISSION": "sdn_controllers:",
294 "<ID>": {
295 "METHODS": ("GET", "DELETE", "PATCH"),
296 "ROLE_PERMISSION": "sdn_controllers:id:",
297 },
298 },
299 "k8sclusters": {
300 "METHODS": ("GET", "POST"),
301 "ROLE_PERMISSION": "k8sclusters:",
302 "<ID>": {
303 "METHODS": ("GET", "DELETE", "PATCH"),
304 "ROLE_PERMISSION": "k8sclusters:id:",
305 },
306 },
307 "vca": {
308 "METHODS": ("GET", "POST"),
309 "ROLE_PERMISSION": "vca:",
310 "<ID>": {
311 "METHODS": ("GET", "DELETE", "PATCH"),
312 "ROLE_PERMISSION": "vca:id:",
313 },
314 },
315 "k8srepos": {
316 "METHODS": ("GET", "POST"),
317 "ROLE_PERMISSION": "k8srepos:",
318 "<ID>": {
319 "METHODS": ("GET", "DELETE"),
320 "ROLE_PERMISSION": "k8srepos:id:",
321 },
322 },
323 "osmrepos": {
324 "METHODS": ("GET", "POST"),
325 "ROLE_PERMISSION": "osmrepos:",
326 "<ID>": {
327 "METHODS": ("GET", "DELETE", "PATCH"),
328 "ROLE_PERMISSION": "osmrepos:id:",
329 },
330 },
331 "domains": {
332 "METHODS": ("GET",),
333 "ROLE_PERMISSION": "domains:",
334 },
tierno701018c2019-06-25 11:13:14 +0000335 }
336 },
337 "pdu": {
338 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100339 "pdu_descriptors": {
340 "METHODS": ("GET", "POST"),
341 "ROLE_PERMISSION": "pduds:",
342 "<ID>": {
343 "METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"),
344 "ROLE_PERMISSION": "pduds:id:",
345 },
346 },
tierno701018c2019-06-25 11:13:14 +0000347 }
348 },
349 "nsd": {
350 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100351 "ns_descriptors_content": {
352 "METHODS": ("GET", "POST"),
353 "ROLE_PERMISSION": "nsds:",
354 "<ID>": {
355 "METHODS": ("GET", "PUT", "DELETE"),
356 "ROLE_PERMISSION": "nsds:id:",
357 },
358 },
359 "ns_descriptors": {
360 "METHODS": ("GET", "POST"),
361 "ROLE_PERMISSION": "nsds:",
362 "<ID>": {
363 "METHODS": ("GET", "DELETE", "PATCH"),
364 "ROLE_PERMISSION": "nsds:id:",
365 "nsd_content": {
366 "METHODS": ("GET", "PUT"),
367 "ROLE_PERMISSION": "nsds:id:content:",
368 },
369 "nsd": {
370 "METHODS": ("GET",), # descriptor inside package
371 "ROLE_PERMISSION": "nsds:id:content:",
372 },
373 "artifacts": {
374 "METHODS": ("GET",),
375 "ROLE_PERMISSION": "nsds:id:nsd_artifact:",
376 "*": None,
377 },
378 },
379 },
kayal2001f71c2e82024-06-25 15:26:24 +0530380 "ns_config_template": {
381 "METHODS": ("GET", "POST"),
382 "ROLE_PERMISSION": "ns_config_template:content:",
383 "<ID>": {
384 "METHODS": ("GET", "DELETE"),
385 "ROLE_PERMISSION": "ns_config_template:id:",
386 "template_content": {
387 "METHODS": ("GET", "PUT"),
388 "ROLE_PERMISSION": "ns_config_template:id:content:",
389 },
390 },
391 },
garciadeblas4568a372021-03-24 09:19:48 +0100392 "pnf_descriptors": {
393 "TODO": ("GET", "POST"),
394 "<ID>": {
395 "TODO": ("GET", "DELETE", "PATCH"),
396 "pnfd_content": {"TODO": ("GET", "PUT")},
397 },
398 },
399 "subscriptions": {
400 "TODO": ("GET", "POST"),
401 "<ID>": {"TODO": ("GET", "DELETE")},
402 },
tierno701018c2019-06-25 11:13:14 +0000403 }
404 },
405 "vnfpkgm": {
406 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100407 "vnf_packages_content": {
408 "METHODS": ("GET", "POST"),
409 "ROLE_PERMISSION": "vnfds:",
410 "<ID>": {
411 "METHODS": ("GET", "PUT", "DELETE"),
412 "ROLE_PERMISSION": "vnfds:id:",
413 },
414 },
415 "vnf_packages": {
416 "METHODS": ("GET", "POST"),
417 "ROLE_PERMISSION": "vnfds:",
418 "<ID>": {
419 "METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo
420 "ROLE_PERMISSION": "vnfds:id:",
421 "package_content": {
422 "METHODS": ("GET", "PUT"), # package
423 "ROLE_PERMISSION": "vnfds:id:",
424 "upload_from_uri": {
425 "METHODS": (),
426 "TODO": ("POST",),
427 "ROLE_PERMISSION": "vnfds:id:upload:",
428 },
429 },
430 "vnfd": {
431 "METHODS": ("GET",), # descriptor inside package
432 "ROLE_PERMISSION": "vnfds:id:content:",
433 },
434 "artifacts": {
435 "METHODS": ("GET",),
436 "ROLE_PERMISSION": "vnfds:id:vnfd_artifact:",
437 "*": None,
438 },
439 "action": {
440 "METHODS": ("POST",),
441 "ROLE_PERMISSION": "vnfds:id:action:",
442 },
443 },
444 },
445 "subscriptions": {
446 "TODO": ("GET", "POST"),
447 "<ID>": {"TODO": ("GET", "DELETE")},
448 },
449 "vnfpkg_op_occs": {
450 "METHODS": ("GET",),
451 "ROLE_PERMISSION": "vnfds:vnfpkgops:",
452 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnfds:vnfpkgops:id:"},
453 },
tierno701018c2019-06-25 11:13:14 +0000454 }
455 },
456 "nslcm": {
457 "v1": {
adurti3af50952024-05-31 11:36:57 +0530458 "ns_instances_terminate": {
459 "METHODS": ("POST"),
460 "ROLE_PERMISSION": "ns_instances:",
461 },
garciadeblas4568a372021-03-24 09:19:48 +0100462 "ns_instances_content": {
463 "METHODS": ("GET", "POST"),
464 "ROLE_PERMISSION": "ns_instances:",
465 "<ID>": {
466 "METHODS": ("GET", "DELETE"),
467 "ROLE_PERMISSION": "ns_instances:id:",
468 },
469 },
470 "ns_instances": {
471 "METHODS": ("GET", "POST"),
472 "ROLE_PERMISSION": "ns_instances:",
473 "<ID>": {
474 "METHODS": ("GET", "DELETE"),
475 "ROLE_PERMISSION": "ns_instances:id:",
garciadeblas0964edf2022-02-11 00:43:44 +0100476 "heal": {
477 "METHODS": ("POST",),
478 "ROLE_PERMISSION": "ns_instances:id:heal:",
479 },
garciadeblas4568a372021-03-24 09:19:48 +0100480 "scale": {
481 "METHODS": ("POST",),
482 "ROLE_PERMISSION": "ns_instances:id:scale:",
483 },
484 "terminate": {
485 "METHODS": ("POST",),
486 "ROLE_PERMISSION": "ns_instances:id:terminate:",
487 },
488 "instantiate": {
489 "METHODS": ("POST",),
490 "ROLE_PERMISSION": "ns_instances:id:instantiate:",
491 },
elumalai8e3806c2022-04-28 17:26:24 +0530492 "migrate": {
493 "METHODS": ("POST",),
494 "ROLE_PERMISSION": "ns_instances:id:migrate:",
495 },
garciadeblas4568a372021-03-24 09:19:48 +0100496 "action": {
497 "METHODS": ("POST",),
498 "ROLE_PERMISSION": "ns_instances:id:action:",
499 },
aticig544a2ae2022-04-05 09:00:17 +0300500 "update": {
501 "METHODS": ("POST",),
502 "ROLE_PERMISSION": "ns_instances:id:update:",
503 },
garciadeblas4568a372021-03-24 09:19:48 +0100504 },
505 },
506 "ns_lcm_op_occs": {
507 "METHODS": ("GET",),
508 "ROLE_PERMISSION": "ns_instances:opps:",
509 "<ID>": {
510 "METHODS": ("GET",),
511 "ROLE_PERMISSION": "ns_instances:opps:id:",
Gabriel Cuba84a60df2023-10-30 14:01:54 -0500512 "cancel": {
513 "METHODS": ("POST",),
514 "ROLE_PERMISSION": "ns_instances:opps:cancel:",
515 },
garciadeblas4568a372021-03-24 09:19:48 +0100516 },
517 },
518 "vnfrs": {
519 "METHODS": ("GET",),
520 "ROLE_PERMISSION": "vnf_instances:",
521 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
522 },
523 "vnf_instances": {
524 "METHODS": ("GET",),
525 "ROLE_PERMISSION": "vnf_instances:",
526 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
527 },
528 "subscriptions": {
529 "METHODS": ("GET", "POST"),
530 "ROLE_PERMISSION": "ns_subscriptions:",
531 "<ID>": {
532 "METHODS": ("GET", "DELETE"),
533 "ROLE_PERMISSION": "ns_subscriptions:id:",
534 },
535 },
tierno701018c2019-06-25 11:13:14 +0000536 }
537 },
almagiae47b9132022-05-17 14:12:22 +0200538 "vnflcm": {
539 "v1": {
garciadeblasf2af4a12023-01-24 16:56:54 +0100540 "vnf_instances": {
541 "METHODS": ("GET", "POST"),
542 "ROLE_PERMISSION": "vnflcm_instances:",
543 "<ID>": {
544 "METHODS": ("GET", "DELETE"),
545 "ROLE_PERMISSION": "vnflcm_instances:id:",
546 "scale": {
547 "METHODS": ("POST",),
548 "ROLE_PERMISSION": "vnflcm_instances:id:scale:",
549 },
550 "terminate": {
551 "METHODS": ("POST",),
552 "ROLE_PERMISSION": "vnflcm_instances:id:terminate:",
553 },
554 "instantiate": {
555 "METHODS": ("POST",),
556 "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:",
557 },
558 },
559 },
560 "vnf_lcm_op_occs": {
561 "METHODS": ("GET",),
562 "ROLE_PERMISSION": "vnf_instances:opps:",
563 "<ID>": {
564 "METHODS": ("GET",),
565 "ROLE_PERMISSION": "vnf_instances:opps:id:",
566 },
567 },
568 "subscriptions": {
569 "METHODS": ("GET", "POST"),
570 "ROLE_PERMISSION": "vnflcm_subscriptions:",
571 "<ID>": {
572 "METHODS": ("GET", "DELETE"),
573 "ROLE_PERMISSION": "vnflcm_subscriptions:id:",
574 },
575 },
almagiae47b9132022-05-17 14:12:22 +0200576 }
577 },
tierno701018c2019-06-25 11:13:14 +0000578 "nst": {
579 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100580 "netslice_templates_content": {
581 "METHODS": ("GET", "POST"),
582 "ROLE_PERMISSION": "slice_templates:",
583 "<ID>": {
584 "METHODS": ("GET", "PUT", "DELETE"),
585 "ROLE_PERMISSION": "slice_templates:id:",
586 },
587 },
588 "netslice_templates": {
589 "METHODS": ("GET", "POST"),
590 "ROLE_PERMISSION": "slice_templates:",
591 "<ID>": {
592 "METHODS": ("GET", "DELETE"),
593 "TODO": ("PATCH",),
594 "ROLE_PERMISSION": "slice_templates:id:",
595 "nst_content": {
596 "METHODS": ("GET", "PUT"),
597 "ROLE_PERMISSION": "slice_templates:id:content:",
598 },
599 "nst": {
600 "METHODS": ("GET",), # descriptor inside package
601 "ROLE_PERMISSION": "slice_templates:id:content:",
602 },
603 "artifacts": {
604 "METHODS": ("GET",),
605 "ROLE_PERMISSION": "slice_templates:id:content:",
606 "*": None,
607 },
608 },
609 },
610 "subscriptions": {
611 "TODO": ("GET", "POST"),
612 "<ID>": {"TODO": ("GET", "DELETE")},
613 },
tierno701018c2019-06-25 11:13:14 +0000614 }
615 },
616 "nsilcm": {
617 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100618 "netslice_instances_content": {
619 "METHODS": ("GET", "POST"),
620 "ROLE_PERMISSION": "slice_instances:",
621 "<ID>": {
622 "METHODS": ("GET", "DELETE"),
623 "ROLE_PERMISSION": "slice_instances:id:",
624 },
625 },
626 "netslice_instances": {
627 "METHODS": ("GET", "POST"),
628 "ROLE_PERMISSION": "slice_instances:",
629 "<ID>": {
630 "METHODS": ("GET", "DELETE"),
631 "ROLE_PERMISSION": "slice_instances:id:",
632 "terminate": {
633 "METHODS": ("POST",),
634 "ROLE_PERMISSION": "slice_instances:id:terminate:",
635 },
636 "instantiate": {
637 "METHODS": ("POST",),
638 "ROLE_PERMISSION": "slice_instances:id:instantiate:",
639 },
640 "action": {
641 "METHODS": ("POST",),
642 "ROLE_PERMISSION": "slice_instances:id:action:",
643 },
644 },
645 },
646 "nsi_lcm_op_occs": {
647 "METHODS": ("GET",),
648 "ROLE_PERMISSION": "slice_instances:opps:",
649 "<ID>": {
650 "METHODS": ("GET",),
651 "ROLE_PERMISSION": "slice_instances:opps:id:",
652 },
653 },
tierno701018c2019-06-25 11:13:14 +0000654 }
655 },
656 "nspm": {
657 "v1": {
658 "pm_jobs": {
659 "<ID>": {
660 "reports": {
garciadeblas4568a372021-03-24 09:19:48 +0100661 "<ID>": {
662 "METHODS": ("GET",),
663 "ROLE_PERMISSION": "reports:id:",
664 }
tierno701018c2019-06-25 11:13:14 +0000665 }
666 },
667 },
668 },
669 },
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000670 "nsfm": {
671 "v1": {
garciadeblasf2af4a12023-01-24 16:56:54 +0100672 "alarms": {
673 "METHODS": ("GET", "PATCH"),
674 "ROLE_PERMISSION": "alarms:",
675 "<ID>": {
676 "METHODS": ("GET", "PATCH"),
677 "ROLE_PERMISSION": "alarms:id:",
678 },
679 }
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000680 },
681 },
rshri2d386cb2024-07-05 14:35:51 +0000682 "k8scluster": {
683 "v1": {
684 "clusters": {
685 "METHODS": ("GET", "POST"),
686 "ROLE_PERMISSION": "k8scluster:",
687 "<ID>": {
688 "METHODS": ("GET", "DELETE"),
689 "ROLE_PERMISSION": "k8scluster:id:",
690 "app_profiles": {
691 "METHODS": ("PATCH", "GET"),
692 "ROLE_PERMISSION": "k8scluster:id:app_profiles:",
693 },
694 "infra_controller_profiles": {
695 "METHODS": ("PATCH", "GET"),
696 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:",
697 },
698 "infra_config_profiles": {
699 "METHODS": ("PATCH", "GET"),
700 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:",
701 },
702 "resource_profiles": {
703 "METHODS": ("PATCH", "GET"),
704 "ROLE_PERMISSION": "k8scluster:id:infra_profiles:",
705 },
706 "deregister": {
707 "METHODS": ("DELETE",),
708 "ROLE_PERMISSION": "k8scluster:id:deregister:",
709 },
yshah53cc9eb2024-07-05 13:06:31 +0000710 "get_creds": {
711 "METHODS": ("GET",),
712 "ROLE_PERMISSION": "k8scluster:id:get_creds:",
713 },
714 "scale": {
715 "METHODS": ("POST",),
716 "ROLE_PERMISSION": "k8scluster:id:scale:",
717 },
718 "upgrade": {
719 "METHODS": ("POST",),
720 "ROLE_PERMISSION": "k8scluster:id:upgrade:",
721 },
rshri2d386cb2024-07-05 14:35:51 +0000722 },
723 "register": {
724 "METHODS": ("POST",),
725 "ROLE_PERMISSION": "k8scluster:register:",
726 },
727 },
728 "app_profiles": {
729 "METHODS": ("POST", "GET"),
730 "ROLE_PERMISSION": "k8scluster:app_profiles:",
731 "<ID>": {
732 "METHODS": ("GET", "PATCH", "DELETE"),
733 "ROLE_PERMISSION": "k8scluster:app_profiles:id:",
734 },
735 },
736 "infra_controller_profiles": {
737 "METHODS": ("POST", "GET"),
738 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:",
739 "<ID>": {
740 "METHODS": ("GET", "PATCH", "DELETE"),
741 "ROLE_PERMISSION": "k8scluster:infra_controller_profiles:id:",
742 },
743 },
744 "infra_config_profiles": {
745 "METHODS": ("POST", "GET"),
746 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:",
747 "<ID>": {
748 "METHODS": ("GET", "PATCH", "DELETE"),
749 "ROLE_PERMISSION": "k8scluster:infra_config_profiles:id:",
750 },
751 },
752 "resource_profiles": {
753 "METHODS": ("POST", "GET"),
754 "ROLE_PERMISSION": "k8scluster:resource_profiles:",
755 "<ID>": {
756 "METHODS": ("GET", "PATCH", "DELETE"),
757 "ROLE_PERMISSION": "k8scluster:resource_profiles:id:",
758 },
759 },
760 }
761 },
yshah53cc9eb2024-07-05 13:06:31 +0000762 "ksu": {
763 "v1": {
764 "ksus": {
765 "METHODS": ("GET", "POST"),
766 "ROLE_PERMISSION": "ksu:",
767 "<ID>": {
768 "METHODS": ("GET", "PATCH", "DELETE"),
769 "ROLE_PERMISSION": "ksu:id:",
770 "clone": {
771 "METHODS": ("POST",),
772 "ROLE_PERMISSION": "ksu:id:clone:",
773 },
774 "move": {
775 "METHODS": ("POST",),
776 "ROLE_PERMISSION": "ksu:id:move:",
777 },
778 },
779 "update": {
780 "METHODS": ("POST",),
781 "ROLE_PERMISSION": "ksu:",
782 },
783 "delete": {
784 "METHODS": ("POST",),
785 "ROLE_PERMISSION": "ksu:",
786 },
787 },
788 }
789 },
790 "oka": {
791 "v1": {
792 "oka_packages": {
793 "METHODS": ("GET", "POST"),
794 "ROLE_PERMISSION": "oka_pkg:",
795 "<ID>": {
796 "METHODS": ("GET", "PATCH", "DELETE", "PUT"),
797 "ROLE_PERMISSION": "oka_pkg:id:",
798 },
799 }
800 }
801 },
tierno701018c2019-06-25 11:13:14 +0000802}
803
tiernoc94c3df2018-02-09 15:38:54 +0100804
805class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100806 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
807 Exception.__init__(self, message)
808 self.http_code = http_code
809
810
811class Server(object):
812 instance = 0
813 # to decode bytes to str
814 reader = getreader("utf-8")
815
816 def __init__(self):
817 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000818 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100819 self.engine = Engine(self.authenticator)
yshah53cc9eb2024-07-05 13:06:31 +0000820 self.logger = logging.getLogger("nbi.server")
tiernoc94c3df2018-02-09 15:38:54 +0100821
tiernoc94c3df2018-02-09 15:38:54 +0100822 def _format_in(self, kwargs):
garciadeblasf2af4a12023-01-24 16:56:54 +0100823 error_text = "" # error_text must be initialized outside try
tiernoc94c3df2018-02-09 15:38:54 +0100824 try:
825 indata = None
826 if cherrypy.request.body.length:
827 error_text = "Invalid input format "
828
829 if "Content-Type" in cherrypy.request.headers:
830 if "application/json" in cherrypy.request.headers["Content-Type"]:
831 error_text = "Invalid json format "
832 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100833 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100834 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
835 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100836 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100837 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100838 elif (
839 "application/binary" in cherrypy.request.headers["Content-Type"]
840 or "application/gzip"
841 in cherrypy.request.headers["Content-Type"]
842 or "application/zip" in cherrypy.request.headers["Content-Type"]
843 or "text/plain" in cherrypy.request.headers["Content-Type"]
844 ):
tiernof27c79b2018-03-12 17:08:42 +0100845 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100846 elif (
847 "multipart/form-data"
848 in cherrypy.request.headers["Content-Type"]
849 ):
yshah53cc9eb2024-07-05 13:06:31 +0000850 if (
851 "descriptor_file" in kwargs
852 or "package" in kwargs
853 and "name" in kwargs
854 ):
855 filecontent = ""
856 if "descriptor_file" in kwargs:
857 filecontent = kwargs.pop("descriptor_file")
858 if "package" in kwargs:
859 filecontent = kwargs.pop("package")
tiernoc94c3df2018-02-09 15:38:54 +0100860 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100861 raise NbiException(
862 "empty file or content", HTTPStatus.BAD_REQUEST
863 )
yshah53cc9eb2024-07-05 13:06:31 +0000864 indata = filecontent
865 if filecontent.content_type.value:
866 cherrypy.request.headers[
867 "Content-Type"
868 ] = filecontent.content_type.value
869 elif "package" in kwargs:
870 filecontent = kwargs.pop("package")
871 if not filecontent.file:
872 raise NbiException(
873 "empty file or content", HTTPStatus.BAD_REQUEST
874 )
875 indata = filecontent
tiernoc94c3df2018-02-09 15:38:54 +0100876 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100877 cherrypy.request.headers[
878 "Content-Type"
879 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100880 else:
881 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
882 # "Only 'Content-Type' of type 'application/json' or
883 # 'application/yaml' for input format are available")
884 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100885 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100886 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100887 else:
888 error_text = "Invalid yaml format "
garciadeblas4cd875d2023-02-14 19:05:34 +0100889 indata = yaml.safe_load(cherrypy.request.body)
gcalvinode4adfe2018-10-30 11:46:09 +0100890 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100891 if not indata:
892 indata = {}
tiernoc94c3df2018-02-09 15:38:54 +0100893 format_yaml = False
894 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
895 format_yaml = True
896
897 for k, v in kwargs.items():
898 if isinstance(v, str):
899 if v == "":
900 kwargs[k] = None
901 elif format_yaml:
902 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100903 kwargs[k] = yaml.safe_load(v)
tiernoe1281182018-05-22 12:24:36 +0200904 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100905 pass
garciadeblas4568a372021-03-24 09:19:48 +0100906 elif (
907 k.endswith(".gt")
908 or k.endswith(".lt")
909 or k.endswith(".gte")
910 or k.endswith(".lte")
911 ):
tiernoc94c3df2018-02-09 15:38:54 +0100912 try:
913 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200914 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100915 try:
916 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200917 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100918 pass
919 elif v.find(",") > 0:
920 kwargs[k] = v.split(",")
921 elif isinstance(v, (list, tuple)):
922 for index in range(0, len(v)):
923 if v[index] == "":
924 v[index] = None
925 elif format_yaml:
926 try:
garciadeblas4cd875d2023-02-14 19:05:34 +0100927 v[index] = yaml.safe_load(v[index])
tiernoe1281182018-05-22 12:24:36 +0200928 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100929 pass
930
tiernof27c79b2018-03-12 17:08:42 +0100931 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100932 except (ValueError, yaml.YAMLError) as exc:
933 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
934 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100935 raise NbiException(
936 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
937 )
tiernob92094f2018-05-11 13:44:22 +0200938 except Exception as exc:
939 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100940
941 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000942 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100943 """
944 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100945 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000946 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000947 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100948 :return: None
949 """
tierno0f98af52018-03-19 10:28:22 +0100950 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100951 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100952 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100953 return html.format(
954 data, cherrypy.request, cherrypy.response, token_info
955 )
tierno09c073e2018-04-26 13:36:48 +0200956 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100957 return
958 elif hasattr(data, "read"): # file object
959 if _format:
960 cherrypy.response.headers["Content-Type"] = _format
961 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100962 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100963 else:
garciadeblas4568a372021-03-24 09:19:48 +0100964 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100965 # TODO check that cherrypy close file. If not implement pending things to close per thread next
966 return data
tierno0f98af52018-03-19 10:28:22 +0100967 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000968 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100969 return html.format(
970 data, cherrypy.request, cherrypy.response, token_info
971 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000972 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100973 pass
garciadeblas4568a372021-03-24 09:19:48 +0100974 elif "application/json" in accept or (
975 cherrypy.response.status and cherrypy.response.status >= 300
976 ):
977 cherrypy.response.headers[
978 "Content-Type"
979 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000980 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100981 return a.encode("utf8")
982 cherrypy.response.headers["Content-Type"] = "application/yaml"
983 return yaml.safe_dump(
984 data,
985 explicit_start=True,
986 indent=4,
987 default_flow_style=False,
988 tags=False,
989 encoding="utf-8",
990 allow_unicode=True,
991 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +0100992
993 @cherrypy.expose
994 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +0000995 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100996 try:
997 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +0000998 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +0100999 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +01001000 else:
garciadeblas4568a372021-03-24 09:19:48 +01001001 raise cherrypy.HTTPError(
1002 HTTPStatus.METHOD_NOT_ALLOWED.value,
1003 "Method {} not allowed for tokens".format(cherrypy.request.method),
1004 )
tiernoc94c3df2018-02-09 15:38:54 +01001005
tierno701018c2019-06-25 11:13:14 +00001006 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001007
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001008 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +00001009 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +01001010 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +00001011 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001012
1013 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +02001014 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +02001015 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +02001016 try:
1017 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001018 raise NbiException(
1019 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
1020 )
tierno55945e72018-04-06 16:40:27 +02001021 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001022 raise NbiException(
1023 "Invalid URL or query string for version",
1024 HTTPStatus.METHOD_NOT_ALLOWED,
1025 )
tierno9c630112019-08-29 14:21:41 +00001026 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +00001027 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
1028 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +02001029 except NbiException as e:
1030 cherrypy.response.status = e.http_code.value
1031 problem_details = {
1032 "code": e.http_code.name,
1033 "status": e.http_code.value,
1034 "detail": str(e),
1035 }
1036 return self._format_out(problem_details, None)
1037
tierno12eac3c2020-03-19 23:22:08 +00001038 def domain(self):
1039 try:
1040 domains = {
garciadeblas4568a372021-03-24 09:19:48 +01001041 "user_domain_name": cherrypy.tree.apps["/osm"]
1042 .config["authentication"]
1043 .get("user_domain_name"),
1044 "project_domain_name": cherrypy.tree.apps["/osm"]
1045 .config["authentication"]
1046 .get("project_domain_name"),
1047 }
tierno12eac3c2020-03-19 23:22:08 +00001048 return self._format_out(domains)
1049 except NbiException as e:
1050 cherrypy.response.status = e.http_code.value
1051 problem_details = {
1052 "code": e.http_code.name,
1053 "status": e.http_code.value,
1054 "detail": str(e),
1055 }
1056 return self._format_out(problem_details, None)
1057
tiernoa5035702019-07-29 08:54:42 +00001058 @staticmethod
1059 def _format_login(token_info):
1060 """
1061 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
1062 log this information
1063 :param token_info: Dictionary with token content
1064 :return: None
1065 """
1066 cherrypy.request.login = token_info.get("username", "-")
1067 if token_info.get("project_name"):
1068 cherrypy.request.login += "/" + token_info["project_name"]
1069 if token_info.get("id"):
1070 cherrypy.request.login += ";session=" + token_info["id"][0:12]
1071
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001072 # NS Fault Management
1073 @cherrypy.expose
garciadeblasf2af4a12023-01-24 16:56:54 +01001074 def nsfm(
1075 self,
1076 version=None,
1077 topic=None,
1078 uuid=None,
1079 project_name=None,
1080 ns_id=None,
1081 *args,
1082 **kwargs
1083 ):
1084 if topic == "alarms":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001085 try:
1086 method = cherrypy.request.method
garciadeblasf2af4a12023-01-24 16:56:54 +01001087 role_permission = self._check_valid_url_method(
1088 method, "nsfm", version, topic, None, None, *args
1089 )
1090 query_string_operations = self._extract_query_string_operations(
1091 kwargs, method
1092 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001093
garciadeblasf2af4a12023-01-24 16:56:54 +01001094 self.authenticator.authorize(
1095 role_permission, query_string_operations, None
1096 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001097
1098 # to handle get request
garciadeblasf2af4a12023-01-24 16:56:54 +01001099 if cherrypy.request.method == "GET":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001100 # if request is on basis of uuid
garciadeblasf2af4a12023-01-24 16:56:54 +01001101 if uuid and uuid != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001102 try:
1103 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
garciadeblasf2af4a12023-01-24 16:56:54 +01001104 alarm_action = self.engine.db.get_one(
1105 "alarms_action", {"uuid": uuid}
1106 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001107 alarm.update(alarm_action)
garciadeblasf2af4a12023-01-24 16:56:54 +01001108 vnf = self.engine.db.get_one(
1109 "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}
1110 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001111 alarm["vnf-id"] = vnf["_id"]
1112 return self._format_out(str(alarm))
1113 except Exception:
1114 return self._format_out("Please provide valid alarm uuid")
garciadeblasf2af4a12023-01-24 16:56:54 +01001115 elif ns_id and ns_id != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001116 # if request is on basis of ns_id
1117 try:
garciadeblasf2af4a12023-01-24 16:56:54 +01001118 alarms = self.engine.db.get_list(
1119 "alarms", {"tags.ns_id": ns_id}
1120 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001121 for alarm in alarms:
garciadeblasf2af4a12023-01-24 16:56:54 +01001122 alarm_action = self.engine.db.get_one(
1123 "alarms_action", {"uuid": alarm["uuid"]}
1124 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001125 alarm.update(alarm_action)
1126 return self._format_out(str(alarms))
1127 except Exception:
1128 return self._format_out("Please provide valid ns id")
1129 else:
1130 # to return only alarm which are related to given project
garciadeblasf2af4a12023-01-24 16:56:54 +01001131 project = self.engine.db.get_one(
1132 "projects", {"name": project_name}
1133 )
1134 project_id = project.get("_id")
1135 ns_list = self.engine.db.get_list(
1136 "nsrs", {"_admin.projects_read": project_id}
1137 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001138 ns_ids = []
1139 for ns in ns_list:
1140 ns_ids.append(ns.get("_id"))
1141 alarms = self.engine.db.get_list("alarms")
garciadeblasf2af4a12023-01-24 16:56:54 +01001142 alarm_list = [
1143 alarm
1144 for alarm in alarms
1145 if alarm["tags"]["ns_id"] in ns_ids
1146 ]
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001147 for alrm in alarm_list:
garciadeblasf2af4a12023-01-24 16:56:54 +01001148 action = self.engine.db.get_one(
1149 "alarms_action", {"uuid": alrm.get("uuid")}
1150 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001151 alrm.update(action)
1152 return self._format_out(str(alarm_list))
1153 # to handle patch request for alarm update
garciadeblasf2af4a12023-01-24 16:56:54 +01001154 elif cherrypy.request.method == "PATCH":
garciadeblas4cd875d2023-02-14 19:05:34 +01001155 data = yaml.safe_load(cherrypy.request.body)
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001156 try:
1157 # check if uuid is valid
1158 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
1159 except Exception:
1160 return self._format_out("Please provide valid alarm uuid.")
1161 if data.get("is_enable") is not None:
1162 if data.get("is_enable"):
garciadeblasf2af4a12023-01-24 16:56:54 +01001163 alarm_status = "ok"
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001164 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001165 alarm_status = "disabled"
1166 self.engine.db.set_one(
1167 "alarms",
1168 {"uuid": data.get("uuid")},
1169 {"alarm_status": alarm_status},
1170 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001171 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001172 self.engine.db.set_one(
1173 "alarms",
1174 {"uuid": data.get("uuid")},
1175 {"threshold": data.get("threshold")},
1176 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001177 return self._format_out("Alarm updated")
1178 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01001179 if isinstance(
1180 e,
1181 (
1182 NbiException,
1183 EngineException,
1184 DbException,
1185 FsException,
1186 MsgException,
1187 AuthException,
1188 ValidationError,
1189 AuthconnException,
1190 ),
1191 ):
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001192 http_code_value = cherrypy.response.status = e.http_code.value
1193 http_code_name = e.http_code.name
1194 cherrypy.log("Exception {}".format(e))
1195 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001196 http_code_value = (
1197 cherrypy.response.status
1198 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001199 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
1200 http_code_name = HTTPStatus.BAD_REQUEST.name
1201 problem_details = {
1202 "code": http_code_name,
1203 "status": http_code_value,
1204 "detail": str(e),
1205 }
1206 return self._format_out(problem_details)
1207
tierno55945e72018-04-06 16:40:27 +02001208 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +01001209 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +00001210 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001211 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +01001212 indata = self._format_in(kwargs)
1213 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001214 raise NbiException(
1215 "Expected application/yaml or application/json Content-Type",
1216 HTTPStatus.BAD_REQUEST,
1217 )
tiernoa5035702019-07-29 08:54:42 +00001218
1219 if method == "GET":
1220 token_info = self.authenticator.authorize()
1221 # for logging
1222 self._format_login(token_info)
1223 if token_id:
1224 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +01001225 else:
tiernoa5035702019-07-29 08:54:42 +00001226 outdata = self.authenticator.get_token_list(token_info)
1227 elif method == "POST":
1228 try:
1229 token_info = self.authenticator.authorize()
1230 except Exception:
1231 token_info = None
1232 if kwargs:
1233 indata.update(kwargs)
1234 # This is needed to log the user when authentication fails
1235 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +01001236 outdata = token_info = self.authenticator.new_token(
1237 token_info, indata, cherrypy.request.remote
1238 )
garciadeblasf2af4a12023-01-24 16:56:54 +01001239 cherrypy.session["Authorization"] = outdata["_id"] # pylint: disable=E1101
tiernoa5035702019-07-29 08:54:42 +00001240 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1241 # for logging
1242 self._format_login(token_info)
selvi.ja9a1fc82022-04-04 06:54:30 +00001243 # password expiry check
1244 if self.authenticator.check_password_expiry(outdata):
garciadeblasf2af4a12023-01-24 16:56:54 +01001245 outdata = {
1246 "id": outdata["id"],
1247 "message": "change_password",
1248 "user_id": outdata["user_id"],
1249 }
tiernoa5035702019-07-29 08:54:42 +00001250 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1251 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
elumalai7802ff82023-04-24 20:38:32 +05301252 cef_event(
1253 cef_logger,
1254 {
1255 "name": "User Login",
1256 "sourceUserName": token_info.get("username"),
1257 "message": "User Logged In, Project={} Outcome=Success".format(
1258 token_info.get("project_name")
1259 ),
1260 },
1261 )
1262 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001263 elif method == "DELETE":
1264 if not token_id and "id" in kwargs:
1265 token_id = kwargs["id"]
1266 elif not token_id:
1267 token_info = self.authenticator.authorize()
1268 # for logging
1269 self._format_login(token_info)
1270 token_id = token_info["_id"]
Rahulc72bc8e2023-12-05 11:54:38 +00001271 if current_backend != "keystone":
1272 token_details = self.engine.db.get_one("tokens", {"_id": token_id})
1273 current_user = token_details.get("username")
1274 current_project = token_details.get("project_name")
1275 else:
1276 current_user = "keystone backend"
1277 current_project = "keystone backend"
tiernoa5035702019-07-29 08:54:42 +00001278 outdata = self.authenticator.del_token(token_id)
1279 token_info = None
garciadeblasf2af4a12023-01-24 16:56:54 +01001280 cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101
elumalai7802ff82023-04-24 20:38:32 +05301281 cef_event(
1282 cef_logger,
1283 {
1284 "name": "User Logout",
1285 "sourceUserName": current_user,
1286 "message": "User Logged Out, Project={} Outcome=Success".format(
1287 current_project
1288 ),
1289 },
1290 )
1291 cherrypy.log("{}".format(cef_logger))
tiernoa5035702019-07-29 08:54:42 +00001292 # cherrypy.response.cookie["Authorization"] = token_id
1293 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1294 else:
garciadeblas4568a372021-03-24 09:19:48 +01001295 raise NbiException(
1296 "Method {} not allowed for token".format(method),
1297 HTTPStatus.METHOD_NOT_ALLOWED,
1298 )
tiernoa5035702019-07-29 08:54:42 +00001299 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001300
1301 @cherrypy.expose
1302 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001303 if not cherrypy.config.get("server.enable_test") or (
1304 isinstance(cherrypy.config["server.enable_test"], str)
1305 and cherrypy.config["server.enable_test"].lower() == "false"
1306 ):
tierno4836bac2020-01-15 14:41:48 +00001307 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1308 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001309 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001310 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001311 return (
1312 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1313 "sleep/<time>\nmessage/topic\n</pre></html>"
1314 )
tiernof27c79b2018-03-12 17:08:42 +01001315
1316 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001317 try:
1318 # self.engine.load_dbase(cherrypy.request.app.config)
garciadeblasf2af4a12023-01-24 16:56:54 +01001319 pid = self.authenticator.create_admin_project()
1320 self.authenticator.create_admin_user(pid)
tiernoc94c3df2018-02-09 15:38:54 +01001321 return "Done. User 'admin', password 'admin' created"
1322 except Exception:
1323 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1324 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001325 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001326 return cherrypy.lib.static.serve_file(
1327 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1328 "text/plain",
1329 "attachment",
1330 )
tiernof27c79b2018-03-12 17:08:42 +01001331 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001332 f_path = (
1333 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1334 )
tiernof27c79b2018-03-12 17:08:42 +01001335 f = open(f_path, "r")
1336 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001337 return f
tierno55945e72018-04-06 16:40:27 +02001338
tiernof27c79b2018-03-12 17:08:42 +01001339 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001340 deleted_info = self.engine.db.del_list(args[1], kwargs)
1341 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1342 elif len(args) and args[0] == "fs-clear":
1343 if len(args) >= 2:
1344 folders = (args[1],)
1345 else:
1346 folders = self.engine.fs.dir_ls(".")
1347 for folder in folders:
1348 self.engine.fs.file_delete(folder)
1349 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001350 elif args and args[0] == "login":
1351 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001352 cherrypy.response.headers[
1353 "WWW-Authenticate"
1354 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001355 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1356 elif args and args[0] == "login2":
1357 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001358 cherrypy.response.headers[
1359 "WWW-Authenticate"
1360 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001361 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1362 elif args and args[0] == "sleep":
1363 sleep_time = 5
1364 try:
1365 sleep_time = int(args[1])
1366 except Exception:
1367 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1368 return self._format_out("Database already initialized")
1369 thread_info = cherrypy.thread_data
1370 print(thread_info)
1371 time.sleep(sleep_time)
1372 # thread_info
1373 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001374 main_topic = args[1]
1375 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001376 try:
garciadeblas4568a372021-03-24 09:19:48 +01001377 if cherrypy.request.method == "POST":
garciadeblas4cd875d2023-02-14 19:05:34 +01001378 to_send = yaml.safe_load(cherrypy.request.body)
tierno55945e72018-04-06 16:40:27 +02001379 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001380 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001381 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001382 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001383 for k, v in kwargs.items():
garciadeblas4cd875d2023-02-14 19:05:34 +01001384 v_dict = yaml.safe_load(v)
tiernof1509b22020-05-12 14:32:37 +00001385 self.engine.msg.write(main_topic, k, v_dict)
1386 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001387 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001388 return_text += "Error: " + str(e)
1389 return_text += "</pre></html>\n"
1390 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001391
1392 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001393 "<html><pre>\nheaders:\n args: {}\n".format(args)
1394 + " kwargs: {}\n".format(kwargs)
1395 + " headers: {}\n".format(cherrypy.request.headers)
1396 + " path_info: {}\n".format(cherrypy.request.path_info)
1397 + " query_string: {}\n".format(cherrypy.request.query_string)
garciadeblasf2af4a12023-01-24 16:56:54 +01001398 + " session: {}\n".format(cherrypy.session) # pylint: disable=E1101
garciadeblas4568a372021-03-24 09:19:48 +01001399 + " cookie: {}\n".format(cherrypy.request.cookie)
1400 + " method: {}\n".format(cherrypy.request.method)
garciadeblasf2af4a12023-01-24 16:56:54 +01001401 + " session: {}\n".format(
1402 cherrypy.session.get("fieldname") # pylint: disable=E1101
1403 )
garciadeblas4568a372021-03-24 09:19:48 +01001404 + " body:\n"
1405 )
tiernoc94c3df2018-02-09 15:38:54 +01001406 return_text += " length: {}\n".format(cherrypy.request.body.length)
1407 if cherrypy.request.body.length:
1408 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001409 str(
1410 cherrypy.request.body.read(
1411 int(cherrypy.request.headers.get("Content-Length", 0))
1412 )
1413 )
1414 )
tiernoc94c3df2018-02-09 15:38:54 +01001415 if thread_info:
1416 return_text += "thread: {}\n".format(thread_info)
1417 return_text += "</pre></html>"
1418 return return_text
1419
tierno701018c2019-06-25 11:13:14 +00001420 @staticmethod
1421 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001422 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001423 raise NbiException(
1424 "URL must contain at least 'main_topic/version/topic'",
1425 HTTPStatus.METHOD_NOT_ALLOWED,
1426 )
tiernof27c79b2018-03-12 17:08:42 +01001427
tierno701018c2019-06-25 11:13:14 +00001428 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001429 for arg in args:
1430 if arg is None:
1431 break
1432 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001433 raise NbiException(
1434 "URL contains unexpected extra items '{}'".format(arg),
1435 HTTPStatus.METHOD_NOT_ALLOWED,
1436 )
tiernof27c79b2018-03-12 17:08:42 +01001437
1438 if arg in reference:
1439 reference = reference[arg]
1440 elif "<ID>" in reference:
1441 reference = reference["<ID>"]
1442 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001443 # if there is content
1444 if reference["*"]:
1445 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001446 break
1447 else:
garciadeblas4568a372021-03-24 09:19:48 +01001448 raise NbiException(
1449 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1450 )
tiernof27c79b2018-03-12 17:08:42 +01001451 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001452 raise NbiException(
1453 "Method {} not supported yet for this URL".format(method),
1454 HTTPStatus.NOT_IMPLEMENTED,
1455 )
tierno2236d202018-05-16 19:05:16 +02001456 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001457 raise NbiException(
1458 "Method {} not supported for this URL".format(method),
1459 HTTPStatus.METHOD_NOT_ALLOWED,
1460 )
tierno701018c2019-06-25 11:13:14 +00001461 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001462
1463 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001464 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001465 """
1466 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001467 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001468 :param version:
tiernob24258a2018-10-04 18:39:49 +02001469 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001470 :param id:
1471 :return: None
1472 """
1473 # Use cherrypy.request.base for absoluted path and make use of request.header HOST just in case behind aNAT
garciadeblas4568a372021-03-24 09:19:48 +01001474 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1475 main_topic, version, topic, id
1476 )
tiernof27c79b2018-03-12 17:08:42 +01001477 return
1478
tierno65ca36d2019-02-12 19:27:52 +01001479 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001480 def _extract_query_string_operations(kwargs, method):
1481 """
1482
1483 :param kwargs:
1484 :return:
1485 """
1486 query_string_operations = []
1487 if kwargs:
1488 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1489 if qs in kwargs and kwargs[qs].lower() != "false":
1490 query_string_operations.append(qs.lower() + ":" + method.lower())
1491 return query_string_operations
1492
1493 @staticmethod
1494 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001495 """
1496 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1497 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001498 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001499 :param kwargs: query string input.
1500 :param method: http method: GET, POSST, PUT, ...
1501 :param _id:
1502 :return: admin_query dictionary with keys:
1503 public: True, False or None
1504 force: True or False
1505 project_id: tuple with projects used for accessing an element
1506 set_project: tuple with projects that a created element will belong to
1507 method: show, list, delete, write
1508 """
garciadeblas4568a372021-03-24 09:19:48 +01001509 admin_query = {
1510 "force": False,
1511 "project_id": (token_info["project_id"],),
1512 "username": token_info["username"],
1513 "admin": token_info["admin"],
1514 "public": None,
1515 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1516 }
tierno65ca36d2019-02-12 19:27:52 +01001517 if kwargs:
1518 # FORCE
1519 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001520 if (
1521 kwargs["FORCE"].lower() != "false"
1522 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001523 admin_query["force"] = True
1524 del kwargs["FORCE"]
1525 # PUBLIC
1526 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001527 if (
1528 kwargs["PUBLIC"].lower() != "false"
1529 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001530 admin_query["public"] = True
1531 else:
1532 admin_query["public"] = False
1533 del kwargs["PUBLIC"]
1534 # ADMIN
1535 if "ADMIN" in kwargs:
1536 behave_as = kwargs.pop("ADMIN")
1537 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001538 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001539 raise NbiException(
1540 "Only admin projects can use 'ADMIN' query string",
1541 HTTPStatus.UNAUTHORIZED,
1542 )
1543 if (
1544 not behave_as or behave_as.lower() == "true"
1545 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001546 admin_query["project_id"] = ()
1547 elif isinstance(behave_as, (list, tuple)):
1548 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001549 else: # isinstance(behave_as, str)
1550 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001551 if "SET_PROJECT" in kwargs:
1552 set_project = kwargs.pop("SET_PROJECT")
1553 if not set_project:
1554 admin_query["set_project"] = list(admin_query["project_id"])
1555 else:
1556 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001557 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001558 if admin_query["project_id"]:
1559 for p in set_project:
1560 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001561 raise NbiException(
1562 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1563 "'ADMIN='{p}'".format(p=p),
1564 HTTPStatus.UNAUTHORIZED,
1565 )
tierno65ca36d2019-02-12 19:27:52 +01001566 admin_query["set_project"] = set_project
1567
1568 # PROJECT_READ
1569 # if "PROJECT_READ" in kwargs:
1570 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001571 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001572 if method == "GET":
1573 if _id:
1574 admin_query["method"] = "show"
1575 else:
1576 admin_query["method"] = "list"
1577 elif method == "DELETE":
1578 admin_query["method"] = "delete"
1579 else:
1580 admin_query["method"] = "write"
1581 return admin_query
1582
tiernoc94c3df2018-02-09 15:38:54 +01001583 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001584 def default(
1585 self,
1586 main_topic=None,
1587 version=None,
1588 topic=None,
1589 _id=None,
1590 item=None,
1591 *args,
1592 **kwargs
1593 ):
tierno701018c2019-06-25 11:13:14 +00001594 token_info = None
selvi.j9919bbc2023-04-26 12:22:13 +00001595 outdata = {}
tiernof27c79b2018-03-12 17:08:42 +01001596 _format = None
tierno0f98af52018-03-19 10:28:22 +01001597 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001598 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001599 rollback = []
tierno701018c2019-06-25 11:13:14 +00001600 engine_session = None
elumalai7802ff82023-04-24 20:38:32 +05301601 url_id = ""
1602 log_mapping = {
1603 "POST": "Creating",
1604 "GET": "Fetching",
1605 "DELETE": "Deleting",
1606 "PUT": "Updating",
1607 "PATCH": "Updating",
1608 }
tiernoc94c3df2018-02-09 15:38:54 +01001609 try:
tiernob24258a2018-10-04 18:39:49 +02001610 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001611 raise NbiException(
1612 "URL must contain at least 'main_topic/version/topic'",
1613 HTTPStatus.METHOD_NOT_ALLOWED,
1614 )
1615 if main_topic not in (
1616 "admin",
1617 "vnfpkgm",
1618 "nsd",
1619 "nslcm",
1620 "pdu",
1621 "nst",
1622 "nsilcm",
1623 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001624 "vnflcm",
rshri2d386cb2024-07-05 14:35:51 +00001625 "k8scluster",
yshah53cc9eb2024-07-05 13:06:31 +00001626 "ksu",
1627 "oka",
garciadeblas4568a372021-03-24 09:19:48 +01001628 ):
1629 raise NbiException(
1630 "URL main_topic '{}' not supported".format(main_topic),
1631 HTTPStatus.METHOD_NOT_ALLOWED,
1632 )
1633 if version != "v1":
1634 raise NbiException(
1635 "URL version '{}' not supported".format(version),
1636 HTTPStatus.METHOD_NOT_ALLOWED,
1637 )
elumalai7802ff82023-04-24 20:38:32 +05301638 if _id is not None:
1639 url_id = _id
tiernoc94c3df2018-02-09 15:38:54 +01001640
garciadeblas4568a372021-03-24 09:19:48 +01001641 if (
1642 kwargs
1643 and "METHOD" in kwargs
1644 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1645 ):
tiernof27c79b2018-03-12 17:08:42 +01001646 method = kwargs.pop("METHOD")
1647 else:
1648 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001649
garciadeblas4568a372021-03-24 09:19:48 +01001650 role_permission = self._check_valid_url_method(
1651 method, main_topic, version, topic, _id, item, *args
1652 )
1653 query_string_operations = self._extract_query_string_operations(
1654 kwargs, method
1655 )
tiernob24258a2018-10-04 18:39:49 +02001656 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001657 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001658 token_info = self.authenticator.authorize(
1659 role_permission, query_string_operations, _id
1660 )
tierno12eac3c2020-03-19 23:22:08 +00001661 if main_topic == "admin" and topic == "domains":
1662 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001663 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001664 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001665 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301666
vijay.r35ef2f72019-04-30 17:55:49 +05301667 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001668 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001669
tiernob24258a2018-10-04 18:39:49 +02001670 if main_topic == "nsd":
1671 engine_topic = "nsds"
kayal2001f71c2e82024-06-25 15:26:24 +05301672 if topic == "ns_config_template":
1673 engine_topic = "nsconfigtemps"
tiernob24258a2018-10-04 18:39:49 +02001674 elif main_topic == "vnfpkgm":
1675 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001676 if topic == "vnfpkg_op_occs":
1677 engine_topic = "vnfpkgops"
1678 if topic == "vnf_packages" and item == "action":
1679 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001680 elif main_topic == "nslcm":
1681 engine_topic = "nsrs"
1682 if topic == "ns_lcm_op_occs":
1683 engine_topic = "nslcmops"
1684 if topic == "vnfrs" or topic == "vnf_instances":
1685 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001686 elif main_topic == "vnflcm":
1687 if topic == "vnf_lcm_op_occs":
1688 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001689 elif main_topic == "nst":
1690 engine_topic = "nsts"
1691 elif main_topic == "nsilcm":
1692 engine_topic = "nsis"
1693 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001694 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001695 elif main_topic == "pdu":
1696 engine_topic = "pdus"
rshri2d386cb2024-07-05 14:35:51 +00001697 elif main_topic == "k8scluster":
1698 engine_topic = "k8s"
1699 if topic == "clusters" and _id == "register" or item == "deregister":
1700 engine_topic = "k8sops"
1701 elif topic == "infra_controller_profiles":
1702 engine_topic = "infras_cont"
1703 elif topic == "infra_config_profiles":
1704 engine_topic = "infras_conf"
1705 elif topic == "resource_profiles":
1706 engine_topic = "resources"
1707 elif topic == "app_profiles":
1708 engine_topic = "apps"
yshah53cc9eb2024-07-05 13:06:31 +00001709 elif main_topic == "k8scluster" and item in (
1710 "upgrade",
1711 "get_creds",
1712 "scale",
1713 ):
1714 engine_topic = "k8s"
1715 elif main_topic == "ksu" and engine_topic in ("ksus", "clone", "move"):
1716 engine_topic = "ksus"
garciadeblas4568a372021-03-24 09:19:48 +01001717 if (
1718 engine_topic == "vims"
1719 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001720 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001721
preethika.p329b8182020-04-22 12:25:39 +05301722 if topic == "subscriptions":
1723 engine_topic = main_topic + "_" + topic
1724
tiernoc94c3df2018-02-09 15:38:54 +01001725 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001726 if item in (
1727 "nsd_content",
1728 "package_content",
1729 "artifacts",
1730 "vnfd",
1731 "nsd",
1732 "nst",
1733 "nst_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301734 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001735 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001736 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001737 path = "$DESCRIPTOR"
1738 elif args:
1739 path = args
tiernob24258a2018-10-04 18:39:49 +02001740 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001741 path = ()
1742 else:
1743 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001744 file, _format = self.engine.get_file(
1745 engine_session,
1746 engine_topic,
1747 _id,
1748 path,
1749 cherrypy.request.headers.get("Accept"),
1750 )
tiernof27c79b2018-03-12 17:08:42 +01001751 outdata = file
1752 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001753 outdata = self.engine.get_item_list(
1754 engine_session, engine_topic, kwargs, api_req=True
1755 )
rshri2d386cb2024-07-05 14:35:51 +00001756 elif topic == "clusters" and item in (
1757 "infra_controller_profiles",
1758 "infra_config_profiles",
1759 "app_profiles",
1760 "resource_profiles",
1761 ):
1762 profile = item
1763 filter_q = None
1764 outdata = self.engine.get_one_item(
1765 engine_session,
1766 engine_topic,
1767 _id,
1768 profile,
1769 filter_q,
1770 api_req=True,
1771 )
yshah53cc9eb2024-07-05 13:06:31 +00001772 elif topic == "clusters" and item == "get_creds":
1773 outdata = self.engine.get_cluster_info(
1774 engine_session, engine_topic, _id, item
1775 )
tiernoc94c3df2018-02-09 15:38:54 +01001776 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301777 if item == "reports":
1778 # TODO check that project_id (_id in this context) has permissions
1779 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301780 filter_q = None
1781 if "vcaStatusRefresh" in kwargs:
1782 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
garciadeblasf2af4a12023-01-24 16:56:54 +01001783 outdata = self.engine.get_item(
1784 engine_session, engine_topic, _id, filter_q, True
1785 )
delacruzramo271d2002019-12-02 21:00:37 +01001786
tiernof27c79b2018-03-12 17:08:42 +01001787 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001788 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001789 if topic in (
1790 "ns_descriptors_content",
1791 "vnf_packages_content",
1792 "netslice_templates_content",
kayal2001f71c2e82024-06-25 15:26:24 +05301793 "ns_config_template",
garciadeblas4568a372021-03-24 09:19:48 +01001794 ):
tiernof27c79b2018-03-12 17:08:42 +01001795 _id = cherrypy.request.headers.get("Transaction-Id")
yshah53cc9eb2024-07-05 13:06:31 +00001796
tiernof27c79b2018-03-12 17:08:42 +01001797 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001798 _id, _ = self.engine.new_item(
1799 rollback,
1800 engine_session,
1801 engine_topic,
1802 {},
1803 None,
1804 cherrypy.request.headers,
1805 )
1806 completed = self.engine.upload_content(
1807 engine_session,
1808 engine_topic,
1809 _id,
1810 indata,
1811 kwargs,
1812 cherrypy.request.headers,
1813 )
tiernof27c79b2018-03-12 17:08:42 +01001814 if completed:
tiernob24258a2018-10-04 18:39:49 +02001815 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001816 else:
1817 cherrypy.response.headers["Transaction-Id"] = _id
1818 outdata = {"id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001819 elif topic == "oka_packages":
1820 _id = cherrypy.request.headers.get("Transaction-Id")
1821
1822 if not _id:
1823 _id, _ = self.engine.new_item(
1824 rollback,
1825 engine_session,
1826 engine_topic,
1827 {},
1828 kwargs,
1829 cherrypy.request.headers,
1830 )
1831 cherrypy.request.headers["method"] = cherrypy.request.method
1832 if indata:
1833 completed = self.engine.upload_content(
1834 engine_session,
1835 engine_topic,
1836 _id,
1837 indata,
1838 None,
1839 cherrypy.request.headers,
1840 )
1841 if completed:
1842 self._set_location_header(main_topic, version, topic, _id)
1843 else:
1844 cherrypy.response.headers["Transaction-Id"] = _id
1845 outdata = {"_id": _id}
tiernob24258a2018-10-04 18:39:49 +02001846 elif topic == "ns_instances_content":
1847 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001848 _id, _ = self.engine.new_item(
1849 rollback, engine_session, engine_topic, indata, kwargs
1850 )
tiernob24258a2018-10-04 18:39:49 +02001851 # creates nslcmop
1852 indata["lcmOperationType"] = "instantiate"
1853 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001854 nslcmop_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001855 rollback, engine_session, "nslcmops", indata, None
1856 )
tiernob24258a2018-10-04 18:39:49 +02001857 self._set_location_header(main_topic, version, topic, _id)
garciadeblasf53612b2024-07-12 14:44:37 +02001858 outdata = {"id": _id, "nslcmop_id": nslcmop_id, "nsName": nsName}
adurti3af50952024-05-31 11:36:57 +05301859 elif topic == "ns_instances_terminate":
1860 if indata.get("ns_ids"):
1861 for ns_id in indata.get("ns_ids"):
1862 nslcmop_desc = {
1863 "lcmOperationType": "terminate",
1864 "nsInstanceId": ns_id,
1865 "autoremove": indata.get("autoremove")
1866 if "autoremove" in indata
1867 else True,
1868 }
1869 op_id, _, _ = self.engine.new_item(
1870 rollback,
1871 engine_session,
1872 "nslcmops",
1873 nslcmop_desc,
1874 kwargs,
1875 )
1876 if not op_id:
1877 _ = self.engine.del_item(
1878 engine_session, engine_topic, ns_id
1879 )
1880 outdata = {"ns_ids": indata.get("ns_ids")}
1881 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001882 elif topic == "ns_instances" and item:
1883 indata["lcmOperationType"] = item
1884 indata["nsInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001885 _id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01001886 rollback, engine_session, "nslcmops", indata, kwargs
1887 )
1888 self._set_location_header(
1889 main_topic, version, "ns_lcm_op_occs", _id
1890 )
garciadeblasf53612b2024-07-12 14:44:37 +02001891 outdata = {"id": _id, "nsName": nsName}
tierno65acb4d2018-04-06 16:42:40 +02001892 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001893 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001894 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001895 _id, _ = self.engine.new_item(
1896 rollback, engine_session, engine_topic, indata, kwargs
1897 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001898 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001899 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001900 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001901 nsilcmop_id, _ = self.engine.new_item(
1902 rollback, engine_session, "nsilcmops", indata, kwargs
1903 )
kuuse078f55e2019-05-16 19:24:21 +02001904 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001905 elif topic == "netslice_instances" and item:
1906 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001907 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001908 _id, _ = self.engine.new_item(
1909 rollback, engine_session, "nsilcmops", indata, kwargs
1910 )
1911 self._set_location_header(
1912 main_topic, version, "nsi_lcm_op_occs", _id
1913 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001914 outdata = {"id": _id}
1915 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001916 elif topic == "vnf_packages" and item == "action":
1917 indata["lcmOperationType"] = item
1918 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001919 _id, _ = self.engine.new_item(
1920 rollback, engine_session, "vnfpkgops", indata, kwargs
1921 )
1922 self._set_location_header(
1923 main_topic, version, "vnfpkg_op_occs", _id
1924 )
delacruzramo271d2002019-12-02 21:00:37 +01001925 outdata = {"id": _id}
1926 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301927 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001928 _id, _ = self.engine.new_item(
1929 rollback, engine_session, engine_topic, indata, kwargs
1930 )
preethika.p329b8182020-04-22 12:25:39 +05301931 self._set_location_header(main_topic, version, topic, _id)
1932 link = {}
1933 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001934 outdata = {
1935 "id": _id,
1936 "filter": indata["filter"],
1937 "callbackUri": indata["CallbackUri"],
1938 "_links": link,
1939 }
preethika.p329b8182020-04-22 12:25:39 +05301940 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001941 elif topic == "vnf_instances" and item:
1942 indata["lcmOperationType"] = item
1943 indata["vnfInstanceId"] = _id
garciadeblasf53612b2024-07-12 14:44:37 +02001944 _id, nsName, _ = self.engine.new_item(
garciadeblasf2af4a12023-01-24 16:56:54 +01001945 rollback, engine_session, "vnflcmops", indata, kwargs
1946 )
1947 self._set_location_header(
1948 main_topic, version, "vnf_lcm_op_occs", _id
1949 )
garciadeblasf53612b2024-07-12 14:44:37 +02001950 outdata = {"id": _id, "nsName": nsName}
almagiae47b9132022-05-17 14:12:22 +02001951 cherrypy.response.status = HTTPStatus.ACCEPTED.value
Gabriel Cuba84a60df2023-10-30 14:01:54 -05001952 elif topic == "ns_lcm_op_occs" and item == "cancel":
1953 indata["nsLcmOpOccId"] = _id
1954 self.engine.cancel_item(
1955 rollback, engine_session, "nslcmops", indata, None
1956 )
1957 self._set_location_header(main_topic, version, topic, _id)
1958 cherrypy.response.status = HTTPStatus.ACCEPTED.value
rshri2d386cb2024-07-05 14:35:51 +00001959 elif topic == "clusters" and _id == "register":
1960 # To register a cluster
1961 _id, _ = self.engine.add_item(
1962 rollback, engine_session, engine_topic, indata, kwargs
1963 )
1964 self._set_location_header(main_topic, version, topic, _id)
1965 outdata = {"id": _id}
1966 elif (
1967 topic
1968 in (
1969 "clusters",
1970 "infra_controller_profiles",
1971 "infra_config_profiles",
1972 "app_profiles",
1973 "resource_profiles",
1974 )
1975 and item is None
1976 ):
1977 # creates cluster, infra_controller_profiles, app_profiles, infra_config_profiles, and resource_profiles
1978 _id, _ = self.engine.new_item(
1979 rollback, engine_session, engine_topic, indata, kwargs
1980 )
1981 self._set_location_header(main_topic, version, topic, _id)
1982 outdata = {"_id": _id}
yshah53cc9eb2024-07-05 13:06:31 +00001983 elif topic == "ksus" and item:
1984 if item == "clone":
1985 _id = self.engine.clone(
1986 rollback,
1987 engine_session,
1988 engine_topic,
1989 _id,
1990 indata,
1991 kwargs,
1992 cherrypy.request.headers,
1993 )
1994 self._set_location_header(main_topic, version, topic, _id)
1995 outdata = {"id": _id}
1996 if item == "move":
1997 op_id = self.engine.move_ksu(
1998 engine_session, engine_topic, _id, indata, kwargs
1999 )
2000 outdata = {"op_id": op_id}
2001 elif topic == "ksus" and _id == "delete":
2002 op_id = self.engine.delete_ksu(
2003 engine_session, engine_topic, _id, indata
2004 )
2005 outdata = {"op_id": op_id}
2006 elif topic == "ksus" and _id == "update":
2007 op_id = self.engine.edit_item(
2008 engine_session, engine_topic, _id, indata, kwargs
2009 )
2010 outdata = {"op_id": op_id}
2011 elif topic == "clusters" and item in ("upgrade", "scale"):
2012 op_id = self.engine.update_cluster(
2013 engine_session, engine_topic, _id, item, indata
2014 )
2015 outdata = {"op_id": op_id}
tiernof27c79b2018-03-12 17:08:42 +01002016 else:
garciadeblas4568a372021-03-24 09:19:48 +01002017 _id, op_id = self.engine.new_item(
2018 rollback,
2019 engine_session,
2020 engine_topic,
2021 indata,
2022 kwargs,
2023 cherrypy.request.headers,
2024 )
tiernob24258a2018-10-04 18:39:49 +02002025 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01002026 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00002027 if op_id:
2028 outdata["op_id"] = op_id
2029 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02002030 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02002031
tiernoc94c3df2018-02-09 15:38:54 +01002032 elif method == "DELETE":
2033 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01002034 outdata = self.engine.del_item_list(
2035 engine_session, engine_topic, kwargs
2036 )
tierno09c073e2018-04-26 13:36:48 +02002037 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01002038 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00002039 # for NS NSI generate an operation
2040 op_id = None
tierno701018c2019-06-25 11:13:14 +00002041 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02002042 nslcmop_desc = {
2043 "lcmOperationType": "terminate",
2044 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002045 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02002046 }
garciadeblasf53612b2024-07-12 14:44:37 +02002047 op_id, nsName, _ = self.engine.new_item(
garciadeblas4568a372021-03-24 09:19:48 +01002048 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
2049 )
tierno22577432020-04-08 15:16:57 +00002050 if op_id:
garciadeblasf53612b2024-07-12 14:44:37 +02002051 outdata = {"_id": op_id, "nsName": nsName}
garciadeblas4568a372021-03-24 09:19:48 +01002052 elif (
2053 topic == "netslice_instances_content"
2054 and not engine_session["force"]
2055 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02002056 nsilcmop_desc = {
2057 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02002058 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01002059 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02002060 }
garciadeblas4568a372021-03-24 09:19:48 +01002061 op_id, _ = self.engine.new_item(
2062 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
2063 )
tierno22577432020-04-08 15:16:57 +00002064 if op_id:
2065 outdata = {"_id": op_id}
rshri2d386cb2024-07-05 14:35:51 +00002066 elif topic == "clusters" and item == "deregister":
2067 if not op_id:
2068 op_id = self.engine.remove(
2069 engine_session, engine_topic, _id
2070 )
2071 if op_id:
2072 outdata = {"_id": op_id}
2073 cherrypy.response.status = (
2074 HTTPStatus.ACCEPTED.value
2075 if op_id
2076 else HTTPStatus.NO_CONTENT.value
2077 )
yshah53cc9eb2024-07-05 13:06:31 +00002078 elif topic == "ksus":
2079 op_id = self.engine.delete_ksu(
2080 engine_session, engine_topic, _id, indata
2081 )
2082 outdata = {"op_id": op_id}
tierno22577432020-04-08 15:16:57 +00002083 # if there is not any deletion in process, delete
rshri2d386cb2024-07-05 14:35:51 +00002084 elif not op_id:
tierno22577432020-04-08 15:16:57 +00002085 op_id = self.engine.del_item(engine_session, engine_topic, _id)
2086 if op_id:
rshri2d386cb2024-07-05 14:35:51 +00002087 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01002088 cherrypy.response.status = (
2089 HTTPStatus.ACCEPTED.value
2090 if op_id
2091 else HTTPStatus.NO_CONTENT.value
2092 )
tierno09c073e2018-04-26 13:36:48 +02002093
tierno7ae10112018-05-18 14:36:02 +02002094 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00002095 op_id = None
tierno701018c2019-06-25 11:13:14 +00002096 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01002097 raise NbiException(
2098 "Nothing to update. Provide payload and/or query string",
2099 HTTPStatus.BAD_REQUEST,
2100 )
2101 if (
kayal2001f71c2e82024-06-25 15:26:24 +05302102 item
2103 in (
2104 "nsd_content",
2105 "package_content",
2106 "nst_content",
2107 "template_content",
2108 )
garciadeblas4568a372021-03-24 09:19:48 +01002109 and method == "PUT"
2110 ):
2111 completed = self.engine.upload_content(
2112 engine_session,
2113 engine_topic,
2114 _id,
2115 indata,
2116 kwargs,
2117 cherrypy.request.headers,
2118 )
tiernof27c79b2018-03-12 17:08:42 +01002119 if not completed:
2120 cherrypy.response.headers["Transaction-Id"] = id
rshri2d386cb2024-07-05 14:35:51 +00002121 elif item in (
2122 "app_profiles",
2123 "resource_profiles",
2124 "infra_controller_profiles",
2125 "infra_config_profiles",
2126 ):
2127 op_id = self.engine.edit(
2128 engine_session, engine_topic, _id, item, indata, kwargs
2129 )
yshah53cc9eb2024-07-05 13:06:31 +00002130 elif topic == "oka_packages" and method == "PATCH":
2131 if kwargs:
2132 op_id = self.engine.edit_item(
2133 engine_session, engine_topic, _id, None, kwargs
2134 )
2135 if indata:
2136 if indata.get("name") or indata.get("description"):
2137 op_id = self.engine.edit_item(
2138 engine_session, engine_topic, _id, indata, kwargs
2139 )
2140 else:
2141 cherrypy.request.headers["method"] = cherrypy.request.method
2142 completed = self.engine.upload_content(
2143 engine_session,
2144 engine_topic,
2145 _id,
2146 indata,
2147 {},
2148 cherrypy.request.headers,
2149 )
2150 if not completed:
2151 cherrypy.response.headers["Transaction-Id"] = id
2152 elif topic == "oka_packages" and method == "PUT":
2153 if indata:
2154 cherrypy.request.headers["method"] = cherrypy.request.method
2155 completed = self.engine.upload_content(
2156 engine_session,
2157 engine_topic,
2158 _id,
2159 indata,
2160 {},
2161 cherrypy.request.headers,
2162 )
2163 if not completed:
2164 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01002165 else:
garciadeblas4568a372021-03-24 09:19:48 +01002166 op_id = self.engine.edit_item(
2167 engine_session, engine_topic, _id, indata, kwargs
2168 )
tiernobdebce92019-07-01 15:36:49 +00002169
2170 if op_id:
2171 cherrypy.response.status = HTTPStatus.ACCEPTED.value
2172 outdata = {"op_id": op_id}
2173 else:
2174 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
2175 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01002176 else:
garciadeblas4568a372021-03-24 09:19:48 +01002177 raise NbiException(
2178 "Method {} not allowed".format(method),
2179 HTTPStatus.METHOD_NOT_ALLOWED,
2180 )
tiernoa6bb45d2019-06-14 09:45:39 +00002181
2182 # if Role information changes, it is needed to reload the information of roles
2183 if topic == "roles" and method != "GET":
2184 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01002185
garciadeblas4568a372021-03-24 09:19:48 +01002186 if (
2187 topic == "projects"
2188 and method == "DELETE"
2189 or topic in ["users", "roles"]
2190 and method in ["PUT", "PATCH", "DELETE"]
2191 ):
delacruzramoad682a52019-12-10 16:26:34 +01002192 self.authenticator.remove_token_from_cache()
2193
garciadeblasf53612b2024-07-12 14:44:37 +02002194 cef_event(
2195 cef_logger,
2196 {
2197 "name": "User Operation",
2198 "sourceUserName": token_info.get("username"),
2199 },
2200 )
2201 if topic == "ns_instances_content" and url_id:
2202 nsName = (
2203 outdata.get("name") if method == "GET" else outdata.get("nsName")
2204 )
elumalai7802ff82023-04-24 20:38:32 +05302205 cef_event(
2206 cef_logger,
2207 {
garciadeblasf53612b2024-07-12 14:44:37 +02002208 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2209 log_mapping[method],
2210 topic,
2211 nsName,
2212 outdata.get("id"),
2213 token_info.get("project_name"),
2214 ),
2215 },
2216 )
2217 cherrypy.log("{}".format(cef_logger))
2218 elif topic == "ns_instances_content" and method == "POST":
2219 cef_event(
2220 cef_logger,
2221 {
2222 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2223 log_mapping[method],
2224 topic,
2225 outdata.get("nsName"),
2226 outdata.get("id"),
2227 token_info.get("project_name"),
2228 ),
2229 },
2230 )
2231 cherrypy.log("{}".format(cef_logger))
2232 elif topic in ("ns_instances", "vnf_instances") and item:
2233 cef_event(
2234 cef_logger,
2235 {
2236 "message": "{} {}, nsName={}, nsdId={}, Project={} Outcome=Success".format(
2237 log_mapping[method],
2238 topic,
2239 outdata.get("nsName"),
2240 url_id,
2241 token_info.get("project_name"),
2242 ),
2243 },
2244 )
2245 cherrypy.log("{}".format(cef_logger))
2246 elif item is not None:
2247 cef_event(
2248 cef_logger,
2249 {
elumalai7802ff82023-04-24 20:38:32 +05302250 "message": "Performing {} operation on {} {}, Project={} Outcome=Success".format(
2251 item,
2252 topic,
2253 url_id,
2254 token_info.get("project_name"),
2255 ),
2256 },
2257 )
2258 cherrypy.log("{}".format(cef_logger))
2259 else:
2260 cef_event(
2261 cef_logger,
2262 {
elumalai7802ff82023-04-24 20:38:32 +05302263 "message": "{} {} {}, Project={} Outcome=Success".format(
2264 log_mapping[method],
2265 topic,
2266 url_id,
2267 token_info.get("project_name"),
2268 ),
2269 },
2270 )
2271 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002272 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02002273 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01002274 if isinstance(
2275 e,
2276 (
2277 NbiException,
2278 EngineException,
2279 DbException,
2280 FsException,
2281 MsgException,
2282 AuthException,
2283 ValidationError,
2284 AuthconnException,
2285 ),
2286 ):
tiernob24258a2018-10-04 18:39:49 +02002287 http_code_value = cherrypy.response.status = e.http_code.value
2288 http_code_name = e.http_code.name
2289 cherrypy.log("Exception {}".format(e))
2290 else:
garciadeblas4568a372021-03-24 09:19:48 +01002291 http_code_value = (
2292 cherrypy.response.status
2293 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01002294 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02002295 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02002296 if hasattr(outdata, "close"): # is an open file
2297 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02002298 error_text = str(e)
2299 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02002300 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02002301 try:
tiernocc103432018-10-19 14:10:35 +02002302 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01002303 self.engine.db.set_one(
2304 rollback_item["topic"],
2305 {"_id": rollback_item["_id"]},
2306 rollback_item["content"],
2307 fail_on_empty=False,
2308 )
preethika.p329b8182020-04-22 12:25:39 +05302309 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01002310 self.engine.db.del_list(
2311 rollback_item["topic"],
2312 rollback_item["filter"],
garciadeblas4568a372021-03-24 09:19:48 +01002313 )
tiernocc103432018-10-19 14:10:35 +02002314 else:
garciadeblas4568a372021-03-24 09:19:48 +01002315 self.engine.db.del_one(
2316 rollback_item["topic"],
2317 {"_id": rollback_item["_id"]},
2318 fail_on_empty=False,
2319 )
tierno3ace63c2018-05-03 17:51:43 +02002320 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01002321 rollback_error_text = "Rollback Exception {}: {}".format(
2322 rollback_item, e2
2323 )
tiernob24258a2018-10-04 18:39:49 +02002324 cherrypy.log(rollback_error_text)
2325 error_text += ". " + rollback_error_text
2326 # if isinstance(e, MsgException):
2327 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
2328 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01002329 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02002330 "code": http_code_name,
2331 "status": http_code_value,
2332 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01002333 }
elumalai7802ff82023-04-24 20:38:32 +05302334 if item is not None and token_info is not None:
2335 cef_event(
2336 cef_logger,
2337 {
2338 "name": "User Operation",
2339 "sourceUserName": token_info.get("username", None),
2340 "message": "Performing {} operation on {} {}, Project={} Outcome=Failure".format(
2341 item,
2342 topic,
2343 url_id,
2344 token_info.get("project_name", None),
2345 ),
2346 "severity": "2",
2347 },
2348 )
2349 cherrypy.log("{}".format(cef_logger))
2350 elif token_info is not None:
2351 cef_event(
2352 cef_logger,
2353 {
2354 "name": "User Operation",
2355 "sourceUserName": token_info.get("username", None),
2356 "message": "{} {} {}, Project={} Outcome=Failure".format(
2357 item,
2358 topic,
2359 url_id,
2360 token_info.get("project_name", None),
2361 ),
2362 "severity": "2",
2363 },
2364 )
2365 cherrypy.log("{}".format(cef_logger))
tierno701018c2019-06-25 11:13:14 +00002366 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01002367 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00002368 finally:
2369 if token_info:
2370 self._format_login(token_info)
2371 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
2372 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
2373 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01002374 cherrypy.request.login += ";{}={}".format(
2375 logging_id, outdata[logging_id][:36]
2376 )
tiernoc94c3df2018-02-09 15:38:54 +01002377
2378
tiernoc94c3df2018-02-09 15:38:54 +01002379def _start_service():
2380 """
2381 Callback function called when cherrypy.engine starts
2382 Override configuration with env variables
2383 Set database, storage, message configuration
2384 Init database with admin/admin user password
2385 """
tierno932499c2019-01-28 17:28:10 +00002386 global nbi_server
2387 global subscription_thread
elumalai7802ff82023-04-24 20:38:32 +05302388 global cef_logger
Rahulc72bc8e2023-12-05 11:54:38 +00002389 global current_backend
tiernoc94c3df2018-02-09 15:38:54 +01002390 cherrypy.log.error("Starting osm_nbi")
2391 # update general cherrypy configuration
2392 update_dict = {}
2393
garciadeblas4568a372021-03-24 09:19:48 +01002394 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01002395 for k, v in environ.items():
garciadeblas6d83f8f2023-06-19 22:34:49 +02002396 if k == "OSMNBI_USER_MANAGEMENT":
2397 feature_state = eval(v.title())
2398 engine_config["authentication"]["user_management"] = feature_state
Adurtid68526d2023-11-14 11:14:53 +00002399 elif k == "OSMNBI_PWD_EXPIRE_DAYS":
2400 pwd_expire_days = int(v)
2401 engine_config["authentication"]["pwd_expire_days"] = pwd_expire_days
2402 elif k == "OSMNBI_MAX_PWD_ATTEMPT":
2403 max_pwd_attempt = int(v)
2404 engine_config["authentication"]["max_pwd_attempt"] = max_pwd_attempt
2405 elif k == "OSMNBI_ACCOUNT_EXPIRE_DAYS":
2406 account_expire_days = int(v)
2407 engine_config["authentication"]["account_expire_days"] = account_expire_days
tiernoc94c3df2018-02-09 15:38:54 +01002408 if not k.startswith("OSMNBI_"):
2409 continue
tiernoe1281182018-05-22 12:24:36 +02002410 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01002411 if not k2:
2412 continue
2413 try:
2414 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01002415 if k == "OSMNBI_STATIC_DIR":
2416 engine_config["/static"]["tools.staticdir.dir"] = v
2417 engine_config["/static"]["tools.staticdir.on"] = True
2418 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
2419 update_dict["server.socket_port"] = int(v)
2420 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
2421 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02002422 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01002423 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002424 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02002425 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002426 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01002427 engine_config[k1][k2] = int(v)
2428 else:
2429 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01002430
tiernoc94c3df2018-02-09 15:38:54 +01002431 except ValueError as e:
2432 cherrypy.log.error("Ignoring environ '{}': " + str(e))
2433 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01002434 cherrypy.log(
2435 "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
2436 )
tiernoc94c3df2018-02-09 15:38:54 +01002437
2438 if update_dict:
2439 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02002440 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01002441
2442 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01002443 log_format_simple = (
2444 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
2445 )
2446 log_formatter_simple = logging.Formatter(
2447 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
2448 )
tiernoc94c3df2018-02-09 15:38:54 +01002449 logger_server = logging.getLogger("cherrypy.error")
2450 logger_access = logging.getLogger("cherrypy.access")
2451 logger_cherry = logging.getLogger("cherrypy")
2452 logger_nbi = logging.getLogger("nbi")
2453
tiernof5298be2018-05-16 14:43:57 +02002454 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01002455 file_handler = logging.handlers.RotatingFileHandler(
2456 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
2457 )
tiernoc94c3df2018-02-09 15:38:54 +01002458 file_handler.setFormatter(log_formatter_simple)
2459 logger_cherry.addHandler(file_handler)
2460 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02002461 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01002462 for format_, logger in {
2463 "nbi.server %(filename)s:%(lineno)s": logger_server,
2464 "nbi.access %(filename)s:%(lineno)s": logger_access,
2465 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
2466 }.items():
tiernob24258a2018-10-04 18:39:49 +02002467 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01002468 log_formatter_cherry = logging.Formatter(
2469 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
2470 )
tiernob24258a2018-10-04 18:39:49 +02002471 str_handler = logging.StreamHandler()
2472 str_handler.setFormatter(log_formatter_cherry)
2473 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01002474
tiernof5298be2018-05-16 14:43:57 +02002475 if engine_config["global"].get("log.level"):
2476 logger_cherry.setLevel(engine_config["global"]["log.level"])
2477 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01002478
2479 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01002480 for k1, logname in {
2481 "message": "nbi.msg",
2482 "database": "nbi.db",
2483 "storage": "nbi.fs",
2484 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01002485 engine_config[k1]["logger_name"] = logname
2486 logger_module = logging.getLogger(logname)
2487 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01002488 file_handler = logging.handlers.RotatingFileHandler(
2489 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
2490 )
tiernoc94c3df2018-02-09 15:38:54 +01002491 file_handler.setFormatter(log_formatter_simple)
2492 logger_module.addHandler(file_handler)
2493 if "loglevel" in engine_config[k1]:
2494 logger_module.setLevel(engine_config[k1]["loglevel"])
2495 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01002496 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
2497 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
2498 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
2499 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
2500 target_version=auth_database_version
2501 )
tiernobee508e2019-01-21 11:21:49 +00002502
elumalai7802ff82023-04-24 20:38:32 +05302503 cef_logger = cef_event_builder(engine_config["authentication"])
2504
tierno932499c2019-01-28 17:28:10 +00002505 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01002506 subscription_thread = SubscriptionThread(
2507 config=engine_config, engine=nbi_server.engine
2508 )
tierno932499c2019-01-28 17:28:10 +00002509 subscription_thread.start()
2510 # Do not capture except SubscriptionException
2511
tiernob2e48bd2020-02-04 15:47:18 +00002512 backend = engine_config["authentication"]["backend"]
Rahulc72bc8e2023-12-05 11:54:38 +00002513 current_backend = backend
garciadeblas4568a372021-03-24 09:19:48 +01002514 cherrypy.log.error(
2515 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
2516 nbi_version, nbi_version_date, backend
2517 )
2518 )
tiernoc94c3df2018-02-09 15:38:54 +01002519
2520
2521def _stop_service():
2522 """
2523 Callback function called when cherrypy.engine stops
2524 TODO: Ending database connections.
2525 """
tierno932499c2019-01-28 17:28:10 +00002526 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01002527 if subscription_thread:
2528 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00002529 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01002530 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01002531 cherrypy.log.error("Stopping osm_nbi")
2532
tierno2236d202018-05-16 19:05:16 +02002533
tiernof5298be2018-05-16 14:43:57 +02002534def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00002535 global nbi_server
tiernoc94c3df2018-02-09 15:38:54 +01002536 # conf = {
2537 # '/': {
2538 # #'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
2539 # 'tools.sessions.on': True,
2540 # 'tools.response_headers.on': True,
2541 # # 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
2542 # }
2543 # }
2544 # cherrypy.Server.ssl_module = 'builtin'
2545 # cherrypy.Server.ssl_certificate = "http/cert.pem"
2546 # cherrypy.Server.ssl_private_key = "http/privkey.pem"
2547 # cherrypy.Server.thread_pool = 10
2548 # cherrypy.config.update({'Server.socket_port': config["port"], 'Server.socket_host': config["host"]})
2549
2550 # cherrypy.config.update({'tools.auth_basic.on': True,
2551 # 'tools.auth_basic.realm': 'localhost',
2552 # 'tools.auth_basic.checkpassword': validate_password})
tierno932499c2019-01-28 17:28:10 +00002553 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01002554 cherrypy.engine.subscribe("start", _start_service)
2555 cherrypy.engine.subscribe("stop", _stop_service)
2556 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02002557
2558
2559def usage():
garciadeblas4568a372021-03-24 09:19:48 +01002560 print(
2561 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02002562 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
2563 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01002564 """.format(
2565 sys.argv[0]
2566 )
2567 )
tierno2236d202018-05-16 19:05:16 +02002568 # --log-socket-host HOST: send logs to this host")
2569 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002570
2571
garciadeblas4568a372021-03-24 09:19:48 +01002572if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002573 try:
2574 # load parameters and configuration
2575 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2576 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2577 config_file = None
2578 for o, a in opts:
2579 if o in ("-h", "--help"):
2580 usage()
2581 sys.exit()
2582 elif o in ("-c", "--config"):
2583 config_file = a
2584 # elif o == "--log-socket-port":
2585 # log_socket_port = a
2586 # elif o == "--log-socket-host":
2587 # log_socket_host = a
2588 # elif o == "--log-file":
2589 # log_file = a
2590 else:
2591 assert False, "Unhandled option"
2592 if config_file:
2593 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002594 print(
2595 "configuration file '{}' that not exist".format(config_file),
2596 file=sys.stderr,
2597 )
tiernof5298be2018-05-16 14:43:57 +02002598 exit(1)
2599 else:
garciadeblas4568a372021-03-24 09:19:48 +01002600 for config_file in (
2601 __file__[: __file__.rfind(".")] + ".cfg",
2602 "./nbi.cfg",
2603 "/etc/osm/nbi.cfg",
2604 ):
tiernof5298be2018-05-16 14:43:57 +02002605 if path.isfile(config_file):
2606 break
2607 else:
garciadeblas4568a372021-03-24 09:19:48 +01002608 print(
2609 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2610 file=sys.stderr,
2611 )
tiernof5298be2018-05-16 14:43:57 +02002612 exit(1)
2613 nbi(config_file)
2614 except getopt.GetoptError as e:
2615 print(str(e), file=sys.stderr)
2616 # usage()
2617 exit(1)