blob: fa414f053384983fa4b1c786697f9f1bd27c8846 [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
31from osm_nbi.validation import ValidationError
tiernoa8d63632018-05-10 13:12:32 +020032from osm_common.dbbase import DbException
33from osm_common.fsbase import FsException
34from osm_common.msgbase import MsgException
tiernoc94c3df2018-02-09 15:38:54 +010035from http import HTTPStatus
tiernoc94c3df2018-02-09 15:38:54 +010036from codecs import getreader
tiernof5298be2018-05-16 14:43:57 +020037from os import environ, path
tiernob2e48bd2020-02-04 15:47:18 +000038from osm_nbi import version as nbi_version, version_date as nbi_version_date
tiernoc94c3df2018-02-09 15:38:54 +010039
40__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
tiernodfe09572018-04-24 10:41:10 +020041
garciadeblas4568a372021-03-24 09:19:48 +010042__version__ = "0.1.3" # file version, not NBI version
tierno9c630112019-08-29 14:21:41 +000043version_date = "Aug 2019"
44
garciadeblas4568a372021-03-24 09:19:48 +010045database_version = "1.2"
46auth_database_version = "1.0"
47nbi_server = None # instance of Server class
tierno932499c2019-01-28 17:28:10 +000048subscription_thread = None # instance of SubscriptionThread class
tiernoc94c3df2018-02-09 15:38:54 +010049
50"""
tiernof27c79b2018-03-12 17:08:42 +010051North Bound Interface (O: OSM specific; 5,X: SOL005 not implemented yet; O5: SOL005 implemented)
tiernoc94c3df2018-02-09 15:38:54 +010052URL: /osm GET POST PUT DELETE PATCH
garciadeblas9750c5a2018-10-15 16:20:35 +020053 /nsd/v1
tierno2236d202018-05-16 19:05:16 +020054 /ns_descriptors_content O O
55 /<nsdInfoId> O O O O
tiernoc94c3df2018-02-09 15:38:54 +010056 /ns_descriptors O5 O5
57 /<nsdInfoId> O5 O5 5
58 /nsd_content O5 O5
tiernof27c79b2018-03-12 17:08:42 +010059 /nsd O
60 /artifacts[/<artifactPath>] O
tiernoc94c3df2018-02-09 15:38:54 +010061 /pnf_descriptors 5 5
62 /<pnfdInfoId> 5 5 5
63 /pnfd_content 5 5
tiernof27c79b2018-03-12 17:08:42 +010064 /subscriptions 5 5
65 /<subscriptionId> 5 X
tiernoc94c3df2018-02-09 15:38:54 +010066
67 /vnfpkgm/v1
tierno55945e72018-04-06 16:40:27 +020068 /vnf_packages_content O O
tierno2236d202018-05-16 19:05:16 +020069 /<vnfPkgId> O O
tiernoc94c3df2018-02-09 15:38:54 +010070 /vnf_packages O5 O5
71 /<vnfPkgId> O5 O5 5
tiernoc94c3df2018-02-09 15:38:54 +010072 /package_content O5 O5
73 /upload_from_uri X
tiernof27c79b2018-03-12 17:08:42 +010074 /vnfd O5
75 /artifacts[/<artifactPath>] O5
76 /subscriptions X X
77 /<subscriptionId> X X
tiernoc94c3df2018-02-09 15:38:54 +010078
79 /nslcm/v1
tiernof27c79b2018-03-12 17:08:42 +010080 /ns_instances_content O O
tierno2236d202018-05-16 19:05:16 +020081 /<nsInstanceId> O O
tiernof27c79b2018-03-12 17:08:42 +010082 /ns_instances 5 5
tierno95692442018-05-24 18:05:28 +020083 /<nsInstanceId> O5 O5
tierno65acb4d2018-04-06 16:42:40 +020084 instantiate O5
85 terminate O5
86 action O
87 scale O5
elumalai8e3806c2022-04-28 17:26:24 +053088 migrate O
aticig544a2ae2022-04-05 09:00:17 +030089 update 05
garciadeblas0964edf2022-02-11 00:43:44 +010090 heal O5
tiernoc94c3df2018-02-09 15:38:54 +010091 /ns_lcm_op_occs 5 5
92 /<nsLcmOpOccId> 5 5 5
93 TO BE COMPLETED 5 5
tiernof759d822018-06-11 18:54:54 +020094 /vnf_instances (also vnfrs for compatibility) O
95 /<vnfInstanceId> O
tiernof27c79b2018-03-12 17:08:42 +010096 /subscriptions 5 5
97 /<subscriptionId> 5 X
garciadeblas9750c5a2018-10-15 16:20:35 +020098
tiernocb83c942018-09-24 17:28:13 +020099 /pdu/v1
tierno032916c2019-03-22 13:27:12 +0000100 /pdu_descriptors O O
tiernocb83c942018-09-24 17:28:13 +0200101 /<id> O O O O
garciadeblas9750c5a2018-10-15 16:20:35 +0200102
tiernof27c79b2018-03-12 17:08:42 +0100103 /admin/v1
104 /tokens O O
tierno2236d202018-05-16 19:05:16 +0200105 /<id> O O
tiernof27c79b2018-03-12 17:08:42 +0100106 /users O O
tiernocd54a4a2018-09-12 16:40:35 +0200107 /<id> O O O O
tiernof27c79b2018-03-12 17:08:42 +0100108 /projects O O
tierno2236d202018-05-16 19:05:16 +0200109 /<id> O O
tierno55ba2e62018-12-11 17:22:22 +0000110 /vim_accounts (also vims for compatibility) O O
111 /<id> O O O
112 /wim_accounts O O
tierno2236d202018-05-16 19:05:16 +0200113 /<id> O O O
tierno0f98af52018-03-19 10:28:22 +0100114 /sdns O O
tierno2236d202018-05-16 19:05:16 +0200115 /<id> O O O
delacruzramofe598fe2019-10-23 18:25:11 +0200116 /k8sclusters O O
117 /<id> O O O
118 /k8srepos O O
119 /<id> O O
Felipe Vicensb66b0412020-05-06 10:11:00 +0200120 /osmrepos O O
121 /<id> O O
tiernoc94c3df2018-02-09 15:38:54 +0100122
garciadeblas9750c5a2018-10-15 16:20:35 +0200123 /nst/v1 O O
124 /netslice_templates_content O O
125 /<nstInfoId> O O O O
126 /netslice_templates O O
127 /<nstInfoId> O O O
128 /nst_content O O
129 /nst O
130 /artifacts[/<artifactPath>] O
131 /subscriptions X X
132 /<subscriptionId> X X
133
134 /nsilcm/v1
135 /netslice_instances_content O O
136 /<SliceInstanceId> O O
137 /netslice_instances O O
138 /<SliceInstanceId> O O
139 instantiate O
140 terminate O
141 action O
142 /nsi_lcm_op_occs O O
143 /<nsiLcmOpOccId> O O O
144 /subscriptions X X
145 /<subscriptionId> X X
146
tierno2236d202018-05-16 19:05:16 +0200147query string:
148 Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force.
tierno36ec8602018-11-02 17:27:11 +0100149 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
150 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
151 op := "eq" | "neq" (or "ne") | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
152 attrName := string
tierno2236d202018-05-16 19:05:16 +0200153 For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any
154 item of the array, that is, pass if any item of the array pass the filter.
155 It allows both ne and neq for not equal
156 TODO: 4.3.3 Attribute selectors
157 all_fields, fields=x,y,.., exclude_default, exclude_fields=x,y,...
tiernoc94c3df2018-02-09 15:38:54 +0100158 (none) … same as “exclude_default”
159 all_fields … all attributes.
tierno2236d202018-05-16 19:05:16 +0200160 fields=<list> … all attributes except all complex attributes with minimum cardinality of zero that are not
161 conditionally mandatory, and that are not provided in <list>.
162 exclude_fields=<list> … all attributes except those complex attributes with a minimum cardinality of zero that
163 are not conditionally mandatory, and that are provided in <list>.
164 exclude_default … all attributes except those complex attributes with a minimum cardinality of zero that are not
165 conditionally mandatory, and that are part of the "default exclude set" defined in the present specification for
166 the particular resource
167 exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality
168 of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the
169 present specification for the particular resource, but that are not part of <list>
tierno65ca36d2019-02-12 19:27:52 +0100170 Additionally it admits some administrator values:
171 FORCE: To force operations skipping dependency checkings
172 ADMIN: To act as an administrator or a different project
173 PUBLIC: To get public descriptors or set a descriptor as public
174 SET_PROJECT: To make a descriptor available for other project
beierlmbc5a5242022-05-17 21:25:29 -0400175
tiernoc94c3df2018-02-09 15:38:54 +0100176Header field name Reference Example Descriptions
177 Accept IETF RFC 7231 [19] application/json Content-Types that are acceptable for the response.
178 This header field shall be present if the response is expected to have a non-empty message body.
179 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the request.
180 This header field shall be present if the request has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200181 Authorization IETF RFC 7235 [22] Bearer mF_9.B5f-4.1JqM The authorization token for the request.
182 Details are specified in clause 4.5.3.
tiernoc94c3df2018-02-09 15:38:54 +0100183 Range IETF RFC 7233 [21] 1000-2000 Requested range of bytes from a file
184Header field name Reference Example Descriptions
185 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the response.
186 This header field shall be present if the response has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200187 Location IETF RFC 7231 [19] http://www.example.com/vnflcm/v1/vnf_instances/123 Used in redirection, or when a
188 new resource has been created.
tiernoc94c3df2018-02-09 15:38:54 +0100189 This header field shall be present if the response status code is 201 or 3xx.
tierno2236d202018-05-16 19:05:16 +0200190 In the present document this header field is also used if the response status code is 202 and a new resource was
191 created.
192 WWW-Authenticate IETF RFC 7235 [22] Bearer realm="example" Challenge if the corresponding HTTP request has not
193 provided authorization, or error details if the corresponding HTTP request has provided an invalid authorization
194 token.
195 Accept-Ranges IETF RFC 7233 [21] bytes Used by the Server to signal whether or not it supports ranges for
196 certain resources.
197 Content-Range IETF RFC 7233 [21] bytes 21010-47021/ 47022 Signals the byte range that is contained in the
198 response, and the total length of the file.
tiernoc94c3df2018-02-09 15:38:54 +0100199 Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT
tiernoc94c3df2018-02-09 15:38:54 +0100200"""
201
tierno701018c2019-06-25 11:13:14 +0000202valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC")
203# ^ Contains possible administrative query string words:
204# ADMIN=True(by default)|Project|Project-list: See all elements, or elements of a project
205# (not owned by my session project).
206# PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public
207# FORCE=True(by default)|False: Force edition/deletion operations
208# SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio
209
210valid_url_methods = {
211 # contains allowed URL and methods, and the role_permission name
212 "admin": {
213 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100214 "tokens": {
215 "METHODS": ("GET", "POST", "DELETE"),
216 "ROLE_PERMISSION": "tokens:",
217 "<ID>": {"METHODS": ("GET", "DELETE"), "ROLE_PERMISSION": "tokens:id:"},
218 },
219 "users": {
220 "METHODS": ("GET", "POST"),
221 "ROLE_PERMISSION": "users:",
222 "<ID>": {
223 "METHODS": ("GET", "DELETE", "PATCH"),
224 "ROLE_PERMISSION": "users:id:",
225 },
226 },
227 "projects": {
228 "METHODS": ("GET", "POST"),
229 "ROLE_PERMISSION": "projects:",
230 "<ID>": {
231 "METHODS": ("GET", "DELETE", "PATCH"),
232 "ROLE_PERMISSION": "projects:id:",
233 },
234 },
235 "roles": {
236 "METHODS": ("GET", "POST"),
237 "ROLE_PERMISSION": "roles:",
238 "<ID>": {
239 "METHODS": ("GET", "DELETE", "PATCH"),
240 "ROLE_PERMISSION": "roles:id:",
241 },
242 },
243 "vims": {
244 "METHODS": ("GET", "POST"),
245 "ROLE_PERMISSION": "vims:",
246 "<ID>": {
247 "METHODS": ("GET", "DELETE", "PATCH"),
248 "ROLE_PERMISSION": "vims:id:",
249 },
250 },
251 "vim_accounts": {
252 "METHODS": ("GET", "POST"),
253 "ROLE_PERMISSION": "vim_accounts:",
254 "<ID>": {
255 "METHODS": ("GET", "DELETE", "PATCH"),
256 "ROLE_PERMISSION": "vim_accounts:id:",
257 },
258 },
259 "wim_accounts": {
260 "METHODS": ("GET", "POST"),
261 "ROLE_PERMISSION": "wim_accounts:",
262 "<ID>": {
263 "METHODS": ("GET", "DELETE", "PATCH"),
264 "ROLE_PERMISSION": "wim_accounts:id:",
265 },
266 },
267 "sdns": {
268 "METHODS": ("GET", "POST"),
269 "ROLE_PERMISSION": "sdn_controllers:",
270 "<ID>": {
271 "METHODS": ("GET", "DELETE", "PATCH"),
272 "ROLE_PERMISSION": "sdn_controllers:id:",
273 },
274 },
275 "k8sclusters": {
276 "METHODS": ("GET", "POST"),
277 "ROLE_PERMISSION": "k8sclusters:",
278 "<ID>": {
279 "METHODS": ("GET", "DELETE", "PATCH"),
280 "ROLE_PERMISSION": "k8sclusters:id:",
281 },
282 },
283 "vca": {
284 "METHODS": ("GET", "POST"),
285 "ROLE_PERMISSION": "vca:",
286 "<ID>": {
287 "METHODS": ("GET", "DELETE", "PATCH"),
288 "ROLE_PERMISSION": "vca:id:",
289 },
290 },
291 "k8srepos": {
292 "METHODS": ("GET", "POST"),
293 "ROLE_PERMISSION": "k8srepos:",
294 "<ID>": {
295 "METHODS": ("GET", "DELETE"),
296 "ROLE_PERMISSION": "k8srepos:id:",
297 },
298 },
299 "osmrepos": {
300 "METHODS": ("GET", "POST"),
301 "ROLE_PERMISSION": "osmrepos:",
302 "<ID>": {
303 "METHODS": ("GET", "DELETE", "PATCH"),
304 "ROLE_PERMISSION": "osmrepos:id:",
305 },
306 },
307 "domains": {
308 "METHODS": ("GET",),
309 "ROLE_PERMISSION": "domains:",
310 },
tierno701018c2019-06-25 11:13:14 +0000311 }
312 },
313 "pdu": {
314 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100315 "pdu_descriptors": {
316 "METHODS": ("GET", "POST"),
317 "ROLE_PERMISSION": "pduds:",
318 "<ID>": {
319 "METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"),
320 "ROLE_PERMISSION": "pduds:id:",
321 },
322 },
tierno701018c2019-06-25 11:13:14 +0000323 }
324 },
325 "nsd": {
326 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100327 "ns_descriptors_content": {
328 "METHODS": ("GET", "POST"),
329 "ROLE_PERMISSION": "nsds:",
330 "<ID>": {
331 "METHODS": ("GET", "PUT", "DELETE"),
332 "ROLE_PERMISSION": "nsds:id:",
333 },
334 },
335 "ns_descriptors": {
336 "METHODS": ("GET", "POST"),
337 "ROLE_PERMISSION": "nsds:",
338 "<ID>": {
339 "METHODS": ("GET", "DELETE", "PATCH"),
340 "ROLE_PERMISSION": "nsds:id:",
341 "nsd_content": {
342 "METHODS": ("GET", "PUT"),
343 "ROLE_PERMISSION": "nsds:id:content:",
344 },
345 "nsd": {
346 "METHODS": ("GET",), # descriptor inside package
347 "ROLE_PERMISSION": "nsds:id:content:",
348 },
349 "artifacts": {
350 "METHODS": ("GET",),
351 "ROLE_PERMISSION": "nsds:id:nsd_artifact:",
352 "*": None,
353 },
354 },
355 },
356 "pnf_descriptors": {
357 "TODO": ("GET", "POST"),
358 "<ID>": {
359 "TODO": ("GET", "DELETE", "PATCH"),
360 "pnfd_content": {"TODO": ("GET", "PUT")},
361 },
362 },
363 "subscriptions": {
364 "TODO": ("GET", "POST"),
365 "<ID>": {"TODO": ("GET", "DELETE")},
366 },
tierno701018c2019-06-25 11:13:14 +0000367 }
368 },
369 "vnfpkgm": {
370 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100371 "vnf_packages_content": {
372 "METHODS": ("GET", "POST"),
373 "ROLE_PERMISSION": "vnfds:",
374 "<ID>": {
375 "METHODS": ("GET", "PUT", "DELETE"),
376 "ROLE_PERMISSION": "vnfds:id:",
377 },
378 },
379 "vnf_packages": {
380 "METHODS": ("GET", "POST"),
381 "ROLE_PERMISSION": "vnfds:",
382 "<ID>": {
383 "METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo
384 "ROLE_PERMISSION": "vnfds:id:",
385 "package_content": {
386 "METHODS": ("GET", "PUT"), # package
387 "ROLE_PERMISSION": "vnfds:id:",
388 "upload_from_uri": {
389 "METHODS": (),
390 "TODO": ("POST",),
391 "ROLE_PERMISSION": "vnfds:id:upload:",
392 },
393 },
394 "vnfd": {
395 "METHODS": ("GET",), # descriptor inside package
396 "ROLE_PERMISSION": "vnfds:id:content:",
397 },
398 "artifacts": {
399 "METHODS": ("GET",),
400 "ROLE_PERMISSION": "vnfds:id:vnfd_artifact:",
401 "*": None,
402 },
403 "action": {
404 "METHODS": ("POST",),
405 "ROLE_PERMISSION": "vnfds:id:action:",
406 },
407 },
408 },
409 "subscriptions": {
410 "TODO": ("GET", "POST"),
411 "<ID>": {"TODO": ("GET", "DELETE")},
412 },
413 "vnfpkg_op_occs": {
414 "METHODS": ("GET",),
415 "ROLE_PERMISSION": "vnfds:vnfpkgops:",
416 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnfds:vnfpkgops:id:"},
417 },
tierno701018c2019-06-25 11:13:14 +0000418 }
419 },
420 "nslcm": {
421 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100422 "ns_instances_content": {
423 "METHODS": ("GET", "POST"),
424 "ROLE_PERMISSION": "ns_instances:",
425 "<ID>": {
426 "METHODS": ("GET", "DELETE"),
427 "ROLE_PERMISSION": "ns_instances:id:",
428 },
429 },
430 "ns_instances": {
431 "METHODS": ("GET", "POST"),
432 "ROLE_PERMISSION": "ns_instances:",
433 "<ID>": {
434 "METHODS": ("GET", "DELETE"),
435 "ROLE_PERMISSION": "ns_instances:id:",
garciadeblas0964edf2022-02-11 00:43:44 +0100436 "heal": {
437 "METHODS": ("POST",),
438 "ROLE_PERMISSION": "ns_instances:id:heal:",
439 },
garciadeblas4568a372021-03-24 09:19:48 +0100440 "scale": {
441 "METHODS": ("POST",),
442 "ROLE_PERMISSION": "ns_instances:id:scale:",
443 },
444 "terminate": {
445 "METHODS": ("POST",),
446 "ROLE_PERMISSION": "ns_instances:id:terminate:",
447 },
448 "instantiate": {
449 "METHODS": ("POST",),
450 "ROLE_PERMISSION": "ns_instances:id:instantiate:",
451 },
elumalai8e3806c2022-04-28 17:26:24 +0530452 "migrate": {
453 "METHODS": ("POST",),
454 "ROLE_PERMISSION": "ns_instances:id:migrate:",
455 },
garciadeblas4568a372021-03-24 09:19:48 +0100456 "action": {
457 "METHODS": ("POST",),
458 "ROLE_PERMISSION": "ns_instances:id:action:",
459 },
aticig544a2ae2022-04-05 09:00:17 +0300460 "update": {
461 "METHODS": ("POST",),
462 "ROLE_PERMISSION": "ns_instances:id:update:",
463 },
govindarajul519da482022-04-29 19:05:22 +0530464 "verticalscale": {
465 "METHODS": ("POST",),
garciadeblasf2af4a12023-01-24 16:56:54 +0100466 "ROLE_PERMISSION": "ns_instances:id:verticalscale:",
467 },
garciadeblas4568a372021-03-24 09:19:48 +0100468 },
469 },
470 "ns_lcm_op_occs": {
471 "METHODS": ("GET",),
472 "ROLE_PERMISSION": "ns_instances:opps:",
473 "<ID>": {
474 "METHODS": ("GET",),
475 "ROLE_PERMISSION": "ns_instances:opps:id:",
476 },
477 },
478 "vnfrs": {
479 "METHODS": ("GET",),
480 "ROLE_PERMISSION": "vnf_instances:",
481 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
482 },
483 "vnf_instances": {
484 "METHODS": ("GET",),
485 "ROLE_PERMISSION": "vnf_instances:",
486 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
487 },
488 "subscriptions": {
489 "METHODS": ("GET", "POST"),
490 "ROLE_PERMISSION": "ns_subscriptions:",
491 "<ID>": {
492 "METHODS": ("GET", "DELETE"),
493 "ROLE_PERMISSION": "ns_subscriptions:id:",
494 },
495 },
tierno701018c2019-06-25 11:13:14 +0000496 }
497 },
almagiae47b9132022-05-17 14:12:22 +0200498 "vnflcm": {
499 "v1": {
garciadeblasf2af4a12023-01-24 16:56:54 +0100500 "vnf_instances": {
501 "METHODS": ("GET", "POST"),
502 "ROLE_PERMISSION": "vnflcm_instances:",
503 "<ID>": {
504 "METHODS": ("GET", "DELETE"),
505 "ROLE_PERMISSION": "vnflcm_instances:id:",
506 "scale": {
507 "METHODS": ("POST",),
508 "ROLE_PERMISSION": "vnflcm_instances:id:scale:",
509 },
510 "terminate": {
511 "METHODS": ("POST",),
512 "ROLE_PERMISSION": "vnflcm_instances:id:terminate:",
513 },
514 "instantiate": {
515 "METHODS": ("POST",),
516 "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:",
517 },
518 },
519 },
520 "vnf_lcm_op_occs": {
521 "METHODS": ("GET",),
522 "ROLE_PERMISSION": "vnf_instances:opps:",
523 "<ID>": {
524 "METHODS": ("GET",),
525 "ROLE_PERMISSION": "vnf_instances:opps:id:",
526 },
527 },
528 "subscriptions": {
529 "METHODS": ("GET", "POST"),
530 "ROLE_PERMISSION": "vnflcm_subscriptions:",
531 "<ID>": {
532 "METHODS": ("GET", "DELETE"),
533 "ROLE_PERMISSION": "vnflcm_subscriptions:id:",
534 },
535 },
almagiae47b9132022-05-17 14:12:22 +0200536 }
537 },
tierno701018c2019-06-25 11:13:14 +0000538 "nst": {
539 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100540 "netslice_templates_content": {
541 "METHODS": ("GET", "POST"),
542 "ROLE_PERMISSION": "slice_templates:",
543 "<ID>": {
544 "METHODS": ("GET", "PUT", "DELETE"),
545 "ROLE_PERMISSION": "slice_templates:id:",
546 },
547 },
548 "netslice_templates": {
549 "METHODS": ("GET", "POST"),
550 "ROLE_PERMISSION": "slice_templates:",
551 "<ID>": {
552 "METHODS": ("GET", "DELETE"),
553 "TODO": ("PATCH",),
554 "ROLE_PERMISSION": "slice_templates:id:",
555 "nst_content": {
556 "METHODS": ("GET", "PUT"),
557 "ROLE_PERMISSION": "slice_templates:id:content:",
558 },
559 "nst": {
560 "METHODS": ("GET",), # descriptor inside package
561 "ROLE_PERMISSION": "slice_templates:id:content:",
562 },
563 "artifacts": {
564 "METHODS": ("GET",),
565 "ROLE_PERMISSION": "slice_templates:id:content:",
566 "*": None,
567 },
568 },
569 },
570 "subscriptions": {
571 "TODO": ("GET", "POST"),
572 "<ID>": {"TODO": ("GET", "DELETE")},
573 },
tierno701018c2019-06-25 11:13:14 +0000574 }
575 },
576 "nsilcm": {
577 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100578 "netslice_instances_content": {
579 "METHODS": ("GET", "POST"),
580 "ROLE_PERMISSION": "slice_instances:",
581 "<ID>": {
582 "METHODS": ("GET", "DELETE"),
583 "ROLE_PERMISSION": "slice_instances:id:",
584 },
585 },
586 "netslice_instances": {
587 "METHODS": ("GET", "POST"),
588 "ROLE_PERMISSION": "slice_instances:",
589 "<ID>": {
590 "METHODS": ("GET", "DELETE"),
591 "ROLE_PERMISSION": "slice_instances:id:",
592 "terminate": {
593 "METHODS": ("POST",),
594 "ROLE_PERMISSION": "slice_instances:id:terminate:",
595 },
596 "instantiate": {
597 "METHODS": ("POST",),
598 "ROLE_PERMISSION": "slice_instances:id:instantiate:",
599 },
600 "action": {
601 "METHODS": ("POST",),
602 "ROLE_PERMISSION": "slice_instances:id:action:",
603 },
604 },
605 },
606 "nsi_lcm_op_occs": {
607 "METHODS": ("GET",),
608 "ROLE_PERMISSION": "slice_instances:opps:",
609 "<ID>": {
610 "METHODS": ("GET",),
611 "ROLE_PERMISSION": "slice_instances:opps:id:",
612 },
613 },
tierno701018c2019-06-25 11:13:14 +0000614 }
615 },
616 "nspm": {
617 "v1": {
618 "pm_jobs": {
619 "<ID>": {
620 "reports": {
garciadeblas4568a372021-03-24 09:19:48 +0100621 "<ID>": {
622 "METHODS": ("GET",),
623 "ROLE_PERMISSION": "reports:id:",
624 }
tierno701018c2019-06-25 11:13:14 +0000625 }
626 },
627 },
628 },
629 },
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000630 "nsfm": {
631 "v1": {
garciadeblasf2af4a12023-01-24 16:56:54 +0100632 "alarms": {
633 "METHODS": ("GET", "PATCH"),
634 "ROLE_PERMISSION": "alarms:",
635 "<ID>": {
636 "METHODS": ("GET", "PATCH"),
637 "ROLE_PERMISSION": "alarms:id:",
638 },
639 }
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000640 },
641 },
tierno701018c2019-06-25 11:13:14 +0000642}
643
tiernoc94c3df2018-02-09 15:38:54 +0100644
645class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100646 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
647 Exception.__init__(self, message)
648 self.http_code = http_code
649
650
651class Server(object):
652 instance = 0
653 # to decode bytes to str
654 reader = getreader("utf-8")
655
656 def __init__(self):
657 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000658 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100659 self.engine = Engine(self.authenticator)
tiernoc94c3df2018-02-09 15:38:54 +0100660
tiernoc94c3df2018-02-09 15:38:54 +0100661 def _format_in(self, kwargs):
garciadeblasf2af4a12023-01-24 16:56:54 +0100662 error_text = "" # error_text must be initialized outside try
tiernoc94c3df2018-02-09 15:38:54 +0100663 try:
664 indata = None
665 if cherrypy.request.body.length:
666 error_text = "Invalid input format "
667
668 if "Content-Type" in cherrypy.request.headers:
669 if "application/json" in cherrypy.request.headers["Content-Type"]:
670 error_text = "Invalid json format "
671 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100672 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100673 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
674 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100675 indata = yaml.load(
676 cherrypy.request.body, Loader=yaml.SafeLoader
677 )
gcalvinode4adfe2018-10-30 11:46:09 +0100678 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100679 elif (
680 "application/binary" in cherrypy.request.headers["Content-Type"]
681 or "application/gzip"
682 in cherrypy.request.headers["Content-Type"]
683 or "application/zip" in cherrypy.request.headers["Content-Type"]
684 or "text/plain" in cherrypy.request.headers["Content-Type"]
685 ):
tiernof27c79b2018-03-12 17:08:42 +0100686 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100687 elif (
688 "multipart/form-data"
689 in cherrypy.request.headers["Content-Type"]
690 ):
tiernoc94c3df2018-02-09 15:38:54 +0100691 if "descriptor_file" in kwargs:
692 filecontent = kwargs.pop("descriptor_file")
693 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100694 raise NbiException(
695 "empty file or content", HTTPStatus.BAD_REQUEST
696 )
tiernof27c79b2018-03-12 17:08:42 +0100697 indata = filecontent.file # .read()
tiernoc94c3df2018-02-09 15:38:54 +0100698 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100699 cherrypy.request.headers[
700 "Content-Type"
701 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100702 else:
703 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
704 # "Only 'Content-Type' of type 'application/json' or
705 # 'application/yaml' for input format are available")
706 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100707 indata = yaml.load(
708 cherrypy.request.body, Loader=yaml.SafeLoader
709 )
gcalvinode4adfe2018-10-30 11:46:09 +0100710 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100711 else:
712 error_text = "Invalid yaml format "
delacruzramob19cadc2019-10-08 10:18:02 +0200713 indata = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
gcalvinode4adfe2018-10-30 11:46:09 +0100714 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100715 if not indata:
716 indata = {}
717
tiernoc94c3df2018-02-09 15:38:54 +0100718 format_yaml = False
719 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
720 format_yaml = True
721
722 for k, v in kwargs.items():
723 if isinstance(v, str):
724 if v == "":
725 kwargs[k] = None
726 elif format_yaml:
727 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200728 kwargs[k] = yaml.load(v, Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200729 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100730 pass
garciadeblas4568a372021-03-24 09:19:48 +0100731 elif (
732 k.endswith(".gt")
733 or k.endswith(".lt")
734 or k.endswith(".gte")
735 or k.endswith(".lte")
736 ):
tiernoc94c3df2018-02-09 15:38:54 +0100737 try:
738 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200739 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100740 try:
741 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200742 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100743 pass
744 elif v.find(",") > 0:
745 kwargs[k] = v.split(",")
746 elif isinstance(v, (list, tuple)):
747 for index in range(0, len(v)):
748 if v[index] == "":
749 v[index] = None
750 elif format_yaml:
751 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200752 v[index] = yaml.load(v[index], Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200753 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100754 pass
755
tiernof27c79b2018-03-12 17:08:42 +0100756 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100757 except (ValueError, yaml.YAMLError) as exc:
758 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
759 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100760 raise NbiException(
761 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
762 )
tiernob92094f2018-05-11 13:44:22 +0200763 except Exception as exc:
764 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100765
766 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000767 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100768 """
769 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100770 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000771 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000772 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100773 :return: None
774 """
tierno0f98af52018-03-19 10:28:22 +0100775 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100776 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100777 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100778 return html.format(
779 data, cherrypy.request, cherrypy.response, token_info
780 )
tierno09c073e2018-04-26 13:36:48 +0200781 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100782 return
783 elif hasattr(data, "read"): # file object
784 if _format:
785 cherrypy.response.headers["Content-Type"] = _format
786 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100787 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100788 else:
garciadeblas4568a372021-03-24 09:19:48 +0100789 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100790 # TODO check that cherrypy close file. If not implement pending things to close per thread next
791 return data
tierno0f98af52018-03-19 10:28:22 +0100792 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000793 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100794 return html.format(
795 data, cherrypy.request, cherrypy.response, token_info
796 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000797 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100798 pass
garciadeblas4568a372021-03-24 09:19:48 +0100799 elif "application/json" in accept or (
800 cherrypy.response.status and cherrypy.response.status >= 300
801 ):
802 cherrypy.response.headers[
803 "Content-Type"
804 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000805 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100806 return a.encode("utf8")
807 cherrypy.response.headers["Content-Type"] = "application/yaml"
808 return yaml.safe_dump(
809 data,
810 explicit_start=True,
811 indent=4,
812 default_flow_style=False,
813 tags=False,
814 encoding="utf-8",
815 allow_unicode=True,
816 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +0100817
818 @cherrypy.expose
819 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +0000820 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100821 try:
822 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +0000823 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +0100824 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +0100825 else:
garciadeblas4568a372021-03-24 09:19:48 +0100826 raise cherrypy.HTTPError(
827 HTTPStatus.METHOD_NOT_ALLOWED.value,
828 "Method {} not allowed for tokens".format(cherrypy.request.method),
829 )
tiernoc94c3df2018-02-09 15:38:54 +0100830
tierno701018c2019-06-25 11:13:14 +0000831 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100832
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100833 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +0000834 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +0100835 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +0000836 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100837
838 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +0200839 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +0200840 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +0200841 try:
842 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +0100843 raise NbiException(
844 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
845 )
tierno55945e72018-04-06 16:40:27 +0200846 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +0100847 raise NbiException(
848 "Invalid URL or query string for version",
849 HTTPStatus.METHOD_NOT_ALLOWED,
850 )
tierno9c630112019-08-29 14:21:41 +0000851 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +0000852 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
853 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +0200854 except NbiException as e:
855 cherrypy.response.status = e.http_code.value
856 problem_details = {
857 "code": e.http_code.name,
858 "status": e.http_code.value,
859 "detail": str(e),
860 }
861 return self._format_out(problem_details, None)
862
tierno12eac3c2020-03-19 23:22:08 +0000863 def domain(self):
864 try:
865 domains = {
garciadeblas4568a372021-03-24 09:19:48 +0100866 "user_domain_name": cherrypy.tree.apps["/osm"]
867 .config["authentication"]
868 .get("user_domain_name"),
869 "project_domain_name": cherrypy.tree.apps["/osm"]
870 .config["authentication"]
871 .get("project_domain_name"),
872 }
tierno12eac3c2020-03-19 23:22:08 +0000873 return self._format_out(domains)
874 except NbiException as e:
875 cherrypy.response.status = e.http_code.value
876 problem_details = {
877 "code": e.http_code.name,
878 "status": e.http_code.value,
879 "detail": str(e),
880 }
881 return self._format_out(problem_details, None)
882
tiernoa5035702019-07-29 08:54:42 +0000883 @staticmethod
884 def _format_login(token_info):
885 """
886 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
887 log this information
888 :param token_info: Dictionary with token content
889 :return: None
890 """
891 cherrypy.request.login = token_info.get("username", "-")
892 if token_info.get("project_name"):
893 cherrypy.request.login += "/" + token_info["project_name"]
894 if token_info.get("id"):
895 cherrypy.request.login += ";session=" + token_info["id"][0:12]
896
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000897 # NS Fault Management
898 @cherrypy.expose
garciadeblasf2af4a12023-01-24 16:56:54 +0100899 def nsfm(
900 self,
901 version=None,
902 topic=None,
903 uuid=None,
904 project_name=None,
905 ns_id=None,
906 *args,
907 **kwargs
908 ):
909 if topic == "alarms":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000910 try:
911 method = cherrypy.request.method
garciadeblasf2af4a12023-01-24 16:56:54 +0100912 role_permission = self._check_valid_url_method(
913 method, "nsfm", version, topic, None, None, *args
914 )
915 query_string_operations = self._extract_query_string_operations(
916 kwargs, method
917 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000918
garciadeblasf2af4a12023-01-24 16:56:54 +0100919 self.authenticator.authorize(
920 role_permission, query_string_operations, None
921 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000922
923 # to handle get request
garciadeblasf2af4a12023-01-24 16:56:54 +0100924 if cherrypy.request.method == "GET":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000925 # if request is on basis of uuid
garciadeblasf2af4a12023-01-24 16:56:54 +0100926 if uuid and uuid != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000927 try:
928 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
garciadeblasf2af4a12023-01-24 16:56:54 +0100929 alarm_action = self.engine.db.get_one(
930 "alarms_action", {"uuid": uuid}
931 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000932 alarm.update(alarm_action)
garciadeblasf2af4a12023-01-24 16:56:54 +0100933 vnf = self.engine.db.get_one(
934 "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}
935 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000936 alarm["vnf-id"] = vnf["_id"]
937 return self._format_out(str(alarm))
938 except Exception:
939 return self._format_out("Please provide valid alarm uuid")
garciadeblasf2af4a12023-01-24 16:56:54 +0100940 elif ns_id and ns_id != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000941 # if request is on basis of ns_id
942 try:
garciadeblasf2af4a12023-01-24 16:56:54 +0100943 alarms = self.engine.db.get_list(
944 "alarms", {"tags.ns_id": ns_id}
945 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000946 for alarm in alarms:
garciadeblasf2af4a12023-01-24 16:56:54 +0100947 alarm_action = self.engine.db.get_one(
948 "alarms_action", {"uuid": alarm["uuid"]}
949 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000950 alarm.update(alarm_action)
951 return self._format_out(str(alarms))
952 except Exception:
953 return self._format_out("Please provide valid ns id")
954 else:
955 # to return only alarm which are related to given project
garciadeblasf2af4a12023-01-24 16:56:54 +0100956 project = self.engine.db.get_one(
957 "projects", {"name": project_name}
958 )
959 project_id = project.get("_id")
960 ns_list = self.engine.db.get_list(
961 "nsrs", {"_admin.projects_read": project_id}
962 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000963 ns_ids = []
964 for ns in ns_list:
965 ns_ids.append(ns.get("_id"))
966 alarms = self.engine.db.get_list("alarms")
garciadeblasf2af4a12023-01-24 16:56:54 +0100967 alarm_list = [
968 alarm
969 for alarm in alarms
970 if alarm["tags"]["ns_id"] in ns_ids
971 ]
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000972 for alrm in alarm_list:
garciadeblasf2af4a12023-01-24 16:56:54 +0100973 action = self.engine.db.get_one(
974 "alarms_action", {"uuid": alrm.get("uuid")}
975 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000976 alrm.update(action)
977 return self._format_out(str(alarm_list))
978 # to handle patch request for alarm update
garciadeblasf2af4a12023-01-24 16:56:54 +0100979 elif cherrypy.request.method == "PATCH":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000980 data = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
981 try:
982 # check if uuid is valid
983 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
984 except Exception:
985 return self._format_out("Please provide valid alarm uuid.")
986 if data.get("is_enable") is not None:
987 if data.get("is_enable"):
garciadeblasf2af4a12023-01-24 16:56:54 +0100988 alarm_status = "ok"
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000989 else:
garciadeblasf2af4a12023-01-24 16:56:54 +0100990 alarm_status = "disabled"
991 self.engine.db.set_one(
992 "alarms",
993 {"uuid": data.get("uuid")},
994 {"alarm_status": alarm_status},
995 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000996 else:
garciadeblasf2af4a12023-01-24 16:56:54 +0100997 self.engine.db.set_one(
998 "alarms",
999 {"uuid": data.get("uuid")},
1000 {"threshold": data.get("threshold")},
1001 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001002 return self._format_out("Alarm updated")
1003 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01001004 if isinstance(
1005 e,
1006 (
1007 NbiException,
1008 EngineException,
1009 DbException,
1010 FsException,
1011 MsgException,
1012 AuthException,
1013 ValidationError,
1014 AuthconnException,
1015 ),
1016 ):
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001017 http_code_value = cherrypy.response.status = e.http_code.value
1018 http_code_name = e.http_code.name
1019 cherrypy.log("Exception {}".format(e))
1020 else:
garciadeblasf2af4a12023-01-24 16:56:54 +01001021 http_code_value = (
1022 cherrypy.response.status
1023 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001024 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
1025 http_code_name = HTTPStatus.BAD_REQUEST.name
1026 problem_details = {
1027 "code": http_code_name,
1028 "status": http_code_value,
1029 "detail": str(e),
1030 }
1031 return self._format_out(problem_details)
1032
tierno55945e72018-04-06 16:40:27 +02001033 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +01001034 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +00001035 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001036 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +01001037 indata = self._format_in(kwargs)
1038 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001039 raise NbiException(
1040 "Expected application/yaml or application/json Content-Type",
1041 HTTPStatus.BAD_REQUEST,
1042 )
tiernoa5035702019-07-29 08:54:42 +00001043
1044 if method == "GET":
1045 token_info = self.authenticator.authorize()
1046 # for logging
1047 self._format_login(token_info)
1048 if token_id:
1049 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +01001050 else:
tiernoa5035702019-07-29 08:54:42 +00001051 outdata = self.authenticator.get_token_list(token_info)
1052 elif method == "POST":
1053 try:
1054 token_info = self.authenticator.authorize()
1055 except Exception:
1056 token_info = None
1057 if kwargs:
1058 indata.update(kwargs)
1059 # This is needed to log the user when authentication fails
1060 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +01001061 outdata = token_info = self.authenticator.new_token(
1062 token_info, indata, cherrypy.request.remote
1063 )
garciadeblasf2af4a12023-01-24 16:56:54 +01001064 cherrypy.session["Authorization"] = outdata["_id"] # pylint: disable=E1101
tiernoa5035702019-07-29 08:54:42 +00001065 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1066 # for logging
1067 self._format_login(token_info)
selvi.ja9a1fc82022-04-04 06:54:30 +00001068 # password expiry check
1069 if self.authenticator.check_password_expiry(outdata):
garciadeblasf2af4a12023-01-24 16:56:54 +01001070 outdata = {
1071 "id": outdata["id"],
1072 "message": "change_password",
1073 "user_id": outdata["user_id"],
1074 }
tiernoa5035702019-07-29 08:54:42 +00001075 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1076 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
1077 elif method == "DELETE":
1078 if not token_id and "id" in kwargs:
1079 token_id = kwargs["id"]
1080 elif not token_id:
1081 token_info = self.authenticator.authorize()
1082 # for logging
1083 self._format_login(token_info)
1084 token_id = token_info["_id"]
1085 outdata = self.authenticator.del_token(token_id)
1086 token_info = None
garciadeblasf2af4a12023-01-24 16:56:54 +01001087 cherrypy.session["Authorization"] = "logout" # pylint: disable=E1101
tiernoa5035702019-07-29 08:54:42 +00001088 # cherrypy.response.cookie["Authorization"] = token_id
1089 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1090 else:
garciadeblas4568a372021-03-24 09:19:48 +01001091 raise NbiException(
1092 "Method {} not allowed for token".format(method),
1093 HTTPStatus.METHOD_NOT_ALLOWED,
1094 )
tiernoa5035702019-07-29 08:54:42 +00001095 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001096
1097 @cherrypy.expose
1098 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001099 if not cherrypy.config.get("server.enable_test") or (
1100 isinstance(cherrypy.config["server.enable_test"], str)
1101 and cherrypy.config["server.enable_test"].lower() == "false"
1102 ):
tierno4836bac2020-01-15 14:41:48 +00001103 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1104 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001105 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001106 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001107 return (
1108 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1109 "sleep/<time>\nmessage/topic\n</pre></html>"
1110 )
tiernof27c79b2018-03-12 17:08:42 +01001111
1112 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001113 try:
1114 # self.engine.load_dbase(cherrypy.request.app.config)
garciadeblasf2af4a12023-01-24 16:56:54 +01001115 pid = self.authenticator.create_admin_project()
1116 self.authenticator.create_admin_user(pid)
tiernoc94c3df2018-02-09 15:38:54 +01001117 return "Done. User 'admin', password 'admin' created"
1118 except Exception:
1119 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1120 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001121 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001122 return cherrypy.lib.static.serve_file(
1123 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1124 "text/plain",
1125 "attachment",
1126 )
tiernof27c79b2018-03-12 17:08:42 +01001127 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001128 f_path = (
1129 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1130 )
tiernof27c79b2018-03-12 17:08:42 +01001131 f = open(f_path, "r")
1132 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001133 return f
tierno55945e72018-04-06 16:40:27 +02001134
tiernof27c79b2018-03-12 17:08:42 +01001135 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001136 deleted_info = self.engine.db.del_list(args[1], kwargs)
1137 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1138 elif len(args) and args[0] == "fs-clear":
1139 if len(args) >= 2:
1140 folders = (args[1],)
1141 else:
1142 folders = self.engine.fs.dir_ls(".")
1143 for folder in folders:
1144 self.engine.fs.file_delete(folder)
1145 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001146 elif args and args[0] == "login":
1147 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001148 cherrypy.response.headers[
1149 "WWW-Authenticate"
1150 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001151 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1152 elif args and args[0] == "login2":
1153 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001154 cherrypy.response.headers[
1155 "WWW-Authenticate"
1156 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001157 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1158 elif args and args[0] == "sleep":
1159 sleep_time = 5
1160 try:
1161 sleep_time = int(args[1])
1162 except Exception:
1163 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1164 return self._format_out("Database already initialized")
1165 thread_info = cherrypy.thread_data
1166 print(thread_info)
1167 time.sleep(sleep_time)
1168 # thread_info
1169 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001170 main_topic = args[1]
1171 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001172 try:
garciadeblas4568a372021-03-24 09:19:48 +01001173 if cherrypy.request.method == "POST":
delacruzramob19cadc2019-10-08 10:18:02 +02001174 to_send = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
tierno55945e72018-04-06 16:40:27 +02001175 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001176 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001177 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001178 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001179 for k, v in kwargs.items():
tiernof1509b22020-05-12 14:32:37 +00001180 v_dict = yaml.load(v, Loader=yaml.SafeLoader)
1181 self.engine.msg.write(main_topic, k, v_dict)
1182 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001183 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001184 return_text += "Error: " + str(e)
1185 return_text += "</pre></html>\n"
1186 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001187
1188 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001189 "<html><pre>\nheaders:\n args: {}\n".format(args)
1190 + " kwargs: {}\n".format(kwargs)
1191 + " headers: {}\n".format(cherrypy.request.headers)
1192 + " path_info: {}\n".format(cherrypy.request.path_info)
1193 + " query_string: {}\n".format(cherrypy.request.query_string)
garciadeblasf2af4a12023-01-24 16:56:54 +01001194 + " session: {}\n".format(cherrypy.session) # pylint: disable=E1101
garciadeblas4568a372021-03-24 09:19:48 +01001195 + " cookie: {}\n".format(cherrypy.request.cookie)
1196 + " method: {}\n".format(cherrypy.request.method)
garciadeblasf2af4a12023-01-24 16:56:54 +01001197 + " session: {}\n".format(
1198 cherrypy.session.get("fieldname") # pylint: disable=E1101
1199 )
garciadeblas4568a372021-03-24 09:19:48 +01001200 + " body:\n"
1201 )
tiernoc94c3df2018-02-09 15:38:54 +01001202 return_text += " length: {}\n".format(cherrypy.request.body.length)
1203 if cherrypy.request.body.length:
1204 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001205 str(
1206 cherrypy.request.body.read(
1207 int(cherrypy.request.headers.get("Content-Length", 0))
1208 )
1209 )
1210 )
tiernoc94c3df2018-02-09 15:38:54 +01001211 if thread_info:
1212 return_text += "thread: {}\n".format(thread_info)
1213 return_text += "</pre></html>"
1214 return return_text
1215
tierno701018c2019-06-25 11:13:14 +00001216 @staticmethod
1217 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001218 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001219 raise NbiException(
1220 "URL must contain at least 'main_topic/version/topic'",
1221 HTTPStatus.METHOD_NOT_ALLOWED,
1222 )
tiernof27c79b2018-03-12 17:08:42 +01001223
tierno701018c2019-06-25 11:13:14 +00001224 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001225 for arg in args:
1226 if arg is None:
1227 break
1228 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001229 raise NbiException(
1230 "URL contains unexpected extra items '{}'".format(arg),
1231 HTTPStatus.METHOD_NOT_ALLOWED,
1232 )
tiernof27c79b2018-03-12 17:08:42 +01001233
1234 if arg in reference:
1235 reference = reference[arg]
1236 elif "<ID>" in reference:
1237 reference = reference["<ID>"]
1238 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001239 # if there is content
1240 if reference["*"]:
1241 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001242 break
1243 else:
garciadeblas4568a372021-03-24 09:19:48 +01001244 raise NbiException(
1245 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1246 )
tiernof27c79b2018-03-12 17:08:42 +01001247 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001248 raise NbiException(
1249 "Method {} not supported yet for this URL".format(method),
1250 HTTPStatus.NOT_IMPLEMENTED,
1251 )
tierno2236d202018-05-16 19:05:16 +02001252 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001253 raise NbiException(
1254 "Method {} not supported for this URL".format(method),
1255 HTTPStatus.METHOD_NOT_ALLOWED,
1256 )
tierno701018c2019-06-25 11:13:14 +00001257 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001258
1259 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001260 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001261 """
1262 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001263 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001264 :param version:
tiernob24258a2018-10-04 18:39:49 +02001265 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001266 :param id:
1267 :return: None
1268 """
1269 # 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 +01001270 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1271 main_topic, version, topic, id
1272 )
tiernof27c79b2018-03-12 17:08:42 +01001273 return
1274
tierno65ca36d2019-02-12 19:27:52 +01001275 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001276 def _extract_query_string_operations(kwargs, method):
1277 """
1278
1279 :param kwargs:
1280 :return:
1281 """
1282 query_string_operations = []
1283 if kwargs:
1284 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1285 if qs in kwargs and kwargs[qs].lower() != "false":
1286 query_string_operations.append(qs.lower() + ":" + method.lower())
1287 return query_string_operations
1288
1289 @staticmethod
1290 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001291 """
1292 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1293 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001294 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001295 :param kwargs: query string input.
1296 :param method: http method: GET, POSST, PUT, ...
1297 :param _id:
1298 :return: admin_query dictionary with keys:
1299 public: True, False or None
1300 force: True or False
1301 project_id: tuple with projects used for accessing an element
1302 set_project: tuple with projects that a created element will belong to
1303 method: show, list, delete, write
1304 """
garciadeblas4568a372021-03-24 09:19:48 +01001305 admin_query = {
1306 "force": False,
1307 "project_id": (token_info["project_id"],),
1308 "username": token_info["username"],
1309 "admin": token_info["admin"],
1310 "public": None,
1311 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1312 }
tierno65ca36d2019-02-12 19:27:52 +01001313 if kwargs:
1314 # FORCE
1315 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001316 if (
1317 kwargs["FORCE"].lower() != "false"
1318 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001319 admin_query["force"] = True
1320 del kwargs["FORCE"]
1321 # PUBLIC
1322 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001323 if (
1324 kwargs["PUBLIC"].lower() != "false"
1325 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001326 admin_query["public"] = True
1327 else:
1328 admin_query["public"] = False
1329 del kwargs["PUBLIC"]
1330 # ADMIN
1331 if "ADMIN" in kwargs:
1332 behave_as = kwargs.pop("ADMIN")
1333 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001334 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001335 raise NbiException(
1336 "Only admin projects can use 'ADMIN' query string",
1337 HTTPStatus.UNAUTHORIZED,
1338 )
1339 if (
1340 not behave_as or behave_as.lower() == "true"
1341 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001342 admin_query["project_id"] = ()
1343 elif isinstance(behave_as, (list, tuple)):
1344 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001345 else: # isinstance(behave_as, str)
1346 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001347 if "SET_PROJECT" in kwargs:
1348 set_project = kwargs.pop("SET_PROJECT")
1349 if not set_project:
1350 admin_query["set_project"] = list(admin_query["project_id"])
1351 else:
1352 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001353 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001354 if admin_query["project_id"]:
1355 for p in set_project:
1356 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001357 raise NbiException(
1358 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1359 "'ADMIN='{p}'".format(p=p),
1360 HTTPStatus.UNAUTHORIZED,
1361 )
tierno65ca36d2019-02-12 19:27:52 +01001362 admin_query["set_project"] = set_project
1363
1364 # PROJECT_READ
1365 # if "PROJECT_READ" in kwargs:
1366 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001367 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001368 if method == "GET":
1369 if _id:
1370 admin_query["method"] = "show"
1371 else:
1372 admin_query["method"] = "list"
1373 elif method == "DELETE":
1374 admin_query["method"] = "delete"
1375 else:
1376 admin_query["method"] = "write"
1377 return admin_query
1378
tiernoc94c3df2018-02-09 15:38:54 +01001379 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001380 def default(
1381 self,
1382 main_topic=None,
1383 version=None,
1384 topic=None,
1385 _id=None,
1386 item=None,
1387 *args,
1388 **kwargs
1389 ):
tierno701018c2019-06-25 11:13:14 +00001390 token_info = None
tiernof27c79b2018-03-12 17:08:42 +01001391 outdata = None
1392 _format = None
tierno0f98af52018-03-19 10:28:22 +01001393 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001394 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001395 rollback = []
tierno701018c2019-06-25 11:13:14 +00001396 engine_session = None
tiernoc94c3df2018-02-09 15:38:54 +01001397 try:
tiernob24258a2018-10-04 18:39:49 +02001398 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001399 raise NbiException(
1400 "URL must contain at least 'main_topic/version/topic'",
1401 HTTPStatus.METHOD_NOT_ALLOWED,
1402 )
1403 if main_topic not in (
1404 "admin",
1405 "vnfpkgm",
1406 "nsd",
1407 "nslcm",
1408 "pdu",
1409 "nst",
1410 "nsilcm",
1411 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001412 "vnflcm",
garciadeblas4568a372021-03-24 09:19:48 +01001413 ):
1414 raise NbiException(
1415 "URL main_topic '{}' not supported".format(main_topic),
1416 HTTPStatus.METHOD_NOT_ALLOWED,
1417 )
1418 if version != "v1":
1419 raise NbiException(
1420 "URL version '{}' not supported".format(version),
1421 HTTPStatus.METHOD_NOT_ALLOWED,
1422 )
tiernoc94c3df2018-02-09 15:38:54 +01001423
garciadeblas4568a372021-03-24 09:19:48 +01001424 if (
1425 kwargs
1426 and "METHOD" in kwargs
1427 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1428 ):
tiernof27c79b2018-03-12 17:08:42 +01001429 method = kwargs.pop("METHOD")
1430 else:
1431 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001432
garciadeblas4568a372021-03-24 09:19:48 +01001433 role_permission = self._check_valid_url_method(
1434 method, main_topic, version, topic, _id, item, *args
1435 )
1436 query_string_operations = self._extract_query_string_operations(
1437 kwargs, method
1438 )
tiernob24258a2018-10-04 18:39:49 +02001439 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001440 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001441 token_info = self.authenticator.authorize(
1442 role_permission, query_string_operations, _id
1443 )
tierno12eac3c2020-03-19 23:22:08 +00001444 if main_topic == "admin" and topic == "domains":
1445 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001446 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001447 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001448 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301449
vijay.r35ef2f72019-04-30 17:55:49 +05301450 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001451 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001452
tiernob24258a2018-10-04 18:39:49 +02001453 if main_topic == "nsd":
1454 engine_topic = "nsds"
1455 elif main_topic == "vnfpkgm":
1456 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001457 if topic == "vnfpkg_op_occs":
1458 engine_topic = "vnfpkgops"
1459 if topic == "vnf_packages" and item == "action":
1460 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001461 elif main_topic == "nslcm":
1462 engine_topic = "nsrs"
1463 if topic == "ns_lcm_op_occs":
1464 engine_topic = "nslcmops"
1465 if topic == "vnfrs" or topic == "vnf_instances":
1466 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001467 elif main_topic == "vnflcm":
1468 if topic == "vnf_lcm_op_occs":
1469 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001470 elif main_topic == "nst":
1471 engine_topic = "nsts"
1472 elif main_topic == "nsilcm":
1473 engine_topic = "nsis"
1474 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001475 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001476 elif main_topic == "pdu":
1477 engine_topic = "pdus"
garciadeblas4568a372021-03-24 09:19:48 +01001478 if (
1479 engine_topic == "vims"
1480 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001481 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001482
preethika.p329b8182020-04-22 12:25:39 +05301483 if topic == "subscriptions":
1484 engine_topic = main_topic + "_" + topic
1485
tiernoc94c3df2018-02-09 15:38:54 +01001486 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001487 if item in (
1488 "nsd_content",
1489 "package_content",
1490 "artifacts",
1491 "vnfd",
1492 "nsd",
1493 "nst",
1494 "nst_content",
1495 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001496 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001497 path = "$DESCRIPTOR"
1498 elif args:
1499 path = args
tiernob24258a2018-10-04 18:39:49 +02001500 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001501 path = ()
1502 else:
1503 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001504 file, _format = self.engine.get_file(
1505 engine_session,
1506 engine_topic,
1507 _id,
1508 path,
1509 cherrypy.request.headers.get("Accept"),
1510 )
tiernof27c79b2018-03-12 17:08:42 +01001511 outdata = file
1512 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001513 outdata = self.engine.get_item_list(
1514 engine_session, engine_topic, kwargs, api_req=True
1515 )
tiernoc94c3df2018-02-09 15:38:54 +01001516 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301517 if item == "reports":
1518 # TODO check that project_id (_id in this context) has permissions
1519 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301520 filter_q = None
1521 if "vcaStatusRefresh" in kwargs:
1522 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
garciadeblasf2af4a12023-01-24 16:56:54 +01001523 outdata = self.engine.get_item(
1524 engine_session, engine_topic, _id, filter_q, True
1525 )
delacruzramo271d2002019-12-02 21:00:37 +01001526
tiernof27c79b2018-03-12 17:08:42 +01001527 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001528 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001529 if topic in (
1530 "ns_descriptors_content",
1531 "vnf_packages_content",
1532 "netslice_templates_content",
1533 ):
tiernof27c79b2018-03-12 17:08:42 +01001534 _id = cherrypy.request.headers.get("Transaction-Id")
1535 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001536 _id, _ = self.engine.new_item(
1537 rollback,
1538 engine_session,
1539 engine_topic,
1540 {},
1541 None,
1542 cherrypy.request.headers,
1543 )
1544 completed = self.engine.upload_content(
1545 engine_session,
1546 engine_topic,
1547 _id,
1548 indata,
1549 kwargs,
1550 cherrypy.request.headers,
1551 )
tiernof27c79b2018-03-12 17:08:42 +01001552 if completed:
tiernob24258a2018-10-04 18:39:49 +02001553 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001554 else:
1555 cherrypy.response.headers["Transaction-Id"] = _id
1556 outdata = {"id": _id}
tiernob24258a2018-10-04 18:39:49 +02001557 elif topic == "ns_instances_content":
1558 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001559 _id, _ = self.engine.new_item(
1560 rollback, engine_session, engine_topic, indata, kwargs
1561 )
tiernob24258a2018-10-04 18:39:49 +02001562 # creates nslcmop
1563 indata["lcmOperationType"] = "instantiate"
1564 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001565 nslcmop_id, _ = self.engine.new_item(
1566 rollback, engine_session, "nslcmops", indata, None
1567 )
tiernob24258a2018-10-04 18:39:49 +02001568 self._set_location_header(main_topic, version, topic, _id)
kuuse078f55e2019-05-16 19:24:21 +02001569 outdata = {"id": _id, "nslcmop_id": nslcmop_id}
tiernob24258a2018-10-04 18:39:49 +02001570 elif topic == "ns_instances" and item:
1571 indata["lcmOperationType"] = item
1572 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001573 _id, _ = self.engine.new_item(
1574 rollback, engine_session, "nslcmops", indata, kwargs
1575 )
1576 self._set_location_header(
1577 main_topic, version, "ns_lcm_op_occs", _id
1578 )
tierno65acb4d2018-04-06 16:42:40 +02001579 outdata = {"id": _id}
1580 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001581 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001582 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001583 _id, _ = self.engine.new_item(
1584 rollback, engine_session, engine_topic, indata, kwargs
1585 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001586 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001587 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001588 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001589 nsilcmop_id, _ = self.engine.new_item(
1590 rollback, engine_session, "nsilcmops", indata, kwargs
1591 )
kuuse078f55e2019-05-16 19:24:21 +02001592 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001593 elif topic == "netslice_instances" and item:
1594 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001595 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001596 _id, _ = self.engine.new_item(
1597 rollback, engine_session, "nsilcmops", indata, kwargs
1598 )
1599 self._set_location_header(
1600 main_topic, version, "nsi_lcm_op_occs", _id
1601 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001602 outdata = {"id": _id}
1603 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001604 elif topic == "vnf_packages" and item == "action":
1605 indata["lcmOperationType"] = item
1606 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001607 _id, _ = self.engine.new_item(
1608 rollback, engine_session, "vnfpkgops", indata, kwargs
1609 )
1610 self._set_location_header(
1611 main_topic, version, "vnfpkg_op_occs", _id
1612 )
delacruzramo271d2002019-12-02 21:00:37 +01001613 outdata = {"id": _id}
1614 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301615 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001616 _id, _ = self.engine.new_item(
1617 rollback, engine_session, engine_topic, indata, kwargs
1618 )
preethika.p329b8182020-04-22 12:25:39 +05301619 self._set_location_header(main_topic, version, topic, _id)
1620 link = {}
1621 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001622 outdata = {
1623 "id": _id,
1624 "filter": indata["filter"],
1625 "callbackUri": indata["CallbackUri"],
1626 "_links": link,
1627 }
preethika.p329b8182020-04-22 12:25:39 +05301628 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001629 elif topic == "vnf_instances" and item:
1630 indata["lcmOperationType"] = item
1631 indata["vnfInstanceId"] = _id
garciadeblasf2af4a12023-01-24 16:56:54 +01001632 _id, _ = self.engine.new_item(
1633 rollback, engine_session, "vnflcmops", indata, kwargs
1634 )
1635 self._set_location_header(
1636 main_topic, version, "vnf_lcm_op_occs", _id
1637 )
almagiae47b9132022-05-17 14:12:22 +02001638 outdata = {"id": _id}
1639 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernof27c79b2018-03-12 17:08:42 +01001640 else:
garciadeblas4568a372021-03-24 09:19:48 +01001641 _id, op_id = self.engine.new_item(
1642 rollback,
1643 engine_session,
1644 engine_topic,
1645 indata,
1646 kwargs,
1647 cherrypy.request.headers,
1648 )
tiernob24258a2018-10-04 18:39:49 +02001649 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001650 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00001651 if op_id:
1652 outdata["op_id"] = op_id
1653 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001654 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02001655
tiernoc94c3df2018-02-09 15:38:54 +01001656 elif method == "DELETE":
1657 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001658 outdata = self.engine.del_item_list(
1659 engine_session, engine_topic, kwargs
1660 )
tierno09c073e2018-04-26 13:36:48 +02001661 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01001662 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00001663 # for NS NSI generate an operation
1664 op_id = None
tierno701018c2019-06-25 11:13:14 +00001665 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02001666 nslcmop_desc = {
1667 "lcmOperationType": "terminate",
1668 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001669 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02001670 }
garciadeblas4568a372021-03-24 09:19:48 +01001671 op_id, _ = self.engine.new_item(
1672 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
1673 )
tierno22577432020-04-08 15:16:57 +00001674 if op_id:
1675 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001676 elif (
1677 topic == "netslice_instances_content"
1678 and not engine_session["force"]
1679 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001680 nsilcmop_desc = {
1681 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02001682 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001683 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02001684 }
garciadeblas4568a372021-03-24 09:19:48 +01001685 op_id, _ = self.engine.new_item(
1686 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
1687 )
tierno22577432020-04-08 15:16:57 +00001688 if op_id:
1689 outdata = {"_id": op_id}
1690 # if there is not any deletion in process, delete
1691 if not op_id:
1692 op_id = self.engine.del_item(engine_session, engine_topic, _id)
1693 if op_id:
1694 outdata = {"op_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001695 cherrypy.response.status = (
1696 HTTPStatus.ACCEPTED.value
1697 if op_id
1698 else HTTPStatus.NO_CONTENT.value
1699 )
tierno09c073e2018-04-26 13:36:48 +02001700
tierno7ae10112018-05-18 14:36:02 +02001701 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00001702 op_id = None
tierno701018c2019-06-25 11:13:14 +00001703 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01001704 raise NbiException(
1705 "Nothing to update. Provide payload and/or query string",
1706 HTTPStatus.BAD_REQUEST,
1707 )
1708 if (
1709 item in ("nsd_content", "package_content", "nst_content")
1710 and method == "PUT"
1711 ):
1712 completed = self.engine.upload_content(
1713 engine_session,
1714 engine_topic,
1715 _id,
1716 indata,
1717 kwargs,
1718 cherrypy.request.headers,
1719 )
tiernof27c79b2018-03-12 17:08:42 +01001720 if not completed:
1721 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01001722 else:
garciadeblas4568a372021-03-24 09:19:48 +01001723 op_id = self.engine.edit_item(
1724 engine_session, engine_topic, _id, indata, kwargs
1725 )
tiernobdebce92019-07-01 15:36:49 +00001726
1727 if op_id:
1728 cherrypy.response.status = HTTPStatus.ACCEPTED.value
1729 outdata = {"op_id": op_id}
1730 else:
1731 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
1732 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01001733 else:
garciadeblas4568a372021-03-24 09:19:48 +01001734 raise NbiException(
1735 "Method {} not allowed".format(method),
1736 HTTPStatus.METHOD_NOT_ALLOWED,
1737 )
tiernoa6bb45d2019-06-14 09:45:39 +00001738
1739 # if Role information changes, it is needed to reload the information of roles
1740 if topic == "roles" and method != "GET":
1741 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01001742
garciadeblas4568a372021-03-24 09:19:48 +01001743 if (
1744 topic == "projects"
1745 and method == "DELETE"
1746 or topic in ["users", "roles"]
1747 and method in ["PUT", "PATCH", "DELETE"]
1748 ):
delacruzramoad682a52019-12-10 16:26:34 +01001749 self.authenticator.remove_token_from_cache()
1750
tierno701018c2019-06-25 11:13:14 +00001751 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02001752 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01001753 if isinstance(
1754 e,
1755 (
1756 NbiException,
1757 EngineException,
1758 DbException,
1759 FsException,
1760 MsgException,
1761 AuthException,
1762 ValidationError,
1763 AuthconnException,
1764 ),
1765 ):
tiernob24258a2018-10-04 18:39:49 +02001766 http_code_value = cherrypy.response.status = e.http_code.value
1767 http_code_name = e.http_code.name
1768 cherrypy.log("Exception {}".format(e))
1769 else:
garciadeblas4568a372021-03-24 09:19:48 +01001770 http_code_value = (
1771 cherrypy.response.status
1772 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01001773 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02001774 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02001775 if hasattr(outdata, "close"): # is an open file
1776 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02001777 error_text = str(e)
1778 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02001779 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02001780 try:
tiernocc103432018-10-19 14:10:35 +02001781 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01001782 self.engine.db.set_one(
1783 rollback_item["topic"],
1784 {"_id": rollback_item["_id"]},
1785 rollback_item["content"],
1786 fail_on_empty=False,
1787 )
preethika.p329b8182020-04-22 12:25:39 +05301788 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01001789 self.engine.db.del_list(
1790 rollback_item["topic"],
1791 rollback_item["filter"],
garciadeblas4568a372021-03-24 09:19:48 +01001792 )
tiernocc103432018-10-19 14:10:35 +02001793 else:
garciadeblas4568a372021-03-24 09:19:48 +01001794 self.engine.db.del_one(
1795 rollback_item["topic"],
1796 {"_id": rollback_item["_id"]},
1797 fail_on_empty=False,
1798 )
tierno3ace63c2018-05-03 17:51:43 +02001799 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01001800 rollback_error_text = "Rollback Exception {}: {}".format(
1801 rollback_item, e2
1802 )
tiernob24258a2018-10-04 18:39:49 +02001803 cherrypy.log(rollback_error_text)
1804 error_text += ". " + rollback_error_text
1805 # if isinstance(e, MsgException):
1806 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
1807 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01001808 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02001809 "code": http_code_name,
1810 "status": http_code_value,
1811 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01001812 }
tierno701018c2019-06-25 11:13:14 +00001813 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001814 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00001815 finally:
1816 if token_info:
1817 self._format_login(token_info)
1818 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
1819 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
1820 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01001821 cherrypy.request.login += ";{}={}".format(
1822 logging_id, outdata[logging_id][:36]
1823 )
tiernoc94c3df2018-02-09 15:38:54 +01001824
1825
tiernoc94c3df2018-02-09 15:38:54 +01001826def _start_service():
1827 """
1828 Callback function called when cherrypy.engine starts
1829 Override configuration with env variables
1830 Set database, storage, message configuration
1831 Init database with admin/admin user password
1832 """
tierno932499c2019-01-28 17:28:10 +00001833 global nbi_server
1834 global subscription_thread
tiernoc94c3df2018-02-09 15:38:54 +01001835 cherrypy.log.error("Starting osm_nbi")
1836 # update general cherrypy configuration
1837 update_dict = {}
1838
garciadeblas4568a372021-03-24 09:19:48 +01001839 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01001840 for k, v in environ.items():
1841 if not k.startswith("OSMNBI_"):
1842 continue
tiernoe1281182018-05-22 12:24:36 +02001843 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01001844 if not k2:
1845 continue
1846 try:
1847 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01001848 if k == "OSMNBI_STATIC_DIR":
1849 engine_config["/static"]["tools.staticdir.dir"] = v
1850 engine_config["/static"]["tools.staticdir.on"] = True
1851 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
1852 update_dict["server.socket_port"] = int(v)
1853 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
1854 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02001855 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01001856 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001857 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02001858 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001859 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01001860 engine_config[k1][k2] = int(v)
1861 else:
1862 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001863
tiernoc94c3df2018-02-09 15:38:54 +01001864 except ValueError as e:
1865 cherrypy.log.error("Ignoring environ '{}': " + str(e))
1866 except Exception as e:
garciadeblasf2af4a12023-01-24 16:56:54 +01001867 cherrypy.log(
1868 "WARNING: skipping environ '{}' on exception '{}'".format(k, e)
1869 )
tiernoc94c3df2018-02-09 15:38:54 +01001870
1871 if update_dict:
1872 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02001873 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001874
1875 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01001876 log_format_simple = (
1877 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
1878 )
1879 log_formatter_simple = logging.Formatter(
1880 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
1881 )
tiernoc94c3df2018-02-09 15:38:54 +01001882 logger_server = logging.getLogger("cherrypy.error")
1883 logger_access = logging.getLogger("cherrypy.access")
1884 logger_cherry = logging.getLogger("cherrypy")
1885 logger_nbi = logging.getLogger("nbi")
1886
tiernof5298be2018-05-16 14:43:57 +02001887 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01001888 file_handler = logging.handlers.RotatingFileHandler(
1889 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
1890 )
tiernoc94c3df2018-02-09 15:38:54 +01001891 file_handler.setFormatter(log_formatter_simple)
1892 logger_cherry.addHandler(file_handler)
1893 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02001894 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01001895 for format_, logger in {
1896 "nbi.server %(filename)s:%(lineno)s": logger_server,
1897 "nbi.access %(filename)s:%(lineno)s": logger_access,
1898 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
1899 }.items():
tiernob24258a2018-10-04 18:39:49 +02001900 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01001901 log_formatter_cherry = logging.Formatter(
1902 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
1903 )
tiernob24258a2018-10-04 18:39:49 +02001904 str_handler = logging.StreamHandler()
1905 str_handler.setFormatter(log_formatter_cherry)
1906 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01001907
tiernof5298be2018-05-16 14:43:57 +02001908 if engine_config["global"].get("log.level"):
1909 logger_cherry.setLevel(engine_config["global"]["log.level"])
1910 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01001911
1912 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01001913 for k1, logname in {
1914 "message": "nbi.msg",
1915 "database": "nbi.db",
1916 "storage": "nbi.fs",
1917 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01001918 engine_config[k1]["logger_name"] = logname
1919 logger_module = logging.getLogger(logname)
1920 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01001921 file_handler = logging.handlers.RotatingFileHandler(
1922 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
1923 )
tiernoc94c3df2018-02-09 15:38:54 +01001924 file_handler.setFormatter(log_formatter_simple)
1925 logger_module.addHandler(file_handler)
1926 if "loglevel" in engine_config[k1]:
1927 logger_module.setLevel(engine_config[k1]["loglevel"])
1928 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01001929 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
1930 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
1931 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
1932 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
1933 target_version=auth_database_version
1934 )
tiernobee508e2019-01-21 11:21:49 +00001935
tierno932499c2019-01-28 17:28:10 +00001936 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01001937 subscription_thread = SubscriptionThread(
1938 config=engine_config, engine=nbi_server.engine
1939 )
tierno932499c2019-01-28 17:28:10 +00001940 subscription_thread.start()
1941 # Do not capture except SubscriptionException
1942
tiernob2e48bd2020-02-04 15:47:18 +00001943 backend = engine_config["authentication"]["backend"]
garciadeblas4568a372021-03-24 09:19:48 +01001944 cherrypy.log.error(
1945 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
1946 nbi_version, nbi_version_date, backend
1947 )
1948 )
tiernoc94c3df2018-02-09 15:38:54 +01001949
1950
1951def _stop_service():
1952 """
1953 Callback function called when cherrypy.engine stops
1954 TODO: Ending database connections.
1955 """
tierno932499c2019-01-28 17:28:10 +00001956 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01001957 if subscription_thread:
1958 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00001959 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01001960 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01001961 cherrypy.log.error("Stopping osm_nbi")
1962
tierno2236d202018-05-16 19:05:16 +02001963
tiernof5298be2018-05-16 14:43:57 +02001964def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00001965 global nbi_server
tiernoc94c3df2018-02-09 15:38:54 +01001966 # conf = {
1967 # '/': {
1968 # #'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
1969 # 'tools.sessions.on': True,
1970 # 'tools.response_headers.on': True,
1971 # # 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
1972 # }
1973 # }
1974 # cherrypy.Server.ssl_module = 'builtin'
1975 # cherrypy.Server.ssl_certificate = "http/cert.pem"
1976 # cherrypy.Server.ssl_private_key = "http/privkey.pem"
1977 # cherrypy.Server.thread_pool = 10
1978 # cherrypy.config.update({'Server.socket_port': config["port"], 'Server.socket_host': config["host"]})
1979
1980 # cherrypy.config.update({'tools.auth_basic.on': True,
1981 # 'tools.auth_basic.realm': 'localhost',
1982 # 'tools.auth_basic.checkpassword': validate_password})
tierno932499c2019-01-28 17:28:10 +00001983 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01001984 cherrypy.engine.subscribe("start", _start_service)
1985 cherrypy.engine.subscribe("stop", _stop_service)
1986 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02001987
1988
1989def usage():
garciadeblas4568a372021-03-24 09:19:48 +01001990 print(
1991 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02001992 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
1993 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01001994 """.format(
1995 sys.argv[0]
1996 )
1997 )
tierno2236d202018-05-16 19:05:16 +02001998 # --log-socket-host HOST: send logs to this host")
1999 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002000
2001
garciadeblas4568a372021-03-24 09:19:48 +01002002if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002003 try:
2004 # load parameters and configuration
2005 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2006 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2007 config_file = None
2008 for o, a in opts:
2009 if o in ("-h", "--help"):
2010 usage()
2011 sys.exit()
2012 elif o in ("-c", "--config"):
2013 config_file = a
2014 # elif o == "--log-socket-port":
2015 # log_socket_port = a
2016 # elif o == "--log-socket-host":
2017 # log_socket_host = a
2018 # elif o == "--log-file":
2019 # log_file = a
2020 else:
2021 assert False, "Unhandled option"
2022 if config_file:
2023 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002024 print(
2025 "configuration file '{}' that not exist".format(config_file),
2026 file=sys.stderr,
2027 )
tiernof5298be2018-05-16 14:43:57 +02002028 exit(1)
2029 else:
garciadeblas4568a372021-03-24 09:19:48 +01002030 for config_file in (
2031 __file__[: __file__.rfind(".")] + ".cfg",
2032 "./nbi.cfg",
2033 "/etc/osm/nbi.cfg",
2034 ):
tiernof5298be2018-05-16 14:43:57 +02002035 if path.isfile(config_file):
2036 break
2037 else:
garciadeblas4568a372021-03-24 09:19:48 +01002038 print(
2039 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2040 file=sys.stderr,
2041 )
tiernof5298be2018-05-16 14:43:57 +02002042 exit(1)
2043 nbi(config_file)
2044 except getopt.GetoptError as e:
2045 print(str(e), file=sys.stderr)
2046 # usage()
2047 exit(1)