blob: 80548ce571c85c5e364750deb69cc1b98de71f0a [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
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000118 /paas O5 O5
119 /<id> O5 O5 O5
delacruzramofe598fe2019-10-23 18:25:11 +0200120 /k8srepos O O
121 /<id> O O
Felipe Vicensb66b0412020-05-06 10:11:00 +0200122 /osmrepos O O
123 /<id> O O
tiernoc94c3df2018-02-09 15:38:54 +0100124
garciadeblas9750c5a2018-10-15 16:20:35 +0200125 /nst/v1 O O
126 /netslice_templates_content O O
127 /<nstInfoId> O O O O
128 /netslice_templates O O
129 /<nstInfoId> O O O
130 /nst_content O O
131 /nst O
132 /artifacts[/<artifactPath>] O
133 /subscriptions X X
134 /<subscriptionId> X X
135
136 /nsilcm/v1
137 /netslice_instances_content O O
138 /<SliceInstanceId> O O
139 /netslice_instances O O
140 /<SliceInstanceId> O O
141 instantiate O
142 terminate O
143 action O
144 /nsi_lcm_op_occs O O
145 /<nsiLcmOpOccId> O O O
146 /subscriptions X X
147 /<subscriptionId> X X
148
tierno2236d202018-05-16 19:05:16 +0200149query string:
150 Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force.
tierno36ec8602018-11-02 17:27:11 +0100151 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
152 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
153 op := "eq" | "neq" (or "ne") | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
154 attrName := string
tierno2236d202018-05-16 19:05:16 +0200155 For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any
156 item of the array, that is, pass if any item of the array pass the filter.
157 It allows both ne and neq for not equal
158 TODO: 4.3.3 Attribute selectors
159 all_fields, fields=x,y,.., exclude_default, exclude_fields=x,y,...
tiernoc94c3df2018-02-09 15:38:54 +0100160 (none) … same as “exclude_default”
161 all_fields … all attributes.
tierno2236d202018-05-16 19:05:16 +0200162 fields=<list> … all attributes except all complex attributes with minimum cardinality of zero that are not
163 conditionally mandatory, and that are not provided in <list>.
164 exclude_fields=<list> … all attributes except those complex attributes with a minimum cardinality of zero that
165 are not conditionally mandatory, and that are provided in <list>.
166 exclude_default … all attributes except those complex attributes with a minimum cardinality of zero that are not
167 conditionally mandatory, and that are part of the "default exclude set" defined in the present specification for
168 the particular resource
169 exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality
170 of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the
171 present specification for the particular resource, but that are not part of <list>
tierno65ca36d2019-02-12 19:27:52 +0100172 Additionally it admits some administrator values:
173 FORCE: To force operations skipping dependency checkings
174 ADMIN: To act as an administrator or a different project
175 PUBLIC: To get public descriptors or set a descriptor as public
176 SET_PROJECT: To make a descriptor available for other project
beierlmbc5a5242022-05-17 21:25:29 -0400177
tiernoc94c3df2018-02-09 15:38:54 +0100178Header field name Reference Example Descriptions
179 Accept IETF RFC 7231 [19] application/json Content-Types that are acceptable for the response.
180 This header field shall be present if the response is expected to have a non-empty message body.
181 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the request.
182 This header field shall be present if the request has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200183 Authorization IETF RFC 7235 [22] Bearer mF_9.B5f-4.1JqM The authorization token for the request.
184 Details are specified in clause 4.5.3.
tiernoc94c3df2018-02-09 15:38:54 +0100185 Range IETF RFC 7233 [21] 1000-2000 Requested range of bytes from a file
186Header field name Reference Example Descriptions
187 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the response.
188 This header field shall be present if the response has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200189 Location IETF RFC 7231 [19] http://www.example.com/vnflcm/v1/vnf_instances/123 Used in redirection, or when a
190 new resource has been created.
tiernoc94c3df2018-02-09 15:38:54 +0100191 This header field shall be present if the response status code is 201 or 3xx.
tierno2236d202018-05-16 19:05:16 +0200192 In the present document this header field is also used if the response status code is 202 and a new resource was
193 created.
194 WWW-Authenticate IETF RFC 7235 [22] Bearer realm="example" Challenge if the corresponding HTTP request has not
195 provided authorization, or error details if the corresponding HTTP request has provided an invalid authorization
196 token.
197 Accept-Ranges IETF RFC 7233 [21] bytes Used by the Server to signal whether or not it supports ranges for
198 certain resources.
199 Content-Range IETF RFC 7233 [21] bytes 21010-47021/ 47022 Signals the byte range that is contained in the
200 response, and the total length of the file.
tiernoc94c3df2018-02-09 15:38:54 +0100201 Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT
tiernoc94c3df2018-02-09 15:38:54 +0100202"""
203
tierno701018c2019-06-25 11:13:14 +0000204valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC")
205# ^ Contains possible administrative query string words:
206# ADMIN=True(by default)|Project|Project-list: See all elements, or elements of a project
207# (not owned by my session project).
208# PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public
209# FORCE=True(by default)|False: Force edition/deletion operations
210# SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio
211
212valid_url_methods = {
213 # contains allowed URL and methods, and the role_permission name
214 "admin": {
215 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100216 "tokens": {
217 "METHODS": ("GET", "POST", "DELETE"),
218 "ROLE_PERMISSION": "tokens:",
219 "<ID>": {"METHODS": ("GET", "DELETE"), "ROLE_PERMISSION": "tokens:id:"},
220 },
221 "users": {
222 "METHODS": ("GET", "POST"),
223 "ROLE_PERMISSION": "users:",
224 "<ID>": {
225 "METHODS": ("GET", "DELETE", "PATCH"),
226 "ROLE_PERMISSION": "users:id:",
227 },
228 },
229 "projects": {
230 "METHODS": ("GET", "POST"),
231 "ROLE_PERMISSION": "projects:",
232 "<ID>": {
233 "METHODS": ("GET", "DELETE", "PATCH"),
234 "ROLE_PERMISSION": "projects:id:",
235 },
236 },
237 "roles": {
238 "METHODS": ("GET", "POST"),
239 "ROLE_PERMISSION": "roles:",
240 "<ID>": {
241 "METHODS": ("GET", "DELETE", "PATCH"),
242 "ROLE_PERMISSION": "roles:id:",
243 },
244 },
245 "vims": {
246 "METHODS": ("GET", "POST"),
247 "ROLE_PERMISSION": "vims:",
248 "<ID>": {
249 "METHODS": ("GET", "DELETE", "PATCH"),
250 "ROLE_PERMISSION": "vims:id:",
251 },
252 },
253 "vim_accounts": {
254 "METHODS": ("GET", "POST"),
255 "ROLE_PERMISSION": "vim_accounts:",
256 "<ID>": {
257 "METHODS": ("GET", "DELETE", "PATCH"),
258 "ROLE_PERMISSION": "vim_accounts:id:",
259 },
260 },
261 "wim_accounts": {
262 "METHODS": ("GET", "POST"),
263 "ROLE_PERMISSION": "wim_accounts:",
264 "<ID>": {
265 "METHODS": ("GET", "DELETE", "PATCH"),
266 "ROLE_PERMISSION": "wim_accounts:id:",
267 },
268 },
269 "sdns": {
270 "METHODS": ("GET", "POST"),
271 "ROLE_PERMISSION": "sdn_controllers:",
272 "<ID>": {
273 "METHODS": ("GET", "DELETE", "PATCH"),
274 "ROLE_PERMISSION": "sdn_controllers:id:",
275 },
276 },
277 "k8sclusters": {
278 "METHODS": ("GET", "POST"),
279 "ROLE_PERMISSION": "k8sclusters:",
280 "<ID>": {
281 "METHODS": ("GET", "DELETE", "PATCH"),
282 "ROLE_PERMISSION": "k8sclusters:id:",
283 },
284 },
285 "vca": {
286 "METHODS": ("GET", "POST"),
287 "ROLE_PERMISSION": "vca:",
288 "<ID>": {
289 "METHODS": ("GET", "DELETE", "PATCH"),
290 "ROLE_PERMISSION": "vca:id:",
291 },
292 },
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000293 "paas": {
294 "METHODS": ("GET", "POST"),
295 "ROLE_PERMISSION": "paas:",
296 "<ID>": {
297 "METHODS": ("GET", "DELETE", "PATCH"),
298 "ROLE_PERMISSION": "paas:id:",
299 },
300 },
garciadeblas4568a372021-03-24 09:19:48 +0100301 "k8srepos": {
302 "METHODS": ("GET", "POST"),
303 "ROLE_PERMISSION": "k8srepos:",
304 "<ID>": {
305 "METHODS": ("GET", "DELETE"),
306 "ROLE_PERMISSION": "k8srepos:id:",
307 },
308 },
309 "osmrepos": {
310 "METHODS": ("GET", "POST"),
311 "ROLE_PERMISSION": "osmrepos:",
312 "<ID>": {
313 "METHODS": ("GET", "DELETE", "PATCH"),
314 "ROLE_PERMISSION": "osmrepos:id:",
315 },
316 },
317 "domains": {
318 "METHODS": ("GET",),
319 "ROLE_PERMISSION": "domains:",
320 },
tierno701018c2019-06-25 11:13:14 +0000321 }
322 },
323 "pdu": {
324 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100325 "pdu_descriptors": {
326 "METHODS": ("GET", "POST"),
327 "ROLE_PERMISSION": "pduds:",
328 "<ID>": {
329 "METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"),
330 "ROLE_PERMISSION": "pduds:id:",
331 },
332 },
tierno701018c2019-06-25 11:13:14 +0000333 }
334 },
335 "nsd": {
336 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100337 "ns_descriptors_content": {
338 "METHODS": ("GET", "POST"),
339 "ROLE_PERMISSION": "nsds:",
340 "<ID>": {
341 "METHODS": ("GET", "PUT", "DELETE"),
342 "ROLE_PERMISSION": "nsds:id:",
343 },
344 },
345 "ns_descriptors": {
346 "METHODS": ("GET", "POST"),
347 "ROLE_PERMISSION": "nsds:",
348 "<ID>": {
349 "METHODS": ("GET", "DELETE", "PATCH"),
350 "ROLE_PERMISSION": "nsds:id:",
351 "nsd_content": {
352 "METHODS": ("GET", "PUT"),
353 "ROLE_PERMISSION": "nsds:id:content:",
354 },
355 "nsd": {
356 "METHODS": ("GET",), # descriptor inside package
357 "ROLE_PERMISSION": "nsds:id:content:",
358 },
359 "artifacts": {
360 "METHODS": ("GET",),
361 "ROLE_PERMISSION": "nsds:id:nsd_artifact:",
362 "*": None,
363 },
364 },
365 },
366 "pnf_descriptors": {
367 "TODO": ("GET", "POST"),
368 "<ID>": {
369 "TODO": ("GET", "DELETE", "PATCH"),
370 "pnfd_content": {"TODO": ("GET", "PUT")},
371 },
372 },
373 "subscriptions": {
374 "TODO": ("GET", "POST"),
375 "<ID>": {"TODO": ("GET", "DELETE")},
376 },
tierno701018c2019-06-25 11:13:14 +0000377 }
378 },
379 "vnfpkgm": {
380 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100381 "vnf_packages_content": {
382 "METHODS": ("GET", "POST"),
383 "ROLE_PERMISSION": "vnfds:",
384 "<ID>": {
385 "METHODS": ("GET", "PUT", "DELETE"),
386 "ROLE_PERMISSION": "vnfds:id:",
387 },
388 },
389 "vnf_packages": {
390 "METHODS": ("GET", "POST"),
391 "ROLE_PERMISSION": "vnfds:",
392 "<ID>": {
393 "METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo
394 "ROLE_PERMISSION": "vnfds:id:",
395 "package_content": {
396 "METHODS": ("GET", "PUT"), # package
397 "ROLE_PERMISSION": "vnfds:id:",
398 "upload_from_uri": {
399 "METHODS": (),
400 "TODO": ("POST",),
401 "ROLE_PERMISSION": "vnfds:id:upload:",
402 },
403 },
404 "vnfd": {
405 "METHODS": ("GET",), # descriptor inside package
406 "ROLE_PERMISSION": "vnfds:id:content:",
407 },
408 "artifacts": {
409 "METHODS": ("GET",),
410 "ROLE_PERMISSION": "vnfds:id:vnfd_artifact:",
411 "*": None,
412 },
413 "action": {
414 "METHODS": ("POST",),
415 "ROLE_PERMISSION": "vnfds:id:action:",
416 },
417 },
418 },
419 "subscriptions": {
420 "TODO": ("GET", "POST"),
421 "<ID>": {"TODO": ("GET", "DELETE")},
422 },
423 "vnfpkg_op_occs": {
424 "METHODS": ("GET",),
425 "ROLE_PERMISSION": "vnfds:vnfpkgops:",
426 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnfds:vnfpkgops:id:"},
427 },
tierno701018c2019-06-25 11:13:14 +0000428 }
429 },
430 "nslcm": {
431 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100432 "ns_instances_content": {
433 "METHODS": ("GET", "POST"),
434 "ROLE_PERMISSION": "ns_instances:",
435 "<ID>": {
436 "METHODS": ("GET", "DELETE"),
437 "ROLE_PERMISSION": "ns_instances:id:",
438 },
439 },
440 "ns_instances": {
441 "METHODS": ("GET", "POST"),
442 "ROLE_PERMISSION": "ns_instances:",
443 "<ID>": {
444 "METHODS": ("GET", "DELETE"),
445 "ROLE_PERMISSION": "ns_instances:id:",
garciadeblas0964edf2022-02-11 00:43:44 +0100446 "heal": {
447 "METHODS": ("POST",),
448 "ROLE_PERMISSION": "ns_instances:id:heal:",
449 },
garciadeblas4568a372021-03-24 09:19:48 +0100450 "scale": {
451 "METHODS": ("POST",),
452 "ROLE_PERMISSION": "ns_instances:id:scale:",
453 },
454 "terminate": {
455 "METHODS": ("POST",),
456 "ROLE_PERMISSION": "ns_instances:id:terminate:",
457 },
458 "instantiate": {
459 "METHODS": ("POST",),
460 "ROLE_PERMISSION": "ns_instances:id:instantiate:",
461 },
elumalai8e3806c2022-04-28 17:26:24 +0530462 "migrate": {
463 "METHODS": ("POST",),
464 "ROLE_PERMISSION": "ns_instances:id:migrate:",
465 },
garciadeblas4568a372021-03-24 09:19:48 +0100466 "action": {
467 "METHODS": ("POST",),
468 "ROLE_PERMISSION": "ns_instances:id:action:",
469 },
aticig544a2ae2022-04-05 09:00:17 +0300470 "update": {
471 "METHODS": ("POST",),
472 "ROLE_PERMISSION": "ns_instances:id:update:",
473 },
govindarajul519da482022-04-29 19:05:22 +0530474 "verticalscale": {
475 "METHODS": ("POST",),
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000476 "ROLE_PERMISSION": "ns_instances:id:verticalscale:",
477 },
garciadeblas4568a372021-03-24 09:19:48 +0100478 },
479 },
480 "ns_lcm_op_occs": {
481 "METHODS": ("GET",),
482 "ROLE_PERMISSION": "ns_instances:opps:",
483 "<ID>": {
484 "METHODS": ("GET",),
485 "ROLE_PERMISSION": "ns_instances:opps:id:",
486 },
487 },
488 "vnfrs": {
489 "METHODS": ("GET",),
490 "ROLE_PERMISSION": "vnf_instances:",
491 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
492 },
493 "vnf_instances": {
494 "METHODS": ("GET",),
495 "ROLE_PERMISSION": "vnf_instances:",
496 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
497 },
498 "subscriptions": {
499 "METHODS": ("GET", "POST"),
500 "ROLE_PERMISSION": "ns_subscriptions:",
501 "<ID>": {
502 "METHODS": ("GET", "DELETE"),
503 "ROLE_PERMISSION": "ns_subscriptions:id:",
504 },
505 },
tierno701018c2019-06-25 11:13:14 +0000506 }
507 },
almagiae47b9132022-05-17 14:12:22 +0200508 "vnflcm": {
509 "v1": {
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000510 "vnf_instances": {
511 "METHODS": ("GET", "POST"),
512 "ROLE_PERMISSION": "vnflcm_instances:",
513 "<ID>": {
514 "METHODS": ("GET", "DELETE"),
515 "ROLE_PERMISSION": "vnflcm_instances:id:",
516 "scale": {
517 "METHODS": ("POST",),
518 "ROLE_PERMISSION": "vnflcm_instances:id:scale:",
519 },
520 "terminate": {
521 "METHODS": ("POST",),
522 "ROLE_PERMISSION": "vnflcm_instances:id:terminate:",
523 },
524 "instantiate": {
525 "METHODS": ("POST",),
526 "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:",
527 },
528 },
529 },
530 "vnf_lcm_op_occs": {
531 "METHODS": ("GET",),
532 "ROLE_PERMISSION": "vnf_instances:opps:",
533 "<ID>": {
534 "METHODS": ("GET",),
535 "ROLE_PERMISSION": "vnf_instances:opps:id:",
536 },
537 },
538 "subscriptions": {
539 "METHODS": ("GET", "POST"),
540 "ROLE_PERMISSION": "vnflcm_subscriptions:",
541 "<ID>": {
542 "METHODS": ("GET", "DELETE"),
543 "ROLE_PERMISSION": "vnflcm_subscriptions:id:",
544 },
545 },
almagiae47b9132022-05-17 14:12:22 +0200546 }
547 },
tierno701018c2019-06-25 11:13:14 +0000548 "nst": {
549 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100550 "netslice_templates_content": {
551 "METHODS": ("GET", "POST"),
552 "ROLE_PERMISSION": "slice_templates:",
553 "<ID>": {
554 "METHODS": ("GET", "PUT", "DELETE"),
555 "ROLE_PERMISSION": "slice_templates:id:",
556 },
557 },
558 "netslice_templates": {
559 "METHODS": ("GET", "POST"),
560 "ROLE_PERMISSION": "slice_templates:",
561 "<ID>": {
562 "METHODS": ("GET", "DELETE"),
563 "TODO": ("PATCH",),
564 "ROLE_PERMISSION": "slice_templates:id:",
565 "nst_content": {
566 "METHODS": ("GET", "PUT"),
567 "ROLE_PERMISSION": "slice_templates:id:content:",
568 },
569 "nst": {
570 "METHODS": ("GET",), # descriptor inside package
571 "ROLE_PERMISSION": "slice_templates:id:content:",
572 },
573 "artifacts": {
574 "METHODS": ("GET",),
575 "ROLE_PERMISSION": "slice_templates:id:content:",
576 "*": None,
577 },
578 },
579 },
580 "subscriptions": {
581 "TODO": ("GET", "POST"),
582 "<ID>": {"TODO": ("GET", "DELETE")},
583 },
tierno701018c2019-06-25 11:13:14 +0000584 }
585 },
586 "nsilcm": {
587 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100588 "netslice_instances_content": {
589 "METHODS": ("GET", "POST"),
590 "ROLE_PERMISSION": "slice_instances:",
591 "<ID>": {
592 "METHODS": ("GET", "DELETE"),
593 "ROLE_PERMISSION": "slice_instances:id:",
594 },
595 },
596 "netslice_instances": {
597 "METHODS": ("GET", "POST"),
598 "ROLE_PERMISSION": "slice_instances:",
599 "<ID>": {
600 "METHODS": ("GET", "DELETE"),
601 "ROLE_PERMISSION": "slice_instances:id:",
602 "terminate": {
603 "METHODS": ("POST",),
604 "ROLE_PERMISSION": "slice_instances:id:terminate:",
605 },
606 "instantiate": {
607 "METHODS": ("POST",),
608 "ROLE_PERMISSION": "slice_instances:id:instantiate:",
609 },
610 "action": {
611 "METHODS": ("POST",),
612 "ROLE_PERMISSION": "slice_instances:id:action:",
613 },
614 },
615 },
616 "nsi_lcm_op_occs": {
617 "METHODS": ("GET",),
618 "ROLE_PERMISSION": "slice_instances:opps:",
619 "<ID>": {
620 "METHODS": ("GET",),
621 "ROLE_PERMISSION": "slice_instances:opps:id:",
622 },
623 },
tierno701018c2019-06-25 11:13:14 +0000624 }
625 },
626 "nspm": {
627 "v1": {
628 "pm_jobs": {
629 "<ID>": {
630 "reports": {
garciadeblas4568a372021-03-24 09:19:48 +0100631 "<ID>": {
632 "METHODS": ("GET",),
633 "ROLE_PERMISSION": "reports:id:",
634 }
tierno701018c2019-06-25 11:13:14 +0000635 }
636 },
637 },
638 },
639 },
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000640 "nsfm": {
641 "v1": {
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000642 "alarms": {
643 "METHODS": ("GET", "PATCH"),
644 "ROLE_PERMISSION": "alarms:",
645 "<ID>": {
646 "METHODS": ("GET", "PATCH"),
647 "ROLE_PERMISSION": "alarms:id:",
648 },
649 }
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000650 },
651 },
tierno701018c2019-06-25 11:13:14 +0000652}
653
tiernoc94c3df2018-02-09 15:38:54 +0100654
655class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100656 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
657 Exception.__init__(self, message)
658 self.http_code = http_code
659
660
661class Server(object):
662 instance = 0
663 # to decode bytes to str
664 reader = getreader("utf-8")
665
666 def __init__(self):
667 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000668 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100669 self.engine = Engine(self.authenticator)
tiernoc94c3df2018-02-09 15:38:54 +0100670
tiernoc94c3df2018-02-09 15:38:54 +0100671 def _format_in(self, kwargs):
672 try:
673 indata = None
674 if cherrypy.request.body.length:
675 error_text = "Invalid input format "
676
677 if "Content-Type" in cherrypy.request.headers:
678 if "application/json" in cherrypy.request.headers["Content-Type"]:
679 error_text = "Invalid json format "
680 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100681 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100682 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
683 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100684 indata = yaml.load(
685 cherrypy.request.body, Loader=yaml.SafeLoader
686 )
gcalvinode4adfe2018-10-30 11:46:09 +0100687 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100688 elif (
689 "application/binary" in cherrypy.request.headers["Content-Type"]
690 or "application/gzip"
691 in cherrypy.request.headers["Content-Type"]
692 or "application/zip" in cherrypy.request.headers["Content-Type"]
693 or "text/plain" in cherrypy.request.headers["Content-Type"]
694 ):
tiernof27c79b2018-03-12 17:08:42 +0100695 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100696 elif (
697 "multipart/form-data"
698 in cherrypy.request.headers["Content-Type"]
699 ):
tiernoc94c3df2018-02-09 15:38:54 +0100700 if "descriptor_file" in kwargs:
701 filecontent = kwargs.pop("descriptor_file")
702 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100703 raise NbiException(
704 "empty file or content", HTTPStatus.BAD_REQUEST
705 )
tiernof27c79b2018-03-12 17:08:42 +0100706 indata = filecontent.file # .read()
tiernoc94c3df2018-02-09 15:38:54 +0100707 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100708 cherrypy.request.headers[
709 "Content-Type"
710 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100711 else:
712 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
713 # "Only 'Content-Type' of type 'application/json' or
714 # 'application/yaml' for input format are available")
715 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100716 indata = yaml.load(
717 cherrypy.request.body, Loader=yaml.SafeLoader
718 )
gcalvinode4adfe2018-10-30 11:46:09 +0100719 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100720 else:
721 error_text = "Invalid yaml format "
delacruzramob19cadc2019-10-08 10:18:02 +0200722 indata = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
gcalvinode4adfe2018-10-30 11:46:09 +0100723 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100724 if not indata:
725 indata = {}
726
tiernoc94c3df2018-02-09 15:38:54 +0100727 format_yaml = False
728 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
729 format_yaml = True
730
731 for k, v in kwargs.items():
732 if isinstance(v, str):
733 if v == "":
734 kwargs[k] = None
735 elif format_yaml:
736 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200737 kwargs[k] = yaml.load(v, Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200738 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100739 pass
garciadeblas4568a372021-03-24 09:19:48 +0100740 elif (
741 k.endswith(".gt")
742 or k.endswith(".lt")
743 or k.endswith(".gte")
744 or k.endswith(".lte")
745 ):
tiernoc94c3df2018-02-09 15:38:54 +0100746 try:
747 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200748 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100749 try:
750 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200751 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100752 pass
753 elif v.find(",") > 0:
754 kwargs[k] = v.split(",")
755 elif isinstance(v, (list, tuple)):
756 for index in range(0, len(v)):
757 if v[index] == "":
758 v[index] = None
759 elif format_yaml:
760 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200761 v[index] = yaml.load(v[index], Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200762 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100763 pass
764
tiernof27c79b2018-03-12 17:08:42 +0100765 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100766 except (ValueError, yaml.YAMLError) as exc:
767 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
768 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100769 raise NbiException(
770 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
771 )
tiernob92094f2018-05-11 13:44:22 +0200772 except Exception as exc:
773 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100774
775 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000776 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100777 """
778 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100779 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000780 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000781 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100782 :return: None
783 """
tierno0f98af52018-03-19 10:28:22 +0100784 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100785 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100786 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100787 return html.format(
788 data, cherrypy.request, cherrypy.response, token_info
789 )
tierno09c073e2018-04-26 13:36:48 +0200790 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100791 return
792 elif hasattr(data, "read"): # file object
793 if _format:
794 cherrypy.response.headers["Content-Type"] = _format
795 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100796 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100797 else:
garciadeblas4568a372021-03-24 09:19:48 +0100798 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100799 # TODO check that cherrypy close file. If not implement pending things to close per thread next
800 return data
tierno0f98af52018-03-19 10:28:22 +0100801 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000802 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100803 return html.format(
804 data, cherrypy.request, cherrypy.response, token_info
805 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000806 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100807 pass
garciadeblas4568a372021-03-24 09:19:48 +0100808 elif "application/json" in accept or (
809 cherrypy.response.status and cherrypy.response.status >= 300
810 ):
811 cherrypy.response.headers[
812 "Content-Type"
813 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000814 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100815 return a.encode("utf8")
816 cherrypy.response.headers["Content-Type"] = "application/yaml"
817 return yaml.safe_dump(
818 data,
819 explicit_start=True,
820 indent=4,
821 default_flow_style=False,
822 tags=False,
823 encoding="utf-8",
824 allow_unicode=True,
825 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +0100826
827 @cherrypy.expose
828 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +0000829 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100830 try:
831 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +0000832 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +0100833 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +0100834 else:
garciadeblas4568a372021-03-24 09:19:48 +0100835 raise cherrypy.HTTPError(
836 HTTPStatus.METHOD_NOT_ALLOWED.value,
837 "Method {} not allowed for tokens".format(cherrypy.request.method),
838 )
tiernoc94c3df2018-02-09 15:38:54 +0100839
tierno701018c2019-06-25 11:13:14 +0000840 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100841
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100842 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +0000843 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +0100844 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +0000845 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100846
847 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +0200848 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +0200849 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +0200850 try:
851 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +0100852 raise NbiException(
853 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
854 )
tierno55945e72018-04-06 16:40:27 +0200855 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +0100856 raise NbiException(
857 "Invalid URL or query string for version",
858 HTTPStatus.METHOD_NOT_ALLOWED,
859 )
tierno9c630112019-08-29 14:21:41 +0000860 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +0000861 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
862 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +0200863 except NbiException as e:
864 cherrypy.response.status = e.http_code.value
865 problem_details = {
866 "code": e.http_code.name,
867 "status": e.http_code.value,
868 "detail": str(e),
869 }
870 return self._format_out(problem_details, None)
871
tierno12eac3c2020-03-19 23:22:08 +0000872 def domain(self):
873 try:
874 domains = {
garciadeblas4568a372021-03-24 09:19:48 +0100875 "user_domain_name": cherrypy.tree.apps["/osm"]
876 .config["authentication"]
877 .get("user_domain_name"),
878 "project_domain_name": cherrypy.tree.apps["/osm"]
879 .config["authentication"]
880 .get("project_domain_name"),
881 }
tierno12eac3c2020-03-19 23:22:08 +0000882 return self._format_out(domains)
883 except NbiException as e:
884 cherrypy.response.status = e.http_code.value
885 problem_details = {
886 "code": e.http_code.name,
887 "status": e.http_code.value,
888 "detail": str(e),
889 }
890 return self._format_out(problem_details, None)
891
tiernoa5035702019-07-29 08:54:42 +0000892 @staticmethod
893 def _format_login(token_info):
894 """
895 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
896 log this information
897 :param token_info: Dictionary with token content
898 :return: None
899 """
900 cherrypy.request.login = token_info.get("username", "-")
901 if token_info.get("project_name"):
902 cherrypy.request.login += "/" + token_info["project_name"]
903 if token_info.get("id"):
904 cherrypy.request.login += ";session=" + token_info["id"][0:12]
905
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000906 # NS Fault Management
907 @cherrypy.expose
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000908 def nsfm(
909 self,
910 version=None,
911 topic=None,
912 uuid=None,
913 project_name=None,
914 ns_id=None,
915 *args,
916 **kwargs
917 ):
918 if topic == "alarms":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000919 try:
920 method = cherrypy.request.method
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000921 role_permission = self._check_valid_url_method(
922 method, "nsfm", version, topic, None, None, *args
923 )
924 query_string_operations = self._extract_query_string_operations(
925 kwargs, method
926 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000927
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000928 self.authenticator.authorize(
929 role_permission, query_string_operations, None
930 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000931
932 # to handle get request
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000933 if cherrypy.request.method == "GET":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000934 # if request is on basis of uuid
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000935 if uuid and uuid != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000936 try:
937 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000938 alarm_action = self.engine.db.get_one(
939 "alarms_action", {"uuid": uuid}
940 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000941 alarm.update(alarm_action)
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000942 vnf = self.engine.db.get_one(
943 "vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}
944 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000945 alarm["vnf-id"] = vnf["_id"]
946 return self._format_out(str(alarm))
947 except Exception:
948 return self._format_out("Please provide valid alarm uuid")
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000949 elif ns_id and ns_id != "None":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000950 # if request is on basis of ns_id
951 try:
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000952 alarms = self.engine.db.get_list(
953 "alarms", {"tags.ns_id": ns_id}
954 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000955 for alarm in alarms:
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000956 alarm_action = self.engine.db.get_one(
957 "alarms_action", {"uuid": alarm["uuid"]}
958 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000959 alarm.update(alarm_action)
960 return self._format_out(str(alarms))
961 except Exception:
962 return self._format_out("Please provide valid ns id")
963 else:
964 # to return only alarm which are related to given project
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000965 project = self.engine.db.get_one(
966 "projects", {"name": project_name}
967 )
968 project_id = project.get("_id")
969 ns_list = self.engine.db.get_list(
970 "nsrs", {"_admin.projects_read": project_id}
971 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000972 ns_ids = []
973 for ns in ns_list:
974 ns_ids.append(ns.get("_id"))
975 alarms = self.engine.db.get_list("alarms")
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000976 alarm_list = [
977 alarm
978 for alarm in alarms
979 if alarm["tags"]["ns_id"] in ns_ids
980 ]
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000981 for alrm in alarm_list:
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000982 action = self.engine.db.get_one(
983 "alarms_action", {"uuid": alrm.get("uuid")}
984 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000985 alrm.update(action)
986 return self._format_out(str(alarm_list))
987 # to handle patch request for alarm update
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000988 elif cherrypy.request.method == "PATCH":
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000989 data = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
990 try:
991 # check if uuid is valid
992 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
993 except Exception:
994 return self._format_out("Please provide valid alarm uuid.")
995 if data.get("is_enable") is not None:
996 if data.get("is_enable"):
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000997 alarm_status = "ok"
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000998 else:
Patricia Reinoso43eac1e2022-10-26 08:55:54 +0000999 alarm_status = "disabled"
1000 self.engine.db.set_one(
1001 "alarms",
1002 {"uuid": data.get("uuid")},
1003 {"alarm_status": alarm_status},
1004 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001005 else:
Patricia Reinoso43eac1e2022-10-26 08:55:54 +00001006 self.engine.db.set_one(
1007 "alarms",
1008 {"uuid": data.get("uuid")},
1009 {"threshold": data.get("threshold")},
1010 )
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001011 return self._format_out("Alarm updated")
1012 except Exception as e:
1013 cherrypy.response.status = e.http_code.value
Patricia Reinoso43eac1e2022-10-26 08:55:54 +00001014 if isinstance(
1015 e,
1016 (
1017 NbiException,
1018 EngineException,
1019 DbException,
1020 FsException,
1021 MsgException,
1022 AuthException,
1023 ValidationError,
1024 AuthconnException,
1025 ),
1026 ):
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001027 http_code_value = cherrypy.response.status = e.http_code.value
1028 http_code_name = e.http_code.name
1029 cherrypy.log("Exception {}".format(e))
1030 else:
Patricia Reinoso43eac1e2022-10-26 08:55:54 +00001031 http_code_value = (
1032 cherrypy.response.status
1033 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Atul Agarwalb6480fc2021-03-18 08:19:32 +00001034 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
1035 http_code_name = HTTPStatus.BAD_REQUEST.name
1036 problem_details = {
1037 "code": http_code_name,
1038 "status": http_code_value,
1039 "detail": str(e),
1040 }
1041 return self._format_out(problem_details)
1042
tierno55945e72018-04-06 16:40:27 +02001043 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +01001044 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +00001045 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +01001046 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +01001047 indata = self._format_in(kwargs)
1048 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001049 raise NbiException(
1050 "Expected application/yaml or application/json Content-Type",
1051 HTTPStatus.BAD_REQUEST,
1052 )
tiernoa5035702019-07-29 08:54:42 +00001053
1054 if method == "GET":
1055 token_info = self.authenticator.authorize()
1056 # for logging
1057 self._format_login(token_info)
1058 if token_id:
1059 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +01001060 else:
tiernoa5035702019-07-29 08:54:42 +00001061 outdata = self.authenticator.get_token_list(token_info)
1062 elif method == "POST":
1063 try:
1064 token_info = self.authenticator.authorize()
1065 except Exception:
1066 token_info = None
1067 if kwargs:
1068 indata.update(kwargs)
1069 # This is needed to log the user when authentication fails
1070 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +01001071 outdata = token_info = self.authenticator.new_token(
1072 token_info, indata, cherrypy.request.remote
1073 )
1074 cherrypy.session["Authorization"] = outdata["_id"]
tiernoa5035702019-07-29 08:54:42 +00001075 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1076 # for logging
1077 self._format_login(token_info)
selvi.ja9a1fc82022-04-04 06:54:30 +00001078 # password expiry check
1079 if self.authenticator.check_password_expiry(outdata):
Patricia Reinoso43eac1e2022-10-26 08:55:54 +00001080 outdata = {
1081 "id": outdata["id"],
1082 "message": "change_password",
1083 "user_id": outdata["user_id"],
1084 }
tiernoa5035702019-07-29 08:54:42 +00001085 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1086 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
1087 elif method == "DELETE":
1088 if not token_id and "id" in kwargs:
1089 token_id = kwargs["id"]
1090 elif not token_id:
1091 token_info = self.authenticator.authorize()
1092 # for logging
1093 self._format_login(token_info)
1094 token_id = token_info["_id"]
1095 outdata = self.authenticator.del_token(token_id)
1096 token_info = None
garciadeblas4568a372021-03-24 09:19:48 +01001097 cherrypy.session["Authorization"] = "logout"
tiernoa5035702019-07-29 08:54:42 +00001098 # cherrypy.response.cookie["Authorization"] = token_id
1099 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1100 else:
garciadeblas4568a372021-03-24 09:19:48 +01001101 raise NbiException(
1102 "Method {} not allowed for token".format(method),
1103 HTTPStatus.METHOD_NOT_ALLOWED,
1104 )
tiernoa5035702019-07-29 08:54:42 +00001105 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001106
1107 @cherrypy.expose
1108 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001109 if not cherrypy.config.get("server.enable_test") or (
1110 isinstance(cherrypy.config["server.enable_test"], str)
1111 and cherrypy.config["server.enable_test"].lower() == "false"
1112 ):
tierno4836bac2020-01-15 14:41:48 +00001113 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1114 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001115 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001116 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001117 return (
1118 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1119 "sleep/<time>\nmessage/topic\n</pre></html>"
1120 )
tiernof27c79b2018-03-12 17:08:42 +01001121
1122 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001123 try:
1124 # self.engine.load_dbase(cherrypy.request.app.config)
1125 self.engine.create_admin()
1126 return "Done. User 'admin', password 'admin' created"
1127 except Exception:
1128 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1129 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001130 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001131 return cherrypy.lib.static.serve_file(
1132 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1133 "text/plain",
1134 "attachment",
1135 )
tiernof27c79b2018-03-12 17:08:42 +01001136 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001137 f_path = (
1138 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1139 )
tiernof27c79b2018-03-12 17:08:42 +01001140 f = open(f_path, "r")
1141 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001142 return f
tierno55945e72018-04-06 16:40:27 +02001143
tiernof27c79b2018-03-12 17:08:42 +01001144 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001145 deleted_info = self.engine.db.del_list(args[1], kwargs)
1146 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1147 elif len(args) and args[0] == "fs-clear":
1148 if len(args) >= 2:
1149 folders = (args[1],)
1150 else:
1151 folders = self.engine.fs.dir_ls(".")
1152 for folder in folders:
1153 self.engine.fs.file_delete(folder)
1154 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001155 elif args and args[0] == "login":
1156 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001157 cherrypy.response.headers[
1158 "WWW-Authenticate"
1159 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001160 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1161 elif args and args[0] == "login2":
1162 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001163 cherrypy.response.headers[
1164 "WWW-Authenticate"
1165 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001166 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1167 elif args and args[0] == "sleep":
1168 sleep_time = 5
1169 try:
1170 sleep_time = int(args[1])
1171 except Exception:
1172 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1173 return self._format_out("Database already initialized")
1174 thread_info = cherrypy.thread_data
1175 print(thread_info)
1176 time.sleep(sleep_time)
1177 # thread_info
1178 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001179 main_topic = args[1]
1180 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001181 try:
garciadeblas4568a372021-03-24 09:19:48 +01001182 if cherrypy.request.method == "POST":
delacruzramob19cadc2019-10-08 10:18:02 +02001183 to_send = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
tierno55945e72018-04-06 16:40:27 +02001184 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001185 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001186 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001187 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001188 for k, v in kwargs.items():
tiernof1509b22020-05-12 14:32:37 +00001189 v_dict = yaml.load(v, Loader=yaml.SafeLoader)
1190 self.engine.msg.write(main_topic, k, v_dict)
1191 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001192 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001193 return_text += "Error: " + str(e)
1194 return_text += "</pre></html>\n"
1195 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001196
1197 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001198 "<html><pre>\nheaders:\n args: {}\n".format(args)
1199 + " kwargs: {}\n".format(kwargs)
1200 + " headers: {}\n".format(cherrypy.request.headers)
1201 + " path_info: {}\n".format(cherrypy.request.path_info)
1202 + " query_string: {}\n".format(cherrypy.request.query_string)
1203 + " session: {}\n".format(cherrypy.session)
1204 + " cookie: {}\n".format(cherrypy.request.cookie)
1205 + " method: {}\n".format(cherrypy.request.method)
1206 + " session: {}\n".format(cherrypy.session.get("fieldname"))
1207 + " body:\n"
1208 )
tiernoc94c3df2018-02-09 15:38:54 +01001209 return_text += " length: {}\n".format(cherrypy.request.body.length)
1210 if cherrypy.request.body.length:
1211 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001212 str(
1213 cherrypy.request.body.read(
1214 int(cherrypy.request.headers.get("Content-Length", 0))
1215 )
1216 )
1217 )
tiernoc94c3df2018-02-09 15:38:54 +01001218 if thread_info:
1219 return_text += "thread: {}\n".format(thread_info)
1220 return_text += "</pre></html>"
1221 return return_text
1222
tierno701018c2019-06-25 11:13:14 +00001223 @staticmethod
1224 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001225 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001226 raise NbiException(
1227 "URL must contain at least 'main_topic/version/topic'",
1228 HTTPStatus.METHOD_NOT_ALLOWED,
1229 )
tiernof27c79b2018-03-12 17:08:42 +01001230
tierno701018c2019-06-25 11:13:14 +00001231 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001232 for arg in args:
1233 if arg is None:
1234 break
1235 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001236 raise NbiException(
1237 "URL contains unexpected extra items '{}'".format(arg),
1238 HTTPStatus.METHOD_NOT_ALLOWED,
1239 )
tiernof27c79b2018-03-12 17:08:42 +01001240
1241 if arg in reference:
1242 reference = reference[arg]
1243 elif "<ID>" in reference:
1244 reference = reference["<ID>"]
1245 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001246 # if there is content
1247 if reference["*"]:
1248 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001249 break
1250 else:
garciadeblas4568a372021-03-24 09:19:48 +01001251 raise NbiException(
1252 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1253 )
tiernof27c79b2018-03-12 17:08:42 +01001254 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001255 raise NbiException(
1256 "Method {} not supported yet for this URL".format(method),
1257 HTTPStatus.NOT_IMPLEMENTED,
1258 )
tierno2236d202018-05-16 19:05:16 +02001259 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001260 raise NbiException(
1261 "Method {} not supported for this URL".format(method),
1262 HTTPStatus.METHOD_NOT_ALLOWED,
1263 )
tierno701018c2019-06-25 11:13:14 +00001264 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001265
1266 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001267 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001268 """
1269 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001270 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001271 :param version:
tiernob24258a2018-10-04 18:39:49 +02001272 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001273 :param id:
1274 :return: None
1275 """
1276 # 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 +01001277 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1278 main_topic, version, topic, id
1279 )
tiernof27c79b2018-03-12 17:08:42 +01001280 return
1281
tierno65ca36d2019-02-12 19:27:52 +01001282 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001283 def _extract_query_string_operations(kwargs, method):
1284 """
1285
1286 :param kwargs:
1287 :return:
1288 """
1289 query_string_operations = []
1290 if kwargs:
1291 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1292 if qs in kwargs and kwargs[qs].lower() != "false":
1293 query_string_operations.append(qs.lower() + ":" + method.lower())
1294 return query_string_operations
1295
1296 @staticmethod
1297 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001298 """
1299 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1300 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001301 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001302 :param kwargs: query string input.
1303 :param method: http method: GET, POSST, PUT, ...
1304 :param _id:
1305 :return: admin_query dictionary with keys:
1306 public: True, False or None
1307 force: True or False
1308 project_id: tuple with projects used for accessing an element
1309 set_project: tuple with projects that a created element will belong to
1310 method: show, list, delete, write
1311 """
garciadeblas4568a372021-03-24 09:19:48 +01001312 admin_query = {
1313 "force": False,
1314 "project_id": (token_info["project_id"],),
1315 "username": token_info["username"],
1316 "admin": token_info["admin"],
1317 "public": None,
1318 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1319 }
tierno65ca36d2019-02-12 19:27:52 +01001320 if kwargs:
1321 # FORCE
1322 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001323 if (
1324 kwargs["FORCE"].lower() != "false"
1325 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001326 admin_query["force"] = True
1327 del kwargs["FORCE"]
1328 # PUBLIC
1329 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001330 if (
1331 kwargs["PUBLIC"].lower() != "false"
1332 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001333 admin_query["public"] = True
1334 else:
1335 admin_query["public"] = False
1336 del kwargs["PUBLIC"]
1337 # ADMIN
1338 if "ADMIN" in kwargs:
1339 behave_as = kwargs.pop("ADMIN")
1340 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001341 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001342 raise NbiException(
1343 "Only admin projects can use 'ADMIN' query string",
1344 HTTPStatus.UNAUTHORIZED,
1345 )
1346 if (
1347 not behave_as or behave_as.lower() == "true"
1348 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001349 admin_query["project_id"] = ()
1350 elif isinstance(behave_as, (list, tuple)):
1351 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001352 else: # isinstance(behave_as, str)
1353 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001354 if "SET_PROJECT" in kwargs:
1355 set_project = kwargs.pop("SET_PROJECT")
1356 if not set_project:
1357 admin_query["set_project"] = list(admin_query["project_id"])
1358 else:
1359 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001360 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001361 if admin_query["project_id"]:
1362 for p in set_project:
1363 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001364 raise NbiException(
1365 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1366 "'ADMIN='{p}'".format(p=p),
1367 HTTPStatus.UNAUTHORIZED,
1368 )
tierno65ca36d2019-02-12 19:27:52 +01001369 admin_query["set_project"] = set_project
1370
1371 # PROJECT_READ
1372 # if "PROJECT_READ" in kwargs:
1373 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001374 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001375 if method == "GET":
1376 if _id:
1377 admin_query["method"] = "show"
1378 else:
1379 admin_query["method"] = "list"
1380 elif method == "DELETE":
1381 admin_query["method"] = "delete"
1382 else:
1383 admin_query["method"] = "write"
1384 return admin_query
1385
tiernoc94c3df2018-02-09 15:38:54 +01001386 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001387 def default(
1388 self,
1389 main_topic=None,
1390 version=None,
1391 topic=None,
1392 _id=None,
1393 item=None,
1394 *args,
1395 **kwargs
1396 ):
tierno701018c2019-06-25 11:13:14 +00001397 token_info = None
tiernof27c79b2018-03-12 17:08:42 +01001398 outdata = None
1399 _format = None
tierno0f98af52018-03-19 10:28:22 +01001400 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001401 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001402 rollback = []
tierno701018c2019-06-25 11:13:14 +00001403 engine_session = None
tiernoc94c3df2018-02-09 15:38:54 +01001404 try:
tiernob24258a2018-10-04 18:39:49 +02001405 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001406 raise NbiException(
1407 "URL must contain at least 'main_topic/version/topic'",
1408 HTTPStatus.METHOD_NOT_ALLOWED,
1409 )
1410 if main_topic not in (
1411 "admin",
1412 "vnfpkgm",
1413 "nsd",
1414 "nslcm",
1415 "pdu",
1416 "nst",
1417 "nsilcm",
1418 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001419 "vnflcm",
garciadeblas4568a372021-03-24 09:19:48 +01001420 ):
1421 raise NbiException(
1422 "URL main_topic '{}' not supported".format(main_topic),
1423 HTTPStatus.METHOD_NOT_ALLOWED,
1424 )
1425 if version != "v1":
1426 raise NbiException(
1427 "URL version '{}' not supported".format(version),
1428 HTTPStatus.METHOD_NOT_ALLOWED,
1429 )
tiernoc94c3df2018-02-09 15:38:54 +01001430
garciadeblas4568a372021-03-24 09:19:48 +01001431 if (
1432 kwargs
1433 and "METHOD" in kwargs
1434 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1435 ):
tiernof27c79b2018-03-12 17:08:42 +01001436 method = kwargs.pop("METHOD")
1437 else:
1438 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001439
garciadeblas4568a372021-03-24 09:19:48 +01001440 role_permission = self._check_valid_url_method(
1441 method, main_topic, version, topic, _id, item, *args
1442 )
1443 query_string_operations = self._extract_query_string_operations(
1444 kwargs, method
1445 )
tiernob24258a2018-10-04 18:39:49 +02001446 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001447 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001448 token_info = self.authenticator.authorize(
1449 role_permission, query_string_operations, _id
1450 )
tierno12eac3c2020-03-19 23:22:08 +00001451 if main_topic == "admin" and topic == "domains":
1452 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001453 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001454 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001455 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301456
vijay.r35ef2f72019-04-30 17:55:49 +05301457 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001458 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001459
tiernob24258a2018-10-04 18:39:49 +02001460 if main_topic == "nsd":
1461 engine_topic = "nsds"
1462 elif main_topic == "vnfpkgm":
1463 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001464 if topic == "vnfpkg_op_occs":
1465 engine_topic = "vnfpkgops"
1466 if topic == "vnf_packages" and item == "action":
1467 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001468 elif main_topic == "nslcm":
1469 engine_topic = "nsrs"
1470 if topic == "ns_lcm_op_occs":
1471 engine_topic = "nslcmops"
1472 if topic == "vnfrs" or topic == "vnf_instances":
1473 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001474 elif main_topic == "vnflcm":
1475 if topic == "vnf_lcm_op_occs":
1476 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001477 elif main_topic == "nst":
1478 engine_topic = "nsts"
1479 elif main_topic == "nsilcm":
1480 engine_topic = "nsis"
1481 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001482 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001483 elif main_topic == "pdu":
1484 engine_topic = "pdus"
garciadeblas4568a372021-03-24 09:19:48 +01001485 if (
1486 engine_topic == "vims"
1487 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001488 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001489
preethika.p329b8182020-04-22 12:25:39 +05301490 if topic == "subscriptions":
1491 engine_topic = main_topic + "_" + topic
1492
tiernoc94c3df2018-02-09 15:38:54 +01001493 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001494 if item in (
1495 "nsd_content",
1496 "package_content",
1497 "artifacts",
1498 "vnfd",
1499 "nsd",
1500 "nst",
1501 "nst_content",
1502 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001503 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001504 path = "$DESCRIPTOR"
1505 elif args:
1506 path = args
tiernob24258a2018-10-04 18:39:49 +02001507 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001508 path = ()
1509 else:
1510 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001511 file, _format = self.engine.get_file(
1512 engine_session,
1513 engine_topic,
1514 _id,
1515 path,
1516 cherrypy.request.headers.get("Accept"),
1517 )
tiernof27c79b2018-03-12 17:08:42 +01001518 outdata = file
1519 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001520 outdata = self.engine.get_item_list(
1521 engine_session, engine_topic, kwargs, api_req=True
1522 )
tiernoc94c3df2018-02-09 15:38:54 +01001523 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301524 if item == "reports":
1525 # TODO check that project_id (_id in this context) has permissions
1526 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301527 filter_q = None
1528 if "vcaStatusRefresh" in kwargs:
1529 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
Patricia Reinoso43eac1e2022-10-26 08:55:54 +00001530 outdata = self.engine.get_item(
1531 engine_session, engine_topic, _id, filter_q, True
1532 )
delacruzramo271d2002019-12-02 21:00:37 +01001533
tiernof27c79b2018-03-12 17:08:42 +01001534 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001535 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001536 if topic in (
1537 "ns_descriptors_content",
1538 "vnf_packages_content",
1539 "netslice_templates_content",
1540 ):
tiernof27c79b2018-03-12 17:08:42 +01001541 _id = cherrypy.request.headers.get("Transaction-Id")
1542 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001543 _id, _ = self.engine.new_item(
1544 rollback,
1545 engine_session,
1546 engine_topic,
1547 {},
1548 None,
1549 cherrypy.request.headers,
1550 )
1551 completed = self.engine.upload_content(
1552 engine_session,
1553 engine_topic,
1554 _id,
1555 indata,
1556 kwargs,
1557 cherrypy.request.headers,
1558 )
tiernof27c79b2018-03-12 17:08:42 +01001559 if completed:
tiernob24258a2018-10-04 18:39:49 +02001560 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001561 else:
1562 cherrypy.response.headers["Transaction-Id"] = _id
1563 outdata = {"id": _id}
tiernob24258a2018-10-04 18:39:49 +02001564 elif topic == "ns_instances_content":
1565 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001566 _id, _ = self.engine.new_item(
1567 rollback, engine_session, engine_topic, indata, kwargs
1568 )
tiernob24258a2018-10-04 18:39:49 +02001569 # creates nslcmop
1570 indata["lcmOperationType"] = "instantiate"
1571 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001572 nslcmop_id, _ = self.engine.new_item(
1573 rollback, engine_session, "nslcmops", indata, None
1574 )
tiernob24258a2018-10-04 18:39:49 +02001575 self._set_location_header(main_topic, version, topic, _id)
kuuse078f55e2019-05-16 19:24:21 +02001576 outdata = {"id": _id, "nslcmop_id": nslcmop_id}
tiernob24258a2018-10-04 18:39:49 +02001577 elif topic == "ns_instances" and item:
1578 indata["lcmOperationType"] = item
1579 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001580 _id, _ = self.engine.new_item(
1581 rollback, engine_session, "nslcmops", indata, kwargs
1582 )
1583 self._set_location_header(
1584 main_topic, version, "ns_lcm_op_occs", _id
1585 )
tierno65acb4d2018-04-06 16:42:40 +02001586 outdata = {"id": _id}
1587 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001588 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001589 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001590 _id, _ = self.engine.new_item(
1591 rollback, engine_session, engine_topic, indata, kwargs
1592 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001593 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001594 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001595 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001596 nsilcmop_id, _ = self.engine.new_item(
1597 rollback, engine_session, "nsilcmops", indata, kwargs
1598 )
kuuse078f55e2019-05-16 19:24:21 +02001599 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001600 elif topic == "netslice_instances" and item:
1601 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001602 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001603 _id, _ = self.engine.new_item(
1604 rollback, engine_session, "nsilcmops", indata, kwargs
1605 )
1606 self._set_location_header(
1607 main_topic, version, "nsi_lcm_op_occs", _id
1608 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001609 outdata = {"id": _id}
1610 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001611 elif topic == "vnf_packages" and item == "action":
1612 indata["lcmOperationType"] = item
1613 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001614 _id, _ = self.engine.new_item(
1615 rollback, engine_session, "vnfpkgops", indata, kwargs
1616 )
1617 self._set_location_header(
1618 main_topic, version, "vnfpkg_op_occs", _id
1619 )
delacruzramo271d2002019-12-02 21:00:37 +01001620 outdata = {"id": _id}
1621 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301622 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001623 _id, _ = self.engine.new_item(
1624 rollback, engine_session, engine_topic, indata, kwargs
1625 )
preethika.p329b8182020-04-22 12:25:39 +05301626 self._set_location_header(main_topic, version, topic, _id)
1627 link = {}
1628 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001629 outdata = {
1630 "id": _id,
1631 "filter": indata["filter"],
1632 "callbackUri": indata["CallbackUri"],
1633 "_links": link,
1634 }
preethika.p329b8182020-04-22 12:25:39 +05301635 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001636 elif topic == "vnf_instances" and item:
1637 indata["lcmOperationType"] = item
1638 indata["vnfInstanceId"] = _id
Patricia Reinoso43eac1e2022-10-26 08:55:54 +00001639 _id, _ = self.engine.new_item(
1640 rollback, engine_session, "vnflcmops", indata, kwargs
1641 )
1642 self._set_location_header(
1643 main_topic, version, "vnf_lcm_op_occs", _id
1644 )
almagiae47b9132022-05-17 14:12:22 +02001645 outdata = {"id": _id}
1646 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernof27c79b2018-03-12 17:08:42 +01001647 else:
garciadeblas4568a372021-03-24 09:19:48 +01001648 _id, op_id = self.engine.new_item(
1649 rollback,
1650 engine_session,
1651 engine_topic,
1652 indata,
1653 kwargs,
1654 cherrypy.request.headers,
1655 )
tiernob24258a2018-10-04 18:39:49 +02001656 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001657 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00001658 if op_id:
1659 outdata["op_id"] = op_id
1660 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001661 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02001662
tiernoc94c3df2018-02-09 15:38:54 +01001663 elif method == "DELETE":
1664 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001665 outdata = self.engine.del_item_list(
1666 engine_session, engine_topic, kwargs
1667 )
tierno09c073e2018-04-26 13:36:48 +02001668 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01001669 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00001670 # for NS NSI generate an operation
1671 op_id = None
tierno701018c2019-06-25 11:13:14 +00001672 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02001673 nslcmop_desc = {
1674 "lcmOperationType": "terminate",
1675 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001676 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02001677 }
garciadeblas4568a372021-03-24 09:19:48 +01001678 op_id, _ = self.engine.new_item(
1679 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
1680 )
tierno22577432020-04-08 15:16:57 +00001681 if op_id:
1682 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001683 elif (
1684 topic == "netslice_instances_content"
1685 and not engine_session["force"]
1686 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001687 nsilcmop_desc = {
1688 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02001689 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001690 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02001691 }
garciadeblas4568a372021-03-24 09:19:48 +01001692 op_id, _ = self.engine.new_item(
1693 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
1694 )
tierno22577432020-04-08 15:16:57 +00001695 if op_id:
1696 outdata = {"_id": op_id}
1697 # if there is not any deletion in process, delete
1698 if not op_id:
1699 op_id = self.engine.del_item(engine_session, engine_topic, _id)
1700 if op_id:
1701 outdata = {"op_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001702 cherrypy.response.status = (
1703 HTTPStatus.ACCEPTED.value
1704 if op_id
1705 else HTTPStatus.NO_CONTENT.value
1706 )
tierno09c073e2018-04-26 13:36:48 +02001707
tierno7ae10112018-05-18 14:36:02 +02001708 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00001709 op_id = None
tierno701018c2019-06-25 11:13:14 +00001710 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01001711 raise NbiException(
1712 "Nothing to update. Provide payload and/or query string",
1713 HTTPStatus.BAD_REQUEST,
1714 )
1715 if (
1716 item in ("nsd_content", "package_content", "nst_content")
1717 and method == "PUT"
1718 ):
1719 completed = self.engine.upload_content(
1720 engine_session,
1721 engine_topic,
1722 _id,
1723 indata,
1724 kwargs,
1725 cherrypy.request.headers,
1726 )
tiernof27c79b2018-03-12 17:08:42 +01001727 if not completed:
1728 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01001729 else:
garciadeblas4568a372021-03-24 09:19:48 +01001730 op_id = self.engine.edit_item(
1731 engine_session, engine_topic, _id, indata, kwargs
1732 )
tiernobdebce92019-07-01 15:36:49 +00001733
1734 if op_id:
1735 cherrypy.response.status = HTTPStatus.ACCEPTED.value
1736 outdata = {"op_id": op_id}
1737 else:
1738 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
1739 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01001740 else:
garciadeblas4568a372021-03-24 09:19:48 +01001741 raise NbiException(
1742 "Method {} not allowed".format(method),
1743 HTTPStatus.METHOD_NOT_ALLOWED,
1744 )
tiernoa6bb45d2019-06-14 09:45:39 +00001745
1746 # if Role information changes, it is needed to reload the information of roles
1747 if topic == "roles" and method != "GET":
1748 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01001749
garciadeblas4568a372021-03-24 09:19:48 +01001750 if (
1751 topic == "projects"
1752 and method == "DELETE"
1753 or topic in ["users", "roles"]
1754 and method in ["PUT", "PATCH", "DELETE"]
1755 ):
delacruzramoad682a52019-12-10 16:26:34 +01001756 self.authenticator.remove_token_from_cache()
1757
tierno701018c2019-06-25 11:13:14 +00001758 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02001759 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01001760 if isinstance(
1761 e,
1762 (
1763 NbiException,
1764 EngineException,
1765 DbException,
1766 FsException,
1767 MsgException,
1768 AuthException,
1769 ValidationError,
1770 AuthconnException,
1771 ),
1772 ):
tiernob24258a2018-10-04 18:39:49 +02001773 http_code_value = cherrypy.response.status = e.http_code.value
1774 http_code_name = e.http_code.name
1775 cherrypy.log("Exception {}".format(e))
1776 else:
garciadeblas4568a372021-03-24 09:19:48 +01001777 http_code_value = (
1778 cherrypy.response.status
1779 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01001780 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02001781 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02001782 if hasattr(outdata, "close"): # is an open file
1783 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02001784 error_text = str(e)
1785 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02001786 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02001787 try:
tiernocc103432018-10-19 14:10:35 +02001788 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01001789 self.engine.db.set_one(
1790 rollback_item["topic"],
1791 {"_id": rollback_item["_id"]},
1792 rollback_item["content"],
1793 fail_on_empty=False,
1794 )
preethika.p329b8182020-04-22 12:25:39 +05301795 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01001796 self.engine.db.del_list(
1797 rollback_item["topic"],
1798 rollback_item["filter"],
1799 fail_on_empty=False,
1800 )
tiernocc103432018-10-19 14:10:35 +02001801 else:
garciadeblas4568a372021-03-24 09:19:48 +01001802 self.engine.db.del_one(
1803 rollback_item["topic"],
1804 {"_id": rollback_item["_id"]},
1805 fail_on_empty=False,
1806 )
tierno3ace63c2018-05-03 17:51:43 +02001807 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01001808 rollback_error_text = "Rollback Exception {}: {}".format(
1809 rollback_item, e2
1810 )
tiernob24258a2018-10-04 18:39:49 +02001811 cherrypy.log(rollback_error_text)
1812 error_text += ". " + rollback_error_text
1813 # if isinstance(e, MsgException):
1814 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
1815 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01001816 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02001817 "code": http_code_name,
1818 "status": http_code_value,
1819 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01001820 }
tierno701018c2019-06-25 11:13:14 +00001821 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001822 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00001823 finally:
1824 if token_info:
1825 self._format_login(token_info)
1826 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
1827 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
1828 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01001829 cherrypy.request.login += ";{}={}".format(
1830 logging_id, outdata[logging_id][:36]
1831 )
tiernoc94c3df2018-02-09 15:38:54 +01001832
1833
tiernoc94c3df2018-02-09 15:38:54 +01001834def _start_service():
1835 """
1836 Callback function called when cherrypy.engine starts
1837 Override configuration with env variables
1838 Set database, storage, message configuration
1839 Init database with admin/admin user password
1840 """
tierno932499c2019-01-28 17:28:10 +00001841 global nbi_server
1842 global subscription_thread
tiernoc94c3df2018-02-09 15:38:54 +01001843 cherrypy.log.error("Starting osm_nbi")
1844 # update general cherrypy configuration
1845 update_dict = {}
1846
garciadeblas4568a372021-03-24 09:19:48 +01001847 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01001848 for k, v in environ.items():
1849 if not k.startswith("OSMNBI_"):
1850 continue
tiernoe1281182018-05-22 12:24:36 +02001851 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01001852 if not k2:
1853 continue
1854 try:
1855 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01001856 if k == "OSMNBI_STATIC_DIR":
1857 engine_config["/static"]["tools.staticdir.dir"] = v
1858 engine_config["/static"]["tools.staticdir.on"] = True
1859 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
1860 update_dict["server.socket_port"] = int(v)
1861 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
1862 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02001863 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01001864 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001865 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02001866 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001867 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01001868 engine_config[k1][k2] = int(v)
1869 else:
1870 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001871
tiernoc94c3df2018-02-09 15:38:54 +01001872 except ValueError as e:
1873 cherrypy.log.error("Ignoring environ '{}': " + str(e))
1874 except Exception as e:
1875 cherrypy.log.warn("skipping environ '{}' on exception '{}'".format(k, e))
1876
1877 if update_dict:
1878 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02001879 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001880
1881 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01001882 log_format_simple = (
1883 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
1884 )
1885 log_formatter_simple = logging.Formatter(
1886 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
1887 )
tiernoc94c3df2018-02-09 15:38:54 +01001888 logger_server = logging.getLogger("cherrypy.error")
1889 logger_access = logging.getLogger("cherrypy.access")
1890 logger_cherry = logging.getLogger("cherrypy")
1891 logger_nbi = logging.getLogger("nbi")
1892
tiernof5298be2018-05-16 14:43:57 +02001893 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01001894 file_handler = logging.handlers.RotatingFileHandler(
1895 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
1896 )
tiernoc94c3df2018-02-09 15:38:54 +01001897 file_handler.setFormatter(log_formatter_simple)
1898 logger_cherry.addHandler(file_handler)
1899 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02001900 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01001901 for format_, logger in {
1902 "nbi.server %(filename)s:%(lineno)s": logger_server,
1903 "nbi.access %(filename)s:%(lineno)s": logger_access,
1904 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
1905 }.items():
tiernob24258a2018-10-04 18:39:49 +02001906 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01001907 log_formatter_cherry = logging.Formatter(
1908 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
1909 )
tiernob24258a2018-10-04 18:39:49 +02001910 str_handler = logging.StreamHandler()
1911 str_handler.setFormatter(log_formatter_cherry)
1912 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01001913
tiernof5298be2018-05-16 14:43:57 +02001914 if engine_config["global"].get("log.level"):
1915 logger_cherry.setLevel(engine_config["global"]["log.level"])
1916 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01001917
1918 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01001919 for k1, logname in {
1920 "message": "nbi.msg",
1921 "database": "nbi.db",
1922 "storage": "nbi.fs",
1923 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01001924 engine_config[k1]["logger_name"] = logname
1925 logger_module = logging.getLogger(logname)
1926 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01001927 file_handler = logging.handlers.RotatingFileHandler(
1928 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
1929 )
tiernoc94c3df2018-02-09 15:38:54 +01001930 file_handler.setFormatter(log_formatter_simple)
1931 logger_module.addHandler(file_handler)
1932 if "loglevel" in engine_config[k1]:
1933 logger_module.setLevel(engine_config[k1]["loglevel"])
1934 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01001935 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
1936 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
1937 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
1938 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
1939 target_version=auth_database_version
1940 )
tiernobee508e2019-01-21 11:21:49 +00001941
tierno932499c2019-01-28 17:28:10 +00001942 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01001943 subscription_thread = SubscriptionThread(
1944 config=engine_config, engine=nbi_server.engine
1945 )
tierno932499c2019-01-28 17:28:10 +00001946 subscription_thread.start()
1947 # Do not capture except SubscriptionException
1948
tiernob2e48bd2020-02-04 15:47:18 +00001949 backend = engine_config["authentication"]["backend"]
garciadeblas4568a372021-03-24 09:19:48 +01001950 cherrypy.log.error(
1951 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
1952 nbi_version, nbi_version_date, backend
1953 )
1954 )
tiernoc94c3df2018-02-09 15:38:54 +01001955
1956
1957def _stop_service():
1958 """
1959 Callback function called when cherrypy.engine stops
1960 TODO: Ending database connections.
1961 """
tierno932499c2019-01-28 17:28:10 +00001962 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01001963 if subscription_thread:
1964 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00001965 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01001966 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01001967 cherrypy.log.error("Stopping osm_nbi")
1968
tierno2236d202018-05-16 19:05:16 +02001969
tiernof5298be2018-05-16 14:43:57 +02001970def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00001971 global nbi_server
tiernoc94c3df2018-02-09 15:38:54 +01001972 # conf = {
1973 # '/': {
1974 # #'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
1975 # 'tools.sessions.on': True,
1976 # 'tools.response_headers.on': True,
1977 # # 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
1978 # }
1979 # }
1980 # cherrypy.Server.ssl_module = 'builtin'
1981 # cherrypy.Server.ssl_certificate = "http/cert.pem"
1982 # cherrypy.Server.ssl_private_key = "http/privkey.pem"
1983 # cherrypy.Server.thread_pool = 10
1984 # cherrypy.config.update({'Server.socket_port': config["port"], 'Server.socket_host': config["host"]})
1985
1986 # cherrypy.config.update({'tools.auth_basic.on': True,
1987 # 'tools.auth_basic.realm': 'localhost',
1988 # 'tools.auth_basic.checkpassword': validate_password})
tierno932499c2019-01-28 17:28:10 +00001989 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01001990 cherrypy.engine.subscribe("start", _start_service)
1991 cherrypy.engine.subscribe("stop", _stop_service)
1992 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02001993
1994
1995def usage():
garciadeblas4568a372021-03-24 09:19:48 +01001996 print(
1997 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02001998 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
1999 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01002000 """.format(
2001 sys.argv[0]
2002 )
2003 )
tierno2236d202018-05-16 19:05:16 +02002004 # --log-socket-host HOST: send logs to this host")
2005 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01002006
2007
garciadeblas4568a372021-03-24 09:19:48 +01002008if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02002009 try:
2010 # load parameters and configuration
2011 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
2012 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
2013 config_file = None
2014 for o, a in opts:
2015 if o in ("-h", "--help"):
2016 usage()
2017 sys.exit()
2018 elif o in ("-c", "--config"):
2019 config_file = a
2020 # elif o == "--log-socket-port":
2021 # log_socket_port = a
2022 # elif o == "--log-socket-host":
2023 # log_socket_host = a
2024 # elif o == "--log-file":
2025 # log_file = a
2026 else:
2027 assert False, "Unhandled option"
2028 if config_file:
2029 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01002030 print(
2031 "configuration file '{}' that not exist".format(config_file),
2032 file=sys.stderr,
2033 )
tiernof5298be2018-05-16 14:43:57 +02002034 exit(1)
2035 else:
garciadeblas4568a372021-03-24 09:19:48 +01002036 for config_file in (
2037 __file__[: __file__.rfind(".")] + ".cfg",
2038 "./nbi.cfg",
2039 "/etc/osm/nbi.cfg",
2040 ):
tiernof5298be2018-05-16 14:43:57 +02002041 if path.isfile(config_file):
2042 break
2043 else:
garciadeblas4568a372021-03-24 09:19:48 +01002044 print(
2045 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
2046 file=sys.stderr,
2047 )
tiernof5298be2018-05-16 14:43:57 +02002048 exit(1)
2049 nbi(config_file)
2050 except getopt.GetoptError as e:
2051 print(str(e), file=sys.stderr)
2052 # usage()
2053 exit(1)