blob: de48c033e1b9d8a4fe97a63f58c403f871f13e26 [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 },
garciadeblas4568a372021-03-24 09:19:48 +0100464 },
465 },
466 "ns_lcm_op_occs": {
467 "METHODS": ("GET",),
468 "ROLE_PERMISSION": "ns_instances:opps:",
469 "<ID>": {
470 "METHODS": ("GET",),
471 "ROLE_PERMISSION": "ns_instances:opps:id:",
472 },
473 },
474 "vnfrs": {
475 "METHODS": ("GET",),
476 "ROLE_PERMISSION": "vnf_instances:",
477 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
478 },
479 "vnf_instances": {
480 "METHODS": ("GET",),
481 "ROLE_PERMISSION": "vnf_instances:",
482 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
483 },
484 "subscriptions": {
485 "METHODS": ("GET", "POST"),
486 "ROLE_PERMISSION": "ns_subscriptions:",
487 "<ID>": {
488 "METHODS": ("GET", "DELETE"),
489 "ROLE_PERMISSION": "ns_subscriptions:id:",
490 },
491 },
tierno701018c2019-06-25 11:13:14 +0000492 }
493 },
almagiae47b9132022-05-17 14:12:22 +0200494 "vnflcm": {
495 "v1": {
496 "vnf_instances": {"METHODS": ("GET", "POST"),
497 "ROLE_PERMISSION": "vnflcm_instances:",
498 "<ID>": {"METHODS": ("GET", "DELETE"),
499 "ROLE_PERMISSION": "vnflcm_instances:id:",
500 "scale": {"METHODS": ("POST",),
501 "ROLE_PERMISSION": "vnflcm_instances:id:scale:"
502 },
503 "terminate": {"METHODS": ("POST",),
504 "ROLE_PERMISSION": "vnflcm_instances:id:terminate:"
505 },
506 "instantiate": {"METHODS": ("POST",),
507 "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:"
508 },
509 }
510 },
511 "vnf_lcm_op_occs": {"METHODS": ("GET",),
512 "ROLE_PERMISSION": "vnf_instances:opps:",
513 "<ID>": {"METHODS": ("GET",),
514 "ROLE_PERMISSION": "vnf_instances:opps:id:"
515 },
516 },
selvi.jf1004592022-04-29 05:42:35 +0000517 "subscriptions": {"METHODS": ("GET", "POST"),
518 "ROLE_PERMISSION": "vnflcm_subscriptions:",
519 "<ID>": {"METHODS": ("GET", "DELETE"),
520 "ROLE_PERMISSION": "vnflcm_subscriptions:id:"
521 }
522 },
almagiae47b9132022-05-17 14:12:22 +0200523 }
524 },
tierno701018c2019-06-25 11:13:14 +0000525 "nst": {
526 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100527 "netslice_templates_content": {
528 "METHODS": ("GET", "POST"),
529 "ROLE_PERMISSION": "slice_templates:",
530 "<ID>": {
531 "METHODS": ("GET", "PUT", "DELETE"),
532 "ROLE_PERMISSION": "slice_templates:id:",
533 },
534 },
535 "netslice_templates": {
536 "METHODS": ("GET", "POST"),
537 "ROLE_PERMISSION": "slice_templates:",
538 "<ID>": {
539 "METHODS": ("GET", "DELETE"),
540 "TODO": ("PATCH",),
541 "ROLE_PERMISSION": "slice_templates:id:",
542 "nst_content": {
543 "METHODS": ("GET", "PUT"),
544 "ROLE_PERMISSION": "slice_templates:id:content:",
545 },
546 "nst": {
547 "METHODS": ("GET",), # descriptor inside package
548 "ROLE_PERMISSION": "slice_templates:id:content:",
549 },
550 "artifacts": {
551 "METHODS": ("GET",),
552 "ROLE_PERMISSION": "slice_templates:id:content:",
553 "*": None,
554 },
555 },
556 },
557 "subscriptions": {
558 "TODO": ("GET", "POST"),
559 "<ID>": {"TODO": ("GET", "DELETE")},
560 },
tierno701018c2019-06-25 11:13:14 +0000561 }
562 },
563 "nsilcm": {
564 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100565 "netslice_instances_content": {
566 "METHODS": ("GET", "POST"),
567 "ROLE_PERMISSION": "slice_instances:",
568 "<ID>": {
569 "METHODS": ("GET", "DELETE"),
570 "ROLE_PERMISSION": "slice_instances:id:",
571 },
572 },
573 "netslice_instances": {
574 "METHODS": ("GET", "POST"),
575 "ROLE_PERMISSION": "slice_instances:",
576 "<ID>": {
577 "METHODS": ("GET", "DELETE"),
578 "ROLE_PERMISSION": "slice_instances:id:",
579 "terminate": {
580 "METHODS": ("POST",),
581 "ROLE_PERMISSION": "slice_instances:id:terminate:",
582 },
583 "instantiate": {
584 "METHODS": ("POST",),
585 "ROLE_PERMISSION": "slice_instances:id:instantiate:",
586 },
587 "action": {
588 "METHODS": ("POST",),
589 "ROLE_PERMISSION": "slice_instances:id:action:",
590 },
591 },
592 },
593 "nsi_lcm_op_occs": {
594 "METHODS": ("GET",),
595 "ROLE_PERMISSION": "slice_instances:opps:",
596 "<ID>": {
597 "METHODS": ("GET",),
598 "ROLE_PERMISSION": "slice_instances:opps:id:",
599 },
600 },
tierno701018c2019-06-25 11:13:14 +0000601 }
602 },
603 "nspm": {
604 "v1": {
605 "pm_jobs": {
606 "<ID>": {
607 "reports": {
garciadeblas4568a372021-03-24 09:19:48 +0100608 "<ID>": {
609 "METHODS": ("GET",),
610 "ROLE_PERMISSION": "reports:id:",
611 }
tierno701018c2019-06-25 11:13:14 +0000612 }
613 },
614 },
615 },
616 },
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000617 "nsfm": {
618 "v1": {
619 "alarms": {"METHODS": ("GET", "PATCH"),
620 "ROLE_PERMISSION": "alarms:",
621 "<ID>": {"METHODS": ("GET", "PATCH"),
622 "ROLE_PERMISSION": "alarms:id:",
623 },
624 }
625 },
626 },
tierno701018c2019-06-25 11:13:14 +0000627}
628
tiernoc94c3df2018-02-09 15:38:54 +0100629
630class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100631 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
632 Exception.__init__(self, message)
633 self.http_code = http_code
634
635
636class Server(object):
637 instance = 0
638 # to decode bytes to str
639 reader = getreader("utf-8")
640
641 def __init__(self):
642 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000643 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100644 self.engine = Engine(self.authenticator)
tiernoc94c3df2018-02-09 15:38:54 +0100645
tiernoc94c3df2018-02-09 15:38:54 +0100646 def _format_in(self, kwargs):
647 try:
648 indata = None
649 if cherrypy.request.body.length:
650 error_text = "Invalid input format "
651
652 if "Content-Type" in cherrypy.request.headers:
653 if "application/json" in cherrypy.request.headers["Content-Type"]:
654 error_text = "Invalid json format "
655 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100656 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100657 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
658 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100659 indata = yaml.load(
660 cherrypy.request.body, Loader=yaml.SafeLoader
661 )
gcalvinode4adfe2018-10-30 11:46:09 +0100662 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100663 elif (
664 "application/binary" in cherrypy.request.headers["Content-Type"]
665 or "application/gzip"
666 in cherrypy.request.headers["Content-Type"]
667 or "application/zip" in cherrypy.request.headers["Content-Type"]
668 or "text/plain" in cherrypy.request.headers["Content-Type"]
669 ):
tiernof27c79b2018-03-12 17:08:42 +0100670 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100671 elif (
672 "multipart/form-data"
673 in cherrypy.request.headers["Content-Type"]
674 ):
tiernoc94c3df2018-02-09 15:38:54 +0100675 if "descriptor_file" in kwargs:
676 filecontent = kwargs.pop("descriptor_file")
677 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100678 raise NbiException(
679 "empty file or content", HTTPStatus.BAD_REQUEST
680 )
tiernof27c79b2018-03-12 17:08:42 +0100681 indata = filecontent.file # .read()
tiernoc94c3df2018-02-09 15:38:54 +0100682 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100683 cherrypy.request.headers[
684 "Content-Type"
685 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100686 else:
687 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
688 # "Only 'Content-Type' of type 'application/json' or
689 # 'application/yaml' for input format are available")
690 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100691 indata = yaml.load(
692 cherrypy.request.body, Loader=yaml.SafeLoader
693 )
gcalvinode4adfe2018-10-30 11:46:09 +0100694 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100695 else:
696 error_text = "Invalid yaml format "
delacruzramob19cadc2019-10-08 10:18:02 +0200697 indata = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
gcalvinode4adfe2018-10-30 11:46:09 +0100698 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100699 if not indata:
700 indata = {}
701
tiernoc94c3df2018-02-09 15:38:54 +0100702 format_yaml = False
703 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
704 format_yaml = True
705
706 for k, v in kwargs.items():
707 if isinstance(v, str):
708 if v == "":
709 kwargs[k] = None
710 elif format_yaml:
711 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200712 kwargs[k] = yaml.load(v, Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200713 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100714 pass
garciadeblas4568a372021-03-24 09:19:48 +0100715 elif (
716 k.endswith(".gt")
717 or k.endswith(".lt")
718 or k.endswith(".gte")
719 or k.endswith(".lte")
720 ):
tiernoc94c3df2018-02-09 15:38:54 +0100721 try:
722 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200723 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100724 try:
725 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200726 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100727 pass
728 elif v.find(",") > 0:
729 kwargs[k] = v.split(",")
730 elif isinstance(v, (list, tuple)):
731 for index in range(0, len(v)):
732 if v[index] == "":
733 v[index] = None
734 elif format_yaml:
735 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200736 v[index] = yaml.load(v[index], Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200737 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100738 pass
739
tiernof27c79b2018-03-12 17:08:42 +0100740 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100741 except (ValueError, yaml.YAMLError) as exc:
742 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
743 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100744 raise NbiException(
745 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
746 )
tiernob92094f2018-05-11 13:44:22 +0200747 except Exception as exc:
748 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100749
750 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000751 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100752 """
753 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100754 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000755 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000756 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100757 :return: None
758 """
tierno0f98af52018-03-19 10:28:22 +0100759 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100760 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100761 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100762 return html.format(
763 data, cherrypy.request, cherrypy.response, token_info
764 )
tierno09c073e2018-04-26 13:36:48 +0200765 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100766 return
767 elif hasattr(data, "read"): # file object
768 if _format:
769 cherrypy.response.headers["Content-Type"] = _format
770 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100771 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100772 else:
garciadeblas4568a372021-03-24 09:19:48 +0100773 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100774 # TODO check that cherrypy close file. If not implement pending things to close per thread next
775 return data
tierno0f98af52018-03-19 10:28:22 +0100776 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000777 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100778 return html.format(
779 data, cherrypy.request, cherrypy.response, token_info
780 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000781 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100782 pass
garciadeblas4568a372021-03-24 09:19:48 +0100783 elif "application/json" in accept or (
784 cherrypy.response.status and cherrypy.response.status >= 300
785 ):
786 cherrypy.response.headers[
787 "Content-Type"
788 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000789 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100790 return a.encode("utf8")
791 cherrypy.response.headers["Content-Type"] = "application/yaml"
792 return yaml.safe_dump(
793 data,
794 explicit_start=True,
795 indent=4,
796 default_flow_style=False,
797 tags=False,
798 encoding="utf-8",
799 allow_unicode=True,
800 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +0100801
802 @cherrypy.expose
803 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +0000804 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100805 try:
806 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +0000807 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +0100808 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +0100809 else:
garciadeblas4568a372021-03-24 09:19:48 +0100810 raise cherrypy.HTTPError(
811 HTTPStatus.METHOD_NOT_ALLOWED.value,
812 "Method {} not allowed for tokens".format(cherrypy.request.method),
813 )
tiernoc94c3df2018-02-09 15:38:54 +0100814
tierno701018c2019-06-25 11:13:14 +0000815 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100816
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100817 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +0000818 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +0100819 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +0000820 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100821
822 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +0200823 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +0200824 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +0200825 try:
826 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +0100827 raise NbiException(
828 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
829 )
tierno55945e72018-04-06 16:40:27 +0200830 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +0100831 raise NbiException(
832 "Invalid URL or query string for version",
833 HTTPStatus.METHOD_NOT_ALLOWED,
834 )
tierno9c630112019-08-29 14:21:41 +0000835 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +0000836 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
837 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +0200838 except NbiException as e:
839 cherrypy.response.status = e.http_code.value
840 problem_details = {
841 "code": e.http_code.name,
842 "status": e.http_code.value,
843 "detail": str(e),
844 }
845 return self._format_out(problem_details, None)
846
tierno12eac3c2020-03-19 23:22:08 +0000847 def domain(self):
848 try:
849 domains = {
garciadeblas4568a372021-03-24 09:19:48 +0100850 "user_domain_name": cherrypy.tree.apps["/osm"]
851 .config["authentication"]
852 .get("user_domain_name"),
853 "project_domain_name": cherrypy.tree.apps["/osm"]
854 .config["authentication"]
855 .get("project_domain_name"),
856 }
tierno12eac3c2020-03-19 23:22:08 +0000857 return self._format_out(domains)
858 except NbiException as e:
859 cherrypy.response.status = e.http_code.value
860 problem_details = {
861 "code": e.http_code.name,
862 "status": e.http_code.value,
863 "detail": str(e),
864 }
865 return self._format_out(problem_details, None)
866
tiernoa5035702019-07-29 08:54:42 +0000867 @staticmethod
868 def _format_login(token_info):
869 """
870 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
871 log this information
872 :param token_info: Dictionary with token content
873 :return: None
874 """
875 cherrypy.request.login = token_info.get("username", "-")
876 if token_info.get("project_name"):
877 cherrypy.request.login += "/" + token_info["project_name"]
878 if token_info.get("id"):
879 cherrypy.request.login += ";session=" + token_info["id"][0:12]
880
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000881 # NS Fault Management
882 @cherrypy.expose
883 def nsfm(self, version=None, topic=None, uuid=None, project_name=None, ns_id=None, *args, **kwargs):
884 if topic == 'alarms':
885 try:
886 method = cherrypy.request.method
887 role_permission = self._check_valid_url_method(method, "nsfm", version, topic, None, None, *args)
888 query_string_operations = self._extract_query_string_operations(kwargs, method)
889
890 self.authenticator.authorize(role_permission, query_string_operations, None)
891
892 # to handle get request
893 if cherrypy.request.method == 'GET':
894 # if request is on basis of uuid
895 if uuid and uuid != 'None':
896 try:
897 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
898 alarm_action = self.engine.db.get_one("alarms_action", {"uuid": uuid})
899 alarm.update(alarm_action)
900 vnf = self.engine.db.get_one("vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]})
901 alarm["vnf-id"] = vnf["_id"]
902 return self._format_out(str(alarm))
903 except Exception:
904 return self._format_out("Please provide valid alarm uuid")
905 elif ns_id and ns_id != 'None':
906 # if request is on basis of ns_id
907 try:
908 alarms = self.engine.db.get_list("alarms", {"tags.ns_id": ns_id})
909 for alarm in alarms:
910 alarm_action = self.engine.db.get_one("alarms_action", {"uuid": alarm['uuid']})
911 alarm.update(alarm_action)
912 return self._format_out(str(alarms))
913 except Exception:
914 return self._format_out("Please provide valid ns id")
915 else:
916 # to return only alarm which are related to given project
917 project = self.engine.db.get_one("projects", {"name": project_name})
918 project_id = project.get('_id')
919 ns_list = self.engine.db.get_list("nsrs", {"_admin.projects_read": project_id})
920 ns_ids = []
921 for ns in ns_list:
922 ns_ids.append(ns.get("_id"))
923 alarms = self.engine.db.get_list("alarms")
924 alarm_list = [alarm for alarm in alarms if alarm["tags"]["ns_id"] in ns_ids]
925 for alrm in alarm_list:
926 action = self.engine.db.get_one("alarms_action", {"uuid": alrm.get("uuid")})
927 alrm.update(action)
928 return self._format_out(str(alarm_list))
929 # to handle patch request for alarm update
930 elif cherrypy.request.method == 'PATCH':
931 data = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
932 try:
933 # check if uuid is valid
934 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
935 except Exception:
936 return self._format_out("Please provide valid alarm uuid.")
937 if data.get("is_enable") is not None:
938 if data.get("is_enable"):
939 alarm_status = 'ok'
940 else:
941 alarm_status = 'disabled'
942 self.engine.db.set_one("alarms", {"uuid": data.get("uuid")},
943 {"alarm_status": alarm_status})
944 else:
945 self.engine.db.set_one("alarms", {"uuid": data.get("uuid")},
946 {"threshold": data.get("threshold")})
947 return self._format_out("Alarm updated")
948 except Exception as e:
949 cherrypy.response.status = e.http_code.value
950 if isinstance(e, (NbiException, EngineException, DbException, FsException, MsgException, AuthException,
951 ValidationError, AuthconnException)):
952 http_code_value = cherrypy.response.status = e.http_code.value
953 http_code_name = e.http_code.name
954 cherrypy.log("Exception {}".format(e))
955 else:
956 http_code_value = cherrypy.response.status = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
957 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
958 http_code_name = HTTPStatus.BAD_REQUEST.name
959 problem_details = {
960 "code": http_code_name,
961 "status": http_code_value,
962 "detail": str(e),
963 }
964 return self._format_out(problem_details)
965
tierno55945e72018-04-06 16:40:27 +0200966 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +0100967 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +0000968 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100969 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +0100970 indata = self._format_in(kwargs)
971 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +0100972 raise NbiException(
973 "Expected application/yaml or application/json Content-Type",
974 HTTPStatus.BAD_REQUEST,
975 )
tiernoa5035702019-07-29 08:54:42 +0000976
977 if method == "GET":
978 token_info = self.authenticator.authorize()
979 # for logging
980 self._format_login(token_info)
981 if token_id:
982 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +0100983 else:
tiernoa5035702019-07-29 08:54:42 +0000984 outdata = self.authenticator.get_token_list(token_info)
985 elif method == "POST":
986 try:
987 token_info = self.authenticator.authorize()
988 except Exception:
989 token_info = None
990 if kwargs:
991 indata.update(kwargs)
992 # This is needed to log the user when authentication fails
993 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +0100994 outdata = token_info = self.authenticator.new_token(
995 token_info, indata, cherrypy.request.remote
996 )
997 cherrypy.session["Authorization"] = outdata["_id"]
tiernoa5035702019-07-29 08:54:42 +0000998 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
999 # for logging
1000 self._format_login(token_info)
selvi.ja9a1fc82022-04-04 06:54:30 +00001001 # password expiry check
1002 if self.authenticator.check_password_expiry(outdata):
1003 outdata = {"id": outdata["id"],
1004 "message": "change_password",
1005 "user_id": outdata["user_id"]
1006 }
tiernoa5035702019-07-29 08:54:42 +00001007 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1008 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
1009 elif method == "DELETE":
1010 if not token_id and "id" in kwargs:
1011 token_id = kwargs["id"]
1012 elif not token_id:
1013 token_info = self.authenticator.authorize()
1014 # for logging
1015 self._format_login(token_info)
1016 token_id = token_info["_id"]
1017 outdata = self.authenticator.del_token(token_id)
1018 token_info = None
garciadeblas4568a372021-03-24 09:19:48 +01001019 cherrypy.session["Authorization"] = "logout"
tiernoa5035702019-07-29 08:54:42 +00001020 # cherrypy.response.cookie["Authorization"] = token_id
1021 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1022 else:
garciadeblas4568a372021-03-24 09:19:48 +01001023 raise NbiException(
1024 "Method {} not allowed for token".format(method),
1025 HTTPStatus.METHOD_NOT_ALLOWED,
1026 )
tiernoa5035702019-07-29 08:54:42 +00001027 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001028
1029 @cherrypy.expose
1030 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001031 if not cherrypy.config.get("server.enable_test") or (
1032 isinstance(cherrypy.config["server.enable_test"], str)
1033 and cherrypy.config["server.enable_test"].lower() == "false"
1034 ):
tierno4836bac2020-01-15 14:41:48 +00001035 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1036 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001037 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001038 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001039 return (
1040 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1041 "sleep/<time>\nmessage/topic\n</pre></html>"
1042 )
tiernof27c79b2018-03-12 17:08:42 +01001043
1044 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001045 try:
1046 # self.engine.load_dbase(cherrypy.request.app.config)
1047 self.engine.create_admin()
1048 return "Done. User 'admin', password 'admin' created"
1049 except Exception:
1050 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1051 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001052 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001053 return cherrypy.lib.static.serve_file(
1054 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1055 "text/plain",
1056 "attachment",
1057 )
tiernof27c79b2018-03-12 17:08:42 +01001058 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001059 f_path = (
1060 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1061 )
tiernof27c79b2018-03-12 17:08:42 +01001062 f = open(f_path, "r")
1063 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001064 return f
tierno55945e72018-04-06 16:40:27 +02001065
tiernof27c79b2018-03-12 17:08:42 +01001066 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001067 deleted_info = self.engine.db.del_list(args[1], kwargs)
1068 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1069 elif len(args) and args[0] == "fs-clear":
1070 if len(args) >= 2:
1071 folders = (args[1],)
1072 else:
1073 folders = self.engine.fs.dir_ls(".")
1074 for folder in folders:
1075 self.engine.fs.file_delete(folder)
1076 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001077 elif args and args[0] == "login":
1078 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001079 cherrypy.response.headers[
1080 "WWW-Authenticate"
1081 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001082 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1083 elif args and args[0] == "login2":
1084 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001085 cherrypy.response.headers[
1086 "WWW-Authenticate"
1087 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001088 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1089 elif args and args[0] == "sleep":
1090 sleep_time = 5
1091 try:
1092 sleep_time = int(args[1])
1093 except Exception:
1094 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1095 return self._format_out("Database already initialized")
1096 thread_info = cherrypy.thread_data
1097 print(thread_info)
1098 time.sleep(sleep_time)
1099 # thread_info
1100 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001101 main_topic = args[1]
1102 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001103 try:
garciadeblas4568a372021-03-24 09:19:48 +01001104 if cherrypy.request.method == "POST":
delacruzramob19cadc2019-10-08 10:18:02 +02001105 to_send = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
tierno55945e72018-04-06 16:40:27 +02001106 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001107 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001108 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001109 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001110 for k, v in kwargs.items():
tiernof1509b22020-05-12 14:32:37 +00001111 v_dict = yaml.load(v, Loader=yaml.SafeLoader)
1112 self.engine.msg.write(main_topic, k, v_dict)
1113 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001114 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001115 return_text += "Error: " + str(e)
1116 return_text += "</pre></html>\n"
1117 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001118
1119 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001120 "<html><pre>\nheaders:\n args: {}\n".format(args)
1121 + " kwargs: {}\n".format(kwargs)
1122 + " headers: {}\n".format(cherrypy.request.headers)
1123 + " path_info: {}\n".format(cherrypy.request.path_info)
1124 + " query_string: {}\n".format(cherrypy.request.query_string)
1125 + " session: {}\n".format(cherrypy.session)
1126 + " cookie: {}\n".format(cherrypy.request.cookie)
1127 + " method: {}\n".format(cherrypy.request.method)
1128 + " session: {}\n".format(cherrypy.session.get("fieldname"))
1129 + " body:\n"
1130 )
tiernoc94c3df2018-02-09 15:38:54 +01001131 return_text += " length: {}\n".format(cherrypy.request.body.length)
1132 if cherrypy.request.body.length:
1133 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001134 str(
1135 cherrypy.request.body.read(
1136 int(cherrypy.request.headers.get("Content-Length", 0))
1137 )
1138 )
1139 )
tiernoc94c3df2018-02-09 15:38:54 +01001140 if thread_info:
1141 return_text += "thread: {}\n".format(thread_info)
1142 return_text += "</pre></html>"
1143 return return_text
1144
tierno701018c2019-06-25 11:13:14 +00001145 @staticmethod
1146 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001147 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001148 raise NbiException(
1149 "URL must contain at least 'main_topic/version/topic'",
1150 HTTPStatus.METHOD_NOT_ALLOWED,
1151 )
tiernof27c79b2018-03-12 17:08:42 +01001152
tierno701018c2019-06-25 11:13:14 +00001153 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001154 for arg in args:
1155 if arg is None:
1156 break
1157 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001158 raise NbiException(
1159 "URL contains unexpected extra items '{}'".format(arg),
1160 HTTPStatus.METHOD_NOT_ALLOWED,
1161 )
tiernof27c79b2018-03-12 17:08:42 +01001162
1163 if arg in reference:
1164 reference = reference[arg]
1165 elif "<ID>" in reference:
1166 reference = reference["<ID>"]
1167 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001168 # if there is content
1169 if reference["*"]:
1170 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001171 break
1172 else:
garciadeblas4568a372021-03-24 09:19:48 +01001173 raise NbiException(
1174 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1175 )
tiernof27c79b2018-03-12 17:08:42 +01001176 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001177 raise NbiException(
1178 "Method {} not supported yet for this URL".format(method),
1179 HTTPStatus.NOT_IMPLEMENTED,
1180 )
tierno2236d202018-05-16 19:05:16 +02001181 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001182 raise NbiException(
1183 "Method {} not supported for this URL".format(method),
1184 HTTPStatus.METHOD_NOT_ALLOWED,
1185 )
tierno701018c2019-06-25 11:13:14 +00001186 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001187
1188 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001189 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001190 """
1191 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001192 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001193 :param version:
tiernob24258a2018-10-04 18:39:49 +02001194 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001195 :param id:
1196 :return: None
1197 """
1198 # 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 +01001199 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1200 main_topic, version, topic, id
1201 )
tiernof27c79b2018-03-12 17:08:42 +01001202 return
1203
tierno65ca36d2019-02-12 19:27:52 +01001204 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001205 def _extract_query_string_operations(kwargs, method):
1206 """
1207
1208 :param kwargs:
1209 :return:
1210 """
1211 query_string_operations = []
1212 if kwargs:
1213 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1214 if qs in kwargs and kwargs[qs].lower() != "false":
1215 query_string_operations.append(qs.lower() + ":" + method.lower())
1216 return query_string_operations
1217
1218 @staticmethod
1219 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001220 """
1221 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1222 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001223 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001224 :param kwargs: query string input.
1225 :param method: http method: GET, POSST, PUT, ...
1226 :param _id:
1227 :return: admin_query dictionary with keys:
1228 public: True, False or None
1229 force: True or False
1230 project_id: tuple with projects used for accessing an element
1231 set_project: tuple with projects that a created element will belong to
1232 method: show, list, delete, write
1233 """
garciadeblas4568a372021-03-24 09:19:48 +01001234 admin_query = {
1235 "force": False,
1236 "project_id": (token_info["project_id"],),
1237 "username": token_info["username"],
1238 "admin": token_info["admin"],
1239 "public": None,
1240 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1241 }
tierno65ca36d2019-02-12 19:27:52 +01001242 if kwargs:
1243 # FORCE
1244 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001245 if (
1246 kwargs["FORCE"].lower() != "false"
1247 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001248 admin_query["force"] = True
1249 del kwargs["FORCE"]
1250 # PUBLIC
1251 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001252 if (
1253 kwargs["PUBLIC"].lower() != "false"
1254 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001255 admin_query["public"] = True
1256 else:
1257 admin_query["public"] = False
1258 del kwargs["PUBLIC"]
1259 # ADMIN
1260 if "ADMIN" in kwargs:
1261 behave_as = kwargs.pop("ADMIN")
1262 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001263 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001264 raise NbiException(
1265 "Only admin projects can use 'ADMIN' query string",
1266 HTTPStatus.UNAUTHORIZED,
1267 )
1268 if (
1269 not behave_as or behave_as.lower() == "true"
1270 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001271 admin_query["project_id"] = ()
1272 elif isinstance(behave_as, (list, tuple)):
1273 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001274 else: # isinstance(behave_as, str)
1275 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001276 if "SET_PROJECT" in kwargs:
1277 set_project = kwargs.pop("SET_PROJECT")
1278 if not set_project:
1279 admin_query["set_project"] = list(admin_query["project_id"])
1280 else:
1281 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001282 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001283 if admin_query["project_id"]:
1284 for p in set_project:
1285 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001286 raise NbiException(
1287 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1288 "'ADMIN='{p}'".format(p=p),
1289 HTTPStatus.UNAUTHORIZED,
1290 )
tierno65ca36d2019-02-12 19:27:52 +01001291 admin_query["set_project"] = set_project
1292
1293 # PROJECT_READ
1294 # if "PROJECT_READ" in kwargs:
1295 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001296 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001297 if method == "GET":
1298 if _id:
1299 admin_query["method"] = "show"
1300 else:
1301 admin_query["method"] = "list"
1302 elif method == "DELETE":
1303 admin_query["method"] = "delete"
1304 else:
1305 admin_query["method"] = "write"
1306 return admin_query
1307
tiernoc94c3df2018-02-09 15:38:54 +01001308 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001309 def default(
1310 self,
1311 main_topic=None,
1312 version=None,
1313 topic=None,
1314 _id=None,
1315 item=None,
1316 *args,
1317 **kwargs
1318 ):
tierno701018c2019-06-25 11:13:14 +00001319 token_info = None
tiernof27c79b2018-03-12 17:08:42 +01001320 outdata = None
1321 _format = None
tierno0f98af52018-03-19 10:28:22 +01001322 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001323 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001324 rollback = []
tierno701018c2019-06-25 11:13:14 +00001325 engine_session = None
tiernoc94c3df2018-02-09 15:38:54 +01001326 try:
tiernob24258a2018-10-04 18:39:49 +02001327 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001328 raise NbiException(
1329 "URL must contain at least 'main_topic/version/topic'",
1330 HTTPStatus.METHOD_NOT_ALLOWED,
1331 )
1332 if main_topic not in (
1333 "admin",
1334 "vnfpkgm",
1335 "nsd",
1336 "nslcm",
1337 "pdu",
1338 "nst",
1339 "nsilcm",
1340 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001341 "vnflcm",
garciadeblas4568a372021-03-24 09:19:48 +01001342 ):
1343 raise NbiException(
1344 "URL main_topic '{}' not supported".format(main_topic),
1345 HTTPStatus.METHOD_NOT_ALLOWED,
1346 )
1347 if version != "v1":
1348 raise NbiException(
1349 "URL version '{}' not supported".format(version),
1350 HTTPStatus.METHOD_NOT_ALLOWED,
1351 )
tiernoc94c3df2018-02-09 15:38:54 +01001352
garciadeblas4568a372021-03-24 09:19:48 +01001353 if (
1354 kwargs
1355 and "METHOD" in kwargs
1356 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1357 ):
tiernof27c79b2018-03-12 17:08:42 +01001358 method = kwargs.pop("METHOD")
1359 else:
1360 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001361
garciadeblas4568a372021-03-24 09:19:48 +01001362 role_permission = self._check_valid_url_method(
1363 method, main_topic, version, topic, _id, item, *args
1364 )
1365 query_string_operations = self._extract_query_string_operations(
1366 kwargs, method
1367 )
tiernob24258a2018-10-04 18:39:49 +02001368 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001369 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001370 token_info = self.authenticator.authorize(
1371 role_permission, query_string_operations, _id
1372 )
tierno12eac3c2020-03-19 23:22:08 +00001373 if main_topic == "admin" and topic == "domains":
1374 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001375 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001376 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001377 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301378
vijay.r35ef2f72019-04-30 17:55:49 +05301379 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001380 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001381
tiernob24258a2018-10-04 18:39:49 +02001382 if main_topic == "nsd":
1383 engine_topic = "nsds"
1384 elif main_topic == "vnfpkgm":
1385 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001386 if topic == "vnfpkg_op_occs":
1387 engine_topic = "vnfpkgops"
1388 if topic == "vnf_packages" and item == "action":
1389 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001390 elif main_topic == "nslcm":
1391 engine_topic = "nsrs"
1392 if topic == "ns_lcm_op_occs":
1393 engine_topic = "nslcmops"
1394 if topic == "vnfrs" or topic == "vnf_instances":
1395 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001396 elif main_topic == "vnflcm":
1397 if topic == "vnf_lcm_op_occs":
1398 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001399 elif main_topic == "nst":
1400 engine_topic = "nsts"
1401 elif main_topic == "nsilcm":
1402 engine_topic = "nsis"
1403 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001404 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001405 elif main_topic == "pdu":
1406 engine_topic = "pdus"
garciadeblas4568a372021-03-24 09:19:48 +01001407 if (
1408 engine_topic == "vims"
1409 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001410 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001411
preethika.p329b8182020-04-22 12:25:39 +05301412 if topic == "subscriptions":
1413 engine_topic = main_topic + "_" + topic
1414
tiernoc94c3df2018-02-09 15:38:54 +01001415 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001416 if item in (
1417 "nsd_content",
1418 "package_content",
1419 "artifacts",
1420 "vnfd",
1421 "nsd",
1422 "nst",
1423 "nst_content",
1424 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001425 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001426 path = "$DESCRIPTOR"
1427 elif args:
1428 path = args
tiernob24258a2018-10-04 18:39:49 +02001429 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001430 path = ()
1431 else:
1432 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001433 file, _format = self.engine.get_file(
1434 engine_session,
1435 engine_topic,
1436 _id,
1437 path,
1438 cherrypy.request.headers.get("Accept"),
1439 )
tiernof27c79b2018-03-12 17:08:42 +01001440 outdata = file
1441 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001442 outdata = self.engine.get_item_list(
1443 engine_session, engine_topic, kwargs, api_req=True
1444 )
tiernoc94c3df2018-02-09 15:38:54 +01001445 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301446 if item == "reports":
1447 # TODO check that project_id (_id in this context) has permissions
1448 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301449 filter_q = None
1450 if "vcaStatusRefresh" in kwargs:
1451 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
1452 outdata = self.engine.get_item(engine_session, engine_topic, _id, filter_q, True)
delacruzramo271d2002019-12-02 21:00:37 +01001453
tiernof27c79b2018-03-12 17:08:42 +01001454 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001455 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001456 if topic in (
1457 "ns_descriptors_content",
1458 "vnf_packages_content",
1459 "netslice_templates_content",
1460 ):
tiernof27c79b2018-03-12 17:08:42 +01001461 _id = cherrypy.request.headers.get("Transaction-Id")
1462 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001463 _id, _ = self.engine.new_item(
1464 rollback,
1465 engine_session,
1466 engine_topic,
1467 {},
1468 None,
1469 cherrypy.request.headers,
1470 )
1471 completed = self.engine.upload_content(
1472 engine_session,
1473 engine_topic,
1474 _id,
1475 indata,
1476 kwargs,
1477 cherrypy.request.headers,
1478 )
tiernof27c79b2018-03-12 17:08:42 +01001479 if completed:
tiernob24258a2018-10-04 18:39:49 +02001480 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001481 else:
1482 cherrypy.response.headers["Transaction-Id"] = _id
1483 outdata = {"id": _id}
tiernob24258a2018-10-04 18:39:49 +02001484 elif topic == "ns_instances_content":
1485 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001486 _id, _ = self.engine.new_item(
1487 rollback, engine_session, engine_topic, indata, kwargs
1488 )
tiernob24258a2018-10-04 18:39:49 +02001489 # creates nslcmop
1490 indata["lcmOperationType"] = "instantiate"
1491 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001492 nslcmop_id, _ = self.engine.new_item(
1493 rollback, engine_session, "nslcmops", indata, None
1494 )
tiernob24258a2018-10-04 18:39:49 +02001495 self._set_location_header(main_topic, version, topic, _id)
kuuse078f55e2019-05-16 19:24:21 +02001496 outdata = {"id": _id, "nslcmop_id": nslcmop_id}
tiernob24258a2018-10-04 18:39:49 +02001497 elif topic == "ns_instances" and item:
1498 indata["lcmOperationType"] = item
1499 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001500 _id, _ = self.engine.new_item(
1501 rollback, engine_session, "nslcmops", indata, kwargs
1502 )
1503 self._set_location_header(
1504 main_topic, version, "ns_lcm_op_occs", _id
1505 )
tierno65acb4d2018-04-06 16:42:40 +02001506 outdata = {"id": _id}
1507 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001508 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001509 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001510 _id, _ = self.engine.new_item(
1511 rollback, engine_session, engine_topic, indata, kwargs
1512 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001513 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001514 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001515 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001516 nsilcmop_id, _ = self.engine.new_item(
1517 rollback, engine_session, "nsilcmops", indata, kwargs
1518 )
kuuse078f55e2019-05-16 19:24:21 +02001519 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001520 elif topic == "netslice_instances" and item:
1521 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001522 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001523 _id, _ = self.engine.new_item(
1524 rollback, engine_session, "nsilcmops", indata, kwargs
1525 )
1526 self._set_location_header(
1527 main_topic, version, "nsi_lcm_op_occs", _id
1528 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001529 outdata = {"id": _id}
1530 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001531 elif topic == "vnf_packages" and item == "action":
1532 indata["lcmOperationType"] = item
1533 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001534 _id, _ = self.engine.new_item(
1535 rollback, engine_session, "vnfpkgops", indata, kwargs
1536 )
1537 self._set_location_header(
1538 main_topic, version, "vnfpkg_op_occs", _id
1539 )
delacruzramo271d2002019-12-02 21:00:37 +01001540 outdata = {"id": _id}
1541 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301542 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001543 _id, _ = self.engine.new_item(
1544 rollback, engine_session, engine_topic, indata, kwargs
1545 )
preethika.p329b8182020-04-22 12:25:39 +05301546 self._set_location_header(main_topic, version, topic, _id)
1547 link = {}
1548 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001549 outdata = {
1550 "id": _id,
1551 "filter": indata["filter"],
1552 "callbackUri": indata["CallbackUri"],
1553 "_links": link,
1554 }
preethika.p329b8182020-04-22 12:25:39 +05301555 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001556 elif topic == "vnf_instances" and item:
1557 indata["lcmOperationType"] = item
1558 indata["vnfInstanceId"] = _id
1559 _id, _ = self.engine.new_item(rollback, engine_session, "vnflcmops", indata, kwargs)
1560 self._set_location_header(main_topic, version, "vnf_lcm_op_occs", _id)
1561 outdata = {"id": _id}
1562 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernof27c79b2018-03-12 17:08:42 +01001563 else:
garciadeblas4568a372021-03-24 09:19:48 +01001564 _id, op_id = self.engine.new_item(
1565 rollback,
1566 engine_session,
1567 engine_topic,
1568 indata,
1569 kwargs,
1570 cherrypy.request.headers,
1571 )
tiernob24258a2018-10-04 18:39:49 +02001572 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001573 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00001574 if op_id:
1575 outdata["op_id"] = op_id
1576 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001577 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02001578
tiernoc94c3df2018-02-09 15:38:54 +01001579 elif method == "DELETE":
1580 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001581 outdata = self.engine.del_item_list(
1582 engine_session, engine_topic, kwargs
1583 )
tierno09c073e2018-04-26 13:36:48 +02001584 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01001585 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00001586 # for NS NSI generate an operation
1587 op_id = None
tierno701018c2019-06-25 11:13:14 +00001588 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02001589 nslcmop_desc = {
1590 "lcmOperationType": "terminate",
1591 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001592 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02001593 }
garciadeblas4568a372021-03-24 09:19:48 +01001594 op_id, _ = self.engine.new_item(
1595 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
1596 )
tierno22577432020-04-08 15:16:57 +00001597 if op_id:
1598 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001599 elif (
1600 topic == "netslice_instances_content"
1601 and not engine_session["force"]
1602 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001603 nsilcmop_desc = {
1604 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02001605 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001606 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02001607 }
garciadeblas4568a372021-03-24 09:19:48 +01001608 op_id, _ = self.engine.new_item(
1609 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
1610 )
tierno22577432020-04-08 15:16:57 +00001611 if op_id:
1612 outdata = {"_id": op_id}
1613 # if there is not any deletion in process, delete
1614 if not op_id:
1615 op_id = self.engine.del_item(engine_session, engine_topic, _id)
1616 if op_id:
1617 outdata = {"op_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001618 cherrypy.response.status = (
1619 HTTPStatus.ACCEPTED.value
1620 if op_id
1621 else HTTPStatus.NO_CONTENT.value
1622 )
tierno09c073e2018-04-26 13:36:48 +02001623
tierno7ae10112018-05-18 14:36:02 +02001624 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00001625 op_id = None
tierno701018c2019-06-25 11:13:14 +00001626 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01001627 raise NbiException(
1628 "Nothing to update. Provide payload and/or query string",
1629 HTTPStatus.BAD_REQUEST,
1630 )
1631 if (
1632 item in ("nsd_content", "package_content", "nst_content")
1633 and method == "PUT"
1634 ):
1635 completed = self.engine.upload_content(
1636 engine_session,
1637 engine_topic,
1638 _id,
1639 indata,
1640 kwargs,
1641 cherrypy.request.headers,
1642 )
tiernof27c79b2018-03-12 17:08:42 +01001643 if not completed:
1644 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01001645 else:
garciadeblas4568a372021-03-24 09:19:48 +01001646 op_id = self.engine.edit_item(
1647 engine_session, engine_topic, _id, indata, kwargs
1648 )
tiernobdebce92019-07-01 15:36:49 +00001649
1650 if op_id:
1651 cherrypy.response.status = HTTPStatus.ACCEPTED.value
1652 outdata = {"op_id": op_id}
1653 else:
1654 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
1655 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01001656 else:
garciadeblas4568a372021-03-24 09:19:48 +01001657 raise NbiException(
1658 "Method {} not allowed".format(method),
1659 HTTPStatus.METHOD_NOT_ALLOWED,
1660 )
tiernoa6bb45d2019-06-14 09:45:39 +00001661
1662 # if Role information changes, it is needed to reload the information of roles
1663 if topic == "roles" and method != "GET":
1664 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01001665
garciadeblas4568a372021-03-24 09:19:48 +01001666 if (
1667 topic == "projects"
1668 and method == "DELETE"
1669 or topic in ["users", "roles"]
1670 and method in ["PUT", "PATCH", "DELETE"]
1671 ):
delacruzramoad682a52019-12-10 16:26:34 +01001672 self.authenticator.remove_token_from_cache()
1673
tierno701018c2019-06-25 11:13:14 +00001674 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02001675 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01001676 if isinstance(
1677 e,
1678 (
1679 NbiException,
1680 EngineException,
1681 DbException,
1682 FsException,
1683 MsgException,
1684 AuthException,
1685 ValidationError,
1686 AuthconnException,
1687 ),
1688 ):
tiernob24258a2018-10-04 18:39:49 +02001689 http_code_value = cherrypy.response.status = e.http_code.value
1690 http_code_name = e.http_code.name
1691 cherrypy.log("Exception {}".format(e))
1692 else:
garciadeblas4568a372021-03-24 09:19:48 +01001693 http_code_value = (
1694 cherrypy.response.status
1695 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01001696 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02001697 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02001698 if hasattr(outdata, "close"): # is an open file
1699 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02001700 error_text = str(e)
1701 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02001702 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02001703 try:
tiernocc103432018-10-19 14:10:35 +02001704 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01001705 self.engine.db.set_one(
1706 rollback_item["topic"],
1707 {"_id": rollback_item["_id"]},
1708 rollback_item["content"],
1709 fail_on_empty=False,
1710 )
preethika.p329b8182020-04-22 12:25:39 +05301711 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01001712 self.engine.db.del_list(
1713 rollback_item["topic"],
1714 rollback_item["filter"],
1715 fail_on_empty=False,
1716 )
tiernocc103432018-10-19 14:10:35 +02001717 else:
garciadeblas4568a372021-03-24 09:19:48 +01001718 self.engine.db.del_one(
1719 rollback_item["topic"],
1720 {"_id": rollback_item["_id"]},
1721 fail_on_empty=False,
1722 )
tierno3ace63c2018-05-03 17:51:43 +02001723 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01001724 rollback_error_text = "Rollback Exception {}: {}".format(
1725 rollback_item, e2
1726 )
tiernob24258a2018-10-04 18:39:49 +02001727 cherrypy.log(rollback_error_text)
1728 error_text += ". " + rollback_error_text
1729 # if isinstance(e, MsgException):
1730 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
1731 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01001732 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02001733 "code": http_code_name,
1734 "status": http_code_value,
1735 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01001736 }
tierno701018c2019-06-25 11:13:14 +00001737 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001738 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00001739 finally:
1740 if token_info:
1741 self._format_login(token_info)
1742 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
1743 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
1744 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01001745 cherrypy.request.login += ";{}={}".format(
1746 logging_id, outdata[logging_id][:36]
1747 )
tiernoc94c3df2018-02-09 15:38:54 +01001748
1749
tiernoc94c3df2018-02-09 15:38:54 +01001750def _start_service():
1751 """
1752 Callback function called when cherrypy.engine starts
1753 Override configuration with env variables
1754 Set database, storage, message configuration
1755 Init database with admin/admin user password
1756 """
tierno932499c2019-01-28 17:28:10 +00001757 global nbi_server
1758 global subscription_thread
tiernoc94c3df2018-02-09 15:38:54 +01001759 cherrypy.log.error("Starting osm_nbi")
1760 # update general cherrypy configuration
1761 update_dict = {}
1762
garciadeblas4568a372021-03-24 09:19:48 +01001763 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01001764 for k, v in environ.items():
1765 if not k.startswith("OSMNBI_"):
1766 continue
tiernoe1281182018-05-22 12:24:36 +02001767 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01001768 if not k2:
1769 continue
1770 try:
1771 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01001772 if k == "OSMNBI_STATIC_DIR":
1773 engine_config["/static"]["tools.staticdir.dir"] = v
1774 engine_config["/static"]["tools.staticdir.on"] = True
1775 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
1776 update_dict["server.socket_port"] = int(v)
1777 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
1778 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02001779 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01001780 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001781 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02001782 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001783 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01001784 engine_config[k1][k2] = int(v)
1785 else:
1786 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001787
tiernoc94c3df2018-02-09 15:38:54 +01001788 except ValueError as e:
1789 cherrypy.log.error("Ignoring environ '{}': " + str(e))
1790 except Exception as e:
1791 cherrypy.log.warn("skipping environ '{}' on exception '{}'".format(k, e))
1792
1793 if update_dict:
1794 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02001795 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001796
1797 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01001798 log_format_simple = (
1799 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
1800 )
1801 log_formatter_simple = logging.Formatter(
1802 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
1803 )
tiernoc94c3df2018-02-09 15:38:54 +01001804 logger_server = logging.getLogger("cherrypy.error")
1805 logger_access = logging.getLogger("cherrypy.access")
1806 logger_cherry = logging.getLogger("cherrypy")
1807 logger_nbi = logging.getLogger("nbi")
1808
tiernof5298be2018-05-16 14:43:57 +02001809 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01001810 file_handler = logging.handlers.RotatingFileHandler(
1811 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
1812 )
tiernoc94c3df2018-02-09 15:38:54 +01001813 file_handler.setFormatter(log_formatter_simple)
1814 logger_cherry.addHandler(file_handler)
1815 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02001816 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01001817 for format_, logger in {
1818 "nbi.server %(filename)s:%(lineno)s": logger_server,
1819 "nbi.access %(filename)s:%(lineno)s": logger_access,
1820 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
1821 }.items():
tiernob24258a2018-10-04 18:39:49 +02001822 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01001823 log_formatter_cherry = logging.Formatter(
1824 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
1825 )
tiernob24258a2018-10-04 18:39:49 +02001826 str_handler = logging.StreamHandler()
1827 str_handler.setFormatter(log_formatter_cherry)
1828 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01001829
tiernof5298be2018-05-16 14:43:57 +02001830 if engine_config["global"].get("log.level"):
1831 logger_cherry.setLevel(engine_config["global"]["log.level"])
1832 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01001833
1834 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01001835 for k1, logname in {
1836 "message": "nbi.msg",
1837 "database": "nbi.db",
1838 "storage": "nbi.fs",
1839 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01001840 engine_config[k1]["logger_name"] = logname
1841 logger_module = logging.getLogger(logname)
1842 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01001843 file_handler = logging.handlers.RotatingFileHandler(
1844 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
1845 )
tiernoc94c3df2018-02-09 15:38:54 +01001846 file_handler.setFormatter(log_formatter_simple)
1847 logger_module.addHandler(file_handler)
1848 if "loglevel" in engine_config[k1]:
1849 logger_module.setLevel(engine_config[k1]["loglevel"])
1850 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01001851 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
1852 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
1853 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
1854 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
1855 target_version=auth_database_version
1856 )
tiernobee508e2019-01-21 11:21:49 +00001857
tierno932499c2019-01-28 17:28:10 +00001858 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01001859 subscription_thread = SubscriptionThread(
1860 config=engine_config, engine=nbi_server.engine
1861 )
tierno932499c2019-01-28 17:28:10 +00001862 subscription_thread.start()
1863 # Do not capture except SubscriptionException
1864
tiernob2e48bd2020-02-04 15:47:18 +00001865 backend = engine_config["authentication"]["backend"]
garciadeblas4568a372021-03-24 09:19:48 +01001866 cherrypy.log.error(
1867 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
1868 nbi_version, nbi_version_date, backend
1869 )
1870 )
tiernoc94c3df2018-02-09 15:38:54 +01001871
1872
1873def _stop_service():
1874 """
1875 Callback function called when cherrypy.engine stops
1876 TODO: Ending database connections.
1877 """
tierno932499c2019-01-28 17:28:10 +00001878 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01001879 if subscription_thread:
1880 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00001881 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01001882 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01001883 cherrypy.log.error("Stopping osm_nbi")
1884
tierno2236d202018-05-16 19:05:16 +02001885
tiernof5298be2018-05-16 14:43:57 +02001886def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00001887 global nbi_server
tiernoc94c3df2018-02-09 15:38:54 +01001888 # conf = {
1889 # '/': {
1890 # #'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
1891 # 'tools.sessions.on': True,
1892 # 'tools.response_headers.on': True,
1893 # # 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
1894 # }
1895 # }
1896 # cherrypy.Server.ssl_module = 'builtin'
1897 # cherrypy.Server.ssl_certificate = "http/cert.pem"
1898 # cherrypy.Server.ssl_private_key = "http/privkey.pem"
1899 # cherrypy.Server.thread_pool = 10
1900 # cherrypy.config.update({'Server.socket_port': config["port"], 'Server.socket_host': config["host"]})
1901
1902 # cherrypy.config.update({'tools.auth_basic.on': True,
1903 # 'tools.auth_basic.realm': 'localhost',
1904 # 'tools.auth_basic.checkpassword': validate_password})
tierno932499c2019-01-28 17:28:10 +00001905 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01001906 cherrypy.engine.subscribe("start", _start_service)
1907 cherrypy.engine.subscribe("stop", _stop_service)
1908 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02001909
1910
1911def usage():
garciadeblas4568a372021-03-24 09:19:48 +01001912 print(
1913 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02001914 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
1915 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01001916 """.format(
1917 sys.argv[0]
1918 )
1919 )
tierno2236d202018-05-16 19:05:16 +02001920 # --log-socket-host HOST: send logs to this host")
1921 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01001922
1923
garciadeblas4568a372021-03-24 09:19:48 +01001924if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02001925 try:
1926 # load parameters and configuration
1927 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
1928 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
1929 config_file = None
1930 for o, a in opts:
1931 if o in ("-h", "--help"):
1932 usage()
1933 sys.exit()
1934 elif o in ("-c", "--config"):
1935 config_file = a
1936 # elif o == "--log-socket-port":
1937 # log_socket_port = a
1938 # elif o == "--log-socket-host":
1939 # log_socket_host = a
1940 # elif o == "--log-file":
1941 # log_file = a
1942 else:
1943 assert False, "Unhandled option"
1944 if config_file:
1945 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01001946 print(
1947 "configuration file '{}' that not exist".format(config_file),
1948 file=sys.stderr,
1949 )
tiernof5298be2018-05-16 14:43:57 +02001950 exit(1)
1951 else:
garciadeblas4568a372021-03-24 09:19:48 +01001952 for config_file in (
1953 __file__[: __file__.rfind(".")] + ".cfg",
1954 "./nbi.cfg",
1955 "/etc/osm/nbi.cfg",
1956 ):
tiernof5298be2018-05-16 14:43:57 +02001957 if path.isfile(config_file):
1958 break
1959 else:
garciadeblas4568a372021-03-24 09:19:48 +01001960 print(
1961 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
1962 file=sys.stderr,
1963 )
tiernof5298be2018-05-16 14:43:57 +02001964 exit(1)
1965 nbi(config_file)
1966 except getopt.GetoptError as e:
1967 print(str(e), file=sys.stderr)
1968 # usage()
1969 exit(1)