blob: d78379f77103b8b70629183333300361acf35944 [file] [log] [blame]
tiernoc94c3df2018-02-09 15:38:54 +01001#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3
tiernod125caf2018-11-22 16:05:54 +00004# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
tiernoc94c3df2018-02-09 15:38:54 +010017import cherrypy
18import time
19import json
20import yaml
tierno9c630112019-08-29 14:21:41 +000021import osm_nbi.html_out as html
tiernoc94c3df2018-02-09 15:38:54 +010022import logging
tiernof5298be2018-05-16 14:43:57 +020023import logging.handlers
24import getopt
25import sys
Eduardo Sousa2f988212018-07-26 01:04:11 +010026
tierno9c630112019-08-29 14:21:41 +000027from osm_nbi.authconn import AuthException, AuthconnException
28from osm_nbi.auth import Authenticator
29from osm_nbi.engine import Engine, EngineException
30from osm_nbi.subscriptions import SubscriptionThread
31from osm_nbi.validation import ValidationError
tiernoa8d63632018-05-10 13:12:32 +020032from osm_common.dbbase import DbException
33from osm_common.fsbase import FsException
34from osm_common.msgbase import MsgException
tiernoc94c3df2018-02-09 15:38:54 +010035from http import HTTPStatus
tiernoc94c3df2018-02-09 15:38:54 +010036from codecs import getreader
tiernof5298be2018-05-16 14:43:57 +020037from os import environ, path
tiernob2e48bd2020-02-04 15:47:18 +000038from osm_nbi import version as nbi_version, version_date as nbi_version_date
tiernoc94c3df2018-02-09 15:38:54 +010039
40__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
tiernodfe09572018-04-24 10:41:10 +020041
garciadeblas4568a372021-03-24 09:19:48 +010042__version__ = "0.1.3" # file version, not NBI version
tierno9c630112019-08-29 14:21:41 +000043version_date = "Aug 2019"
44
garciadeblas4568a372021-03-24 09:19:48 +010045database_version = "1.2"
46auth_database_version = "1.0"
47nbi_server = None # instance of Server class
tierno932499c2019-01-28 17:28:10 +000048subscription_thread = None # instance of SubscriptionThread class
tiernoc94c3df2018-02-09 15:38:54 +010049
50"""
tiernof27c79b2018-03-12 17:08:42 +010051North Bound Interface (O: OSM specific; 5,X: SOL005 not implemented yet; O5: SOL005 implemented)
tiernoc94c3df2018-02-09 15:38:54 +010052URL: /osm GET POST PUT DELETE PATCH
garciadeblas9750c5a2018-10-15 16:20:35 +020053 /nsd/v1
tierno2236d202018-05-16 19:05:16 +020054 /ns_descriptors_content O O
55 /<nsdInfoId> O O O O
tiernoc94c3df2018-02-09 15:38:54 +010056 /ns_descriptors O5 O5
57 /<nsdInfoId> O5 O5 5
58 /nsd_content O5 O5
tiernof27c79b2018-03-12 17:08:42 +010059 /nsd O
60 /artifacts[/<artifactPath>] O
tiernoc94c3df2018-02-09 15:38:54 +010061 /pnf_descriptors 5 5
62 /<pnfdInfoId> 5 5 5
63 /pnfd_content 5 5
tiernof27c79b2018-03-12 17:08:42 +010064 /subscriptions 5 5
65 /<subscriptionId> 5 X
tiernoc94c3df2018-02-09 15:38:54 +010066
67 /vnfpkgm/v1
tierno55945e72018-04-06 16:40:27 +020068 /vnf_packages_content O O
tierno2236d202018-05-16 19:05:16 +020069 /<vnfPkgId> O O
tiernoc94c3df2018-02-09 15:38:54 +010070 /vnf_packages O5 O5
71 /<vnfPkgId> O5 O5 5
tiernoc94c3df2018-02-09 15:38:54 +010072 /package_content O5 O5
73 /upload_from_uri X
tiernof27c79b2018-03-12 17:08:42 +010074 /vnfd O5
75 /artifacts[/<artifactPath>] O5
76 /subscriptions X X
77 /<subscriptionId> X X
tiernoc94c3df2018-02-09 15:38:54 +010078
79 /nslcm/v1
tiernof27c79b2018-03-12 17:08:42 +010080 /ns_instances_content O O
tierno2236d202018-05-16 19:05:16 +020081 /<nsInstanceId> O O
tiernof27c79b2018-03-12 17:08:42 +010082 /ns_instances 5 5
tierno95692442018-05-24 18:05:28 +020083 /<nsInstanceId> O5 O5
tierno65acb4d2018-04-06 16:42:40 +020084 instantiate O5
85 terminate O5
86 action O
87 scale O5
elumalai8e3806c2022-04-28 17:26:24 +053088 migrate O
aticig544a2ae2022-04-05 09:00:17 +030089 update 05
garciadeblas0964edf2022-02-11 00:43:44 +010090 heal O5
tiernoc94c3df2018-02-09 15:38:54 +010091 /ns_lcm_op_occs 5 5
92 /<nsLcmOpOccId> 5 5 5
93 TO BE COMPLETED 5 5
tiernof759d822018-06-11 18:54:54 +020094 /vnf_instances (also vnfrs for compatibility) O
95 /<vnfInstanceId> O
tiernof27c79b2018-03-12 17:08:42 +010096 /subscriptions 5 5
97 /<subscriptionId> 5 X
garciadeblas9750c5a2018-10-15 16:20:35 +020098
tiernocb83c942018-09-24 17:28:13 +020099 /pdu/v1
tierno032916c2019-03-22 13:27:12 +0000100 /pdu_descriptors O O
tiernocb83c942018-09-24 17:28:13 +0200101 /<id> O O O O
garciadeblas9750c5a2018-10-15 16:20:35 +0200102
tiernof27c79b2018-03-12 17:08:42 +0100103 /admin/v1
104 /tokens O O
tierno2236d202018-05-16 19:05:16 +0200105 /<id> O O
tiernof27c79b2018-03-12 17:08:42 +0100106 /users O O
tiernocd54a4a2018-09-12 16:40:35 +0200107 /<id> O O O O
tiernof27c79b2018-03-12 17:08:42 +0100108 /projects O O
tierno2236d202018-05-16 19:05:16 +0200109 /<id> O O
tierno55ba2e62018-12-11 17:22:22 +0000110 /vim_accounts (also vims for compatibility) O O
111 /<id> O O O
112 /wim_accounts O O
tierno2236d202018-05-16 19:05:16 +0200113 /<id> O O O
tierno0f98af52018-03-19 10:28:22 +0100114 /sdns O O
tierno2236d202018-05-16 19:05:16 +0200115 /<id> O O O
delacruzramofe598fe2019-10-23 18:25:11 +0200116 /k8sclusters O O
117 /<id> O O O
118 /k8srepos O O
119 /<id> O O
Felipe Vicensb66b0412020-05-06 10:11:00 +0200120 /osmrepos O O
121 /<id> O O
tiernoc94c3df2018-02-09 15:38:54 +0100122
garciadeblas9750c5a2018-10-15 16:20:35 +0200123 /nst/v1 O O
124 /netslice_templates_content O O
125 /<nstInfoId> O O O O
126 /netslice_templates O O
127 /<nstInfoId> O O O
128 /nst_content O O
129 /nst O
130 /artifacts[/<artifactPath>] O
131 /subscriptions X X
132 /<subscriptionId> X X
133
134 /nsilcm/v1
135 /netslice_instances_content O O
136 /<SliceInstanceId> O O
137 /netslice_instances O O
138 /<SliceInstanceId> O O
139 instantiate O
140 terminate O
141 action O
142 /nsi_lcm_op_occs O O
143 /<nsiLcmOpOccId> O O O
144 /subscriptions X X
145 /<subscriptionId> X X
146
tierno2236d202018-05-16 19:05:16 +0200147query string:
148 Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force.
tierno36ec8602018-11-02 17:27:11 +0100149 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
150 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
151 op := "eq" | "neq" (or "ne") | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
152 attrName := string
tierno2236d202018-05-16 19:05:16 +0200153 For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any
154 item of the array, that is, pass if any item of the array pass the filter.
155 It allows both ne and neq for not equal
156 TODO: 4.3.3 Attribute selectors
157 all_fields, fields=x,y,.., exclude_default, exclude_fields=x,y,...
tiernoc94c3df2018-02-09 15:38:54 +0100158 (none) … same as “exclude_default”
159 all_fields … all attributes.
tierno2236d202018-05-16 19:05:16 +0200160 fields=<list> … all attributes except all complex attributes with minimum cardinality of zero that are not
161 conditionally mandatory, and that are not provided in <list>.
162 exclude_fields=<list> … all attributes except those complex attributes with a minimum cardinality of zero that
163 are not conditionally mandatory, and that are provided in <list>.
164 exclude_default … all attributes except those complex attributes with a minimum cardinality of zero that are not
165 conditionally mandatory, and that are part of the "default exclude set" defined in the present specification for
166 the particular resource
167 exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality
168 of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the
169 present specification for the particular resource, but that are not part of <list>
tierno65ca36d2019-02-12 19:27:52 +0100170 Additionally it admits some administrator values:
171 FORCE: To force operations skipping dependency checkings
172 ADMIN: To act as an administrator or a different project
173 PUBLIC: To get public descriptors or set a descriptor as public
174 SET_PROJECT: To make a descriptor available for other project
beierlmbc5a5242022-05-17 21:25:29 -0400175
tiernoc94c3df2018-02-09 15:38:54 +0100176Header field name Reference Example Descriptions
177 Accept IETF RFC 7231 [19] application/json Content-Types that are acceptable for the response.
178 This header field shall be present if the response is expected to have a non-empty message body.
179 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the request.
180 This header field shall be present if the request has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200181 Authorization IETF RFC 7235 [22] Bearer mF_9.B5f-4.1JqM The authorization token for the request.
182 Details are specified in clause 4.5.3.
tiernoc94c3df2018-02-09 15:38:54 +0100183 Range IETF RFC 7233 [21] 1000-2000 Requested range of bytes from a file
184Header field name Reference Example Descriptions
185 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the response.
186 This header field shall be present if the response has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200187 Location IETF RFC 7231 [19] http://www.example.com/vnflcm/v1/vnf_instances/123 Used in redirection, or when a
188 new resource has been created.
tiernoc94c3df2018-02-09 15:38:54 +0100189 This header field shall be present if the response status code is 201 or 3xx.
tierno2236d202018-05-16 19:05:16 +0200190 In the present document this header field is also used if the response status code is 202 and a new resource was
191 created.
192 WWW-Authenticate IETF RFC 7235 [22] Bearer realm="example" Challenge if the corresponding HTTP request has not
193 provided authorization, or error details if the corresponding HTTP request has provided an invalid authorization
194 token.
195 Accept-Ranges IETF RFC 7233 [21] bytes Used by the Server to signal whether or not it supports ranges for
196 certain resources.
197 Content-Range IETF RFC 7233 [21] bytes 21010-47021/ 47022 Signals the byte range that is contained in the
198 response, and the total length of the file.
tiernoc94c3df2018-02-09 15:38:54 +0100199 Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT
tiernoc94c3df2018-02-09 15:38:54 +0100200"""
201
tierno701018c2019-06-25 11:13:14 +0000202valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC")
203# ^ Contains possible administrative query string words:
204# ADMIN=True(by default)|Project|Project-list: See all elements, or elements of a project
205# (not owned by my session project).
206# PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public
207# FORCE=True(by default)|False: Force edition/deletion operations
208# SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio
209
210valid_url_methods = {
211 # contains allowed URL and methods, and the role_permission name
212 "admin": {
213 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100214 "tokens": {
215 "METHODS": ("GET", "POST", "DELETE"),
216 "ROLE_PERMISSION": "tokens:",
217 "<ID>": {"METHODS": ("GET", "DELETE"), "ROLE_PERMISSION": "tokens:id:"},
218 },
219 "users": {
220 "METHODS": ("GET", "POST"),
221 "ROLE_PERMISSION": "users:",
222 "<ID>": {
223 "METHODS": ("GET", "DELETE", "PATCH"),
224 "ROLE_PERMISSION": "users:id:",
225 },
226 },
227 "projects": {
228 "METHODS": ("GET", "POST"),
229 "ROLE_PERMISSION": "projects:",
230 "<ID>": {
231 "METHODS": ("GET", "DELETE", "PATCH"),
232 "ROLE_PERMISSION": "projects:id:",
233 },
234 },
235 "roles": {
236 "METHODS": ("GET", "POST"),
237 "ROLE_PERMISSION": "roles:",
238 "<ID>": {
239 "METHODS": ("GET", "DELETE", "PATCH"),
240 "ROLE_PERMISSION": "roles:id:",
241 },
242 },
243 "vims": {
244 "METHODS": ("GET", "POST"),
245 "ROLE_PERMISSION": "vims:",
246 "<ID>": {
247 "METHODS": ("GET", "DELETE", "PATCH"),
248 "ROLE_PERMISSION": "vims:id:",
249 },
250 },
251 "vim_accounts": {
252 "METHODS": ("GET", "POST"),
253 "ROLE_PERMISSION": "vim_accounts:",
254 "<ID>": {
255 "METHODS": ("GET", "DELETE", "PATCH"),
256 "ROLE_PERMISSION": "vim_accounts:id:",
257 },
258 },
259 "wim_accounts": {
260 "METHODS": ("GET", "POST"),
261 "ROLE_PERMISSION": "wim_accounts:",
262 "<ID>": {
263 "METHODS": ("GET", "DELETE", "PATCH"),
264 "ROLE_PERMISSION": "wim_accounts:id:",
265 },
266 },
267 "sdns": {
268 "METHODS": ("GET", "POST"),
269 "ROLE_PERMISSION": "sdn_controllers:",
270 "<ID>": {
271 "METHODS": ("GET", "DELETE", "PATCH"),
272 "ROLE_PERMISSION": "sdn_controllers:id:",
273 },
274 },
275 "k8sclusters": {
276 "METHODS": ("GET", "POST"),
277 "ROLE_PERMISSION": "k8sclusters:",
278 "<ID>": {
279 "METHODS": ("GET", "DELETE", "PATCH"),
280 "ROLE_PERMISSION": "k8sclusters:id:",
281 },
282 },
283 "vca": {
284 "METHODS": ("GET", "POST"),
285 "ROLE_PERMISSION": "vca:",
286 "<ID>": {
287 "METHODS": ("GET", "DELETE", "PATCH"),
288 "ROLE_PERMISSION": "vca:id:",
289 },
290 },
291 "k8srepos": {
292 "METHODS": ("GET", "POST"),
293 "ROLE_PERMISSION": "k8srepos:",
294 "<ID>": {
295 "METHODS": ("GET", "DELETE"),
296 "ROLE_PERMISSION": "k8srepos:id:",
297 },
298 },
299 "osmrepos": {
300 "METHODS": ("GET", "POST"),
301 "ROLE_PERMISSION": "osmrepos:",
302 "<ID>": {
303 "METHODS": ("GET", "DELETE", "PATCH"),
304 "ROLE_PERMISSION": "osmrepos:id:",
305 },
306 },
307 "domains": {
308 "METHODS": ("GET",),
309 "ROLE_PERMISSION": "domains:",
310 },
tierno701018c2019-06-25 11:13:14 +0000311 }
312 },
313 "pdu": {
314 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100315 "pdu_descriptors": {
316 "METHODS": ("GET", "POST"),
317 "ROLE_PERMISSION": "pduds:",
318 "<ID>": {
319 "METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"),
320 "ROLE_PERMISSION": "pduds:id:",
321 },
322 },
tierno701018c2019-06-25 11:13:14 +0000323 }
324 },
325 "nsd": {
326 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100327 "ns_descriptors_content": {
328 "METHODS": ("GET", "POST"),
329 "ROLE_PERMISSION": "nsds:",
330 "<ID>": {
331 "METHODS": ("GET", "PUT", "DELETE"),
332 "ROLE_PERMISSION": "nsds:id:",
333 },
334 },
335 "ns_descriptors": {
336 "METHODS": ("GET", "POST"),
337 "ROLE_PERMISSION": "nsds:",
338 "<ID>": {
339 "METHODS": ("GET", "DELETE", "PATCH"),
340 "ROLE_PERMISSION": "nsds:id:",
341 "nsd_content": {
342 "METHODS": ("GET", "PUT"),
343 "ROLE_PERMISSION": "nsds:id:content:",
344 },
345 "nsd": {
346 "METHODS": ("GET",), # descriptor inside package
347 "ROLE_PERMISSION": "nsds:id:content:",
348 },
349 "artifacts": {
350 "METHODS": ("GET",),
351 "ROLE_PERMISSION": "nsds:id:nsd_artifact:",
352 "*": None,
353 },
354 },
355 },
356 "pnf_descriptors": {
357 "TODO": ("GET", "POST"),
358 "<ID>": {
359 "TODO": ("GET", "DELETE", "PATCH"),
360 "pnfd_content": {"TODO": ("GET", "PUT")},
361 },
362 },
363 "subscriptions": {
364 "TODO": ("GET", "POST"),
365 "<ID>": {"TODO": ("GET", "DELETE")},
366 },
tierno701018c2019-06-25 11:13:14 +0000367 }
368 },
369 "vnfpkgm": {
370 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100371 "vnf_packages_content": {
372 "METHODS": ("GET", "POST"),
373 "ROLE_PERMISSION": "vnfds:",
374 "<ID>": {
375 "METHODS": ("GET", "PUT", "DELETE"),
376 "ROLE_PERMISSION": "vnfds:id:",
377 },
378 },
379 "vnf_packages": {
380 "METHODS": ("GET", "POST"),
381 "ROLE_PERMISSION": "vnfds:",
382 "<ID>": {
383 "METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo
384 "ROLE_PERMISSION": "vnfds:id:",
385 "package_content": {
386 "METHODS": ("GET", "PUT"), # package
387 "ROLE_PERMISSION": "vnfds:id:",
388 "upload_from_uri": {
389 "METHODS": (),
390 "TODO": ("POST",),
391 "ROLE_PERMISSION": "vnfds:id:upload:",
392 },
393 },
394 "vnfd": {
395 "METHODS": ("GET",), # descriptor inside package
396 "ROLE_PERMISSION": "vnfds:id:content:",
397 },
398 "artifacts": {
399 "METHODS": ("GET",),
400 "ROLE_PERMISSION": "vnfds:id:vnfd_artifact:",
401 "*": None,
402 },
403 "action": {
404 "METHODS": ("POST",),
405 "ROLE_PERMISSION": "vnfds:id:action:",
406 },
407 },
408 },
409 "subscriptions": {
410 "TODO": ("GET", "POST"),
411 "<ID>": {"TODO": ("GET", "DELETE")},
412 },
413 "vnfpkg_op_occs": {
414 "METHODS": ("GET",),
415 "ROLE_PERMISSION": "vnfds:vnfpkgops:",
416 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnfds:vnfpkgops:id:"},
417 },
tierno701018c2019-06-25 11:13:14 +0000418 }
419 },
420 "nslcm": {
421 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100422 "ns_instances_content": {
423 "METHODS": ("GET", "POST"),
424 "ROLE_PERMISSION": "ns_instances:",
425 "<ID>": {
426 "METHODS": ("GET", "DELETE"),
427 "ROLE_PERMISSION": "ns_instances:id:",
428 },
429 },
430 "ns_instances": {
431 "METHODS": ("GET", "POST"),
432 "ROLE_PERMISSION": "ns_instances:",
433 "<ID>": {
434 "METHODS": ("GET", "DELETE"),
435 "ROLE_PERMISSION": "ns_instances:id:",
garciadeblas0964edf2022-02-11 00:43:44 +0100436 "heal": {
437 "METHODS": ("POST",),
438 "ROLE_PERMISSION": "ns_instances:id:heal:",
439 },
garciadeblas4568a372021-03-24 09:19:48 +0100440 "scale": {
441 "METHODS": ("POST",),
442 "ROLE_PERMISSION": "ns_instances:id:scale:",
443 },
444 "terminate": {
445 "METHODS": ("POST",),
446 "ROLE_PERMISSION": "ns_instances:id:terminate:",
447 },
448 "instantiate": {
449 "METHODS": ("POST",),
450 "ROLE_PERMISSION": "ns_instances:id:instantiate:",
451 },
elumalai8e3806c2022-04-28 17:26:24 +0530452 "migrate": {
453 "METHODS": ("POST",),
454 "ROLE_PERMISSION": "ns_instances:id:migrate:",
455 },
garciadeblas4568a372021-03-24 09:19:48 +0100456 "action": {
457 "METHODS": ("POST",),
458 "ROLE_PERMISSION": "ns_instances:id:action:",
459 },
aticig544a2ae2022-04-05 09:00:17 +0300460 "update": {
461 "METHODS": ("POST",),
462 "ROLE_PERMISSION": "ns_instances:id:update:",
463 },
govindarajul519da482022-04-29 19:05:22 +0530464 "verticalscale": {
465 "METHODS": ("POST",),
466 "ROLE_PERMISSION": "ns_instances:id:verticalscale:"
467 },
garciadeblas4568a372021-03-24 09:19:48 +0100468 },
469 },
470 "ns_lcm_op_occs": {
471 "METHODS": ("GET",),
472 "ROLE_PERMISSION": "ns_instances:opps:",
473 "<ID>": {
474 "METHODS": ("GET",),
475 "ROLE_PERMISSION": "ns_instances:opps:id:",
476 },
477 },
478 "vnfrs": {
479 "METHODS": ("GET",),
480 "ROLE_PERMISSION": "vnf_instances:",
481 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
482 },
483 "vnf_instances": {
484 "METHODS": ("GET",),
485 "ROLE_PERMISSION": "vnf_instances:",
486 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
487 },
488 "subscriptions": {
489 "METHODS": ("GET", "POST"),
490 "ROLE_PERMISSION": "ns_subscriptions:",
491 "<ID>": {
492 "METHODS": ("GET", "DELETE"),
493 "ROLE_PERMISSION": "ns_subscriptions:id:",
494 },
495 },
tierno701018c2019-06-25 11:13:14 +0000496 }
497 },
almagiae47b9132022-05-17 14:12:22 +0200498 "vnflcm": {
499 "v1": {
500 "vnf_instances": {"METHODS": ("GET", "POST"),
501 "ROLE_PERMISSION": "vnflcm_instances:",
502 "<ID>": {"METHODS": ("GET", "DELETE"),
503 "ROLE_PERMISSION": "vnflcm_instances:id:",
504 "scale": {"METHODS": ("POST",),
505 "ROLE_PERMISSION": "vnflcm_instances:id:scale:"
506 },
507 "terminate": {"METHODS": ("POST",),
508 "ROLE_PERMISSION": "vnflcm_instances:id:terminate:"
509 },
510 "instantiate": {"METHODS": ("POST",),
511 "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:"
512 },
513 }
514 },
515 "vnf_lcm_op_occs": {"METHODS": ("GET",),
516 "ROLE_PERMISSION": "vnf_instances:opps:",
517 "<ID>": {"METHODS": ("GET",),
518 "ROLE_PERMISSION": "vnf_instances:opps:id:"
519 },
520 },
selvi.jf1004592022-04-29 05:42:35 +0000521 "subscriptions": {"METHODS": ("GET", "POST"),
522 "ROLE_PERMISSION": "vnflcm_subscriptions:",
523 "<ID>": {"METHODS": ("GET", "DELETE"),
524 "ROLE_PERMISSION": "vnflcm_subscriptions:id:"
525 }
526 },
almagiae47b9132022-05-17 14:12:22 +0200527 }
528 },
tierno701018c2019-06-25 11:13:14 +0000529 "nst": {
530 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100531 "netslice_templates_content": {
532 "METHODS": ("GET", "POST"),
533 "ROLE_PERMISSION": "slice_templates:",
534 "<ID>": {
535 "METHODS": ("GET", "PUT", "DELETE"),
536 "ROLE_PERMISSION": "slice_templates:id:",
537 },
538 },
539 "netslice_templates": {
540 "METHODS": ("GET", "POST"),
541 "ROLE_PERMISSION": "slice_templates:",
542 "<ID>": {
543 "METHODS": ("GET", "DELETE"),
544 "TODO": ("PATCH",),
545 "ROLE_PERMISSION": "slice_templates:id:",
546 "nst_content": {
547 "METHODS": ("GET", "PUT"),
548 "ROLE_PERMISSION": "slice_templates:id:content:",
549 },
550 "nst": {
551 "METHODS": ("GET",), # descriptor inside package
552 "ROLE_PERMISSION": "slice_templates:id:content:",
553 },
554 "artifacts": {
555 "METHODS": ("GET",),
556 "ROLE_PERMISSION": "slice_templates:id:content:",
557 "*": None,
558 },
559 },
560 },
561 "subscriptions": {
562 "TODO": ("GET", "POST"),
563 "<ID>": {"TODO": ("GET", "DELETE")},
564 },
tierno701018c2019-06-25 11:13:14 +0000565 }
566 },
567 "nsilcm": {
568 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100569 "netslice_instances_content": {
570 "METHODS": ("GET", "POST"),
571 "ROLE_PERMISSION": "slice_instances:",
572 "<ID>": {
573 "METHODS": ("GET", "DELETE"),
574 "ROLE_PERMISSION": "slice_instances:id:",
575 },
576 },
577 "netslice_instances": {
578 "METHODS": ("GET", "POST"),
579 "ROLE_PERMISSION": "slice_instances:",
580 "<ID>": {
581 "METHODS": ("GET", "DELETE"),
582 "ROLE_PERMISSION": "slice_instances:id:",
583 "terminate": {
584 "METHODS": ("POST",),
585 "ROLE_PERMISSION": "slice_instances:id:terminate:",
586 },
587 "instantiate": {
588 "METHODS": ("POST",),
589 "ROLE_PERMISSION": "slice_instances:id:instantiate:",
590 },
591 "action": {
592 "METHODS": ("POST",),
593 "ROLE_PERMISSION": "slice_instances:id:action:",
594 },
595 },
596 },
597 "nsi_lcm_op_occs": {
598 "METHODS": ("GET",),
599 "ROLE_PERMISSION": "slice_instances:opps:",
600 "<ID>": {
601 "METHODS": ("GET",),
602 "ROLE_PERMISSION": "slice_instances:opps:id:",
603 },
604 },
tierno701018c2019-06-25 11:13:14 +0000605 }
606 },
607 "nspm": {
608 "v1": {
609 "pm_jobs": {
610 "<ID>": {
611 "reports": {
garciadeblas4568a372021-03-24 09:19:48 +0100612 "<ID>": {
613 "METHODS": ("GET",),
614 "ROLE_PERMISSION": "reports:id:",
615 }
tierno701018c2019-06-25 11:13:14 +0000616 }
617 },
618 },
619 },
620 },
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000621 "nsfm": {
622 "v1": {
623 "alarms": {"METHODS": ("GET", "PATCH"),
624 "ROLE_PERMISSION": "alarms:",
625 "<ID>": {"METHODS": ("GET", "PATCH"),
626 "ROLE_PERMISSION": "alarms:id:",
627 },
628 }
629 },
630 },
tierno701018c2019-06-25 11:13:14 +0000631}
632
tiernoc94c3df2018-02-09 15:38:54 +0100633
634class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100635 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
636 Exception.__init__(self, message)
637 self.http_code = http_code
638
639
640class Server(object):
641 instance = 0
642 # to decode bytes to str
643 reader = getreader("utf-8")
644
645 def __init__(self):
646 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000647 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100648 self.engine = Engine(self.authenticator)
tiernoc94c3df2018-02-09 15:38:54 +0100649
tiernoc94c3df2018-02-09 15:38:54 +0100650 def _format_in(self, kwargs):
651 try:
652 indata = None
653 if cherrypy.request.body.length:
654 error_text = "Invalid input format "
655
656 if "Content-Type" in cherrypy.request.headers:
657 if "application/json" in cherrypy.request.headers["Content-Type"]:
658 error_text = "Invalid json format "
659 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100660 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100661 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
662 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100663 indata = yaml.load(
664 cherrypy.request.body, Loader=yaml.SafeLoader
665 )
gcalvinode4adfe2018-10-30 11:46:09 +0100666 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100667 elif (
668 "application/binary" in cherrypy.request.headers["Content-Type"]
669 or "application/gzip"
670 in cherrypy.request.headers["Content-Type"]
671 or "application/zip" in cherrypy.request.headers["Content-Type"]
672 or "text/plain" in cherrypy.request.headers["Content-Type"]
673 ):
tiernof27c79b2018-03-12 17:08:42 +0100674 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100675 elif (
676 "multipart/form-data"
677 in cherrypy.request.headers["Content-Type"]
678 ):
tiernoc94c3df2018-02-09 15:38:54 +0100679 if "descriptor_file" in kwargs:
680 filecontent = kwargs.pop("descriptor_file")
681 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100682 raise NbiException(
683 "empty file or content", HTTPStatus.BAD_REQUEST
684 )
tiernof27c79b2018-03-12 17:08:42 +0100685 indata = filecontent.file # .read()
tiernoc94c3df2018-02-09 15:38:54 +0100686 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100687 cherrypy.request.headers[
688 "Content-Type"
689 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100690 else:
691 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
692 # "Only 'Content-Type' of type 'application/json' or
693 # 'application/yaml' for input format are available")
694 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100695 indata = yaml.load(
696 cherrypy.request.body, Loader=yaml.SafeLoader
697 )
gcalvinode4adfe2018-10-30 11:46:09 +0100698 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100699 else:
700 error_text = "Invalid yaml format "
delacruzramob19cadc2019-10-08 10:18:02 +0200701 indata = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
gcalvinode4adfe2018-10-30 11:46:09 +0100702 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100703 if not indata:
704 indata = {}
705
tiernoc94c3df2018-02-09 15:38:54 +0100706 format_yaml = False
707 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
708 format_yaml = True
709
710 for k, v in kwargs.items():
711 if isinstance(v, str):
712 if v == "":
713 kwargs[k] = None
714 elif format_yaml:
715 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200716 kwargs[k] = yaml.load(v, Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200717 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100718 pass
garciadeblas4568a372021-03-24 09:19:48 +0100719 elif (
720 k.endswith(".gt")
721 or k.endswith(".lt")
722 or k.endswith(".gte")
723 or k.endswith(".lte")
724 ):
tiernoc94c3df2018-02-09 15:38:54 +0100725 try:
726 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200727 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100728 try:
729 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200730 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100731 pass
732 elif v.find(",") > 0:
733 kwargs[k] = v.split(",")
734 elif isinstance(v, (list, tuple)):
735 for index in range(0, len(v)):
736 if v[index] == "":
737 v[index] = None
738 elif format_yaml:
739 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200740 v[index] = yaml.load(v[index], Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200741 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100742 pass
743
tiernof27c79b2018-03-12 17:08:42 +0100744 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100745 except (ValueError, yaml.YAMLError) as exc:
746 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
747 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100748 raise NbiException(
749 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
750 )
tiernob92094f2018-05-11 13:44:22 +0200751 except Exception as exc:
752 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100753
754 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000755 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100756 """
757 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100758 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000759 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000760 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100761 :return: None
762 """
tierno0f98af52018-03-19 10:28:22 +0100763 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100764 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100765 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100766 return html.format(
767 data, cherrypy.request, cherrypy.response, token_info
768 )
tierno09c073e2018-04-26 13:36:48 +0200769 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100770 return
771 elif hasattr(data, "read"): # file object
772 if _format:
773 cherrypy.response.headers["Content-Type"] = _format
774 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100775 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100776 else:
garciadeblas4568a372021-03-24 09:19:48 +0100777 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100778 # TODO check that cherrypy close file. If not implement pending things to close per thread next
779 return data
tierno0f98af52018-03-19 10:28:22 +0100780 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000781 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100782 return html.format(
783 data, cherrypy.request, cherrypy.response, token_info
784 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000785 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100786 pass
garciadeblas4568a372021-03-24 09:19:48 +0100787 elif "application/json" in accept or (
788 cherrypy.response.status and cherrypy.response.status >= 300
789 ):
790 cherrypy.response.headers[
791 "Content-Type"
792 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000793 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100794 return a.encode("utf8")
795 cherrypy.response.headers["Content-Type"] = "application/yaml"
796 return yaml.safe_dump(
797 data,
798 explicit_start=True,
799 indent=4,
800 default_flow_style=False,
801 tags=False,
802 encoding="utf-8",
803 allow_unicode=True,
804 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +0100805
806 @cherrypy.expose
807 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +0000808 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100809 try:
810 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +0000811 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +0100812 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +0100813 else:
garciadeblas4568a372021-03-24 09:19:48 +0100814 raise cherrypy.HTTPError(
815 HTTPStatus.METHOD_NOT_ALLOWED.value,
816 "Method {} not allowed for tokens".format(cherrypy.request.method),
817 )
tiernoc94c3df2018-02-09 15:38:54 +0100818
tierno701018c2019-06-25 11:13:14 +0000819 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100820
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100821 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +0000822 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +0100823 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +0000824 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100825
826 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +0200827 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +0200828 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +0200829 try:
830 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +0100831 raise NbiException(
832 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
833 )
tierno55945e72018-04-06 16:40:27 +0200834 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +0100835 raise NbiException(
836 "Invalid URL or query string for version",
837 HTTPStatus.METHOD_NOT_ALLOWED,
838 )
tierno9c630112019-08-29 14:21:41 +0000839 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +0000840 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
841 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +0200842 except NbiException as e:
843 cherrypy.response.status = e.http_code.value
844 problem_details = {
845 "code": e.http_code.name,
846 "status": e.http_code.value,
847 "detail": str(e),
848 }
849 return self._format_out(problem_details, None)
850
tierno12eac3c2020-03-19 23:22:08 +0000851 def domain(self):
852 try:
853 domains = {
garciadeblas4568a372021-03-24 09:19:48 +0100854 "user_domain_name": cherrypy.tree.apps["/osm"]
855 .config["authentication"]
856 .get("user_domain_name"),
857 "project_domain_name": cherrypy.tree.apps["/osm"]
858 .config["authentication"]
859 .get("project_domain_name"),
860 }
tierno12eac3c2020-03-19 23:22:08 +0000861 return self._format_out(domains)
862 except NbiException as e:
863 cherrypy.response.status = e.http_code.value
864 problem_details = {
865 "code": e.http_code.name,
866 "status": e.http_code.value,
867 "detail": str(e),
868 }
869 return self._format_out(problem_details, None)
870
tiernoa5035702019-07-29 08:54:42 +0000871 @staticmethod
872 def _format_login(token_info):
873 """
874 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
875 log this information
876 :param token_info: Dictionary with token content
877 :return: None
878 """
879 cherrypy.request.login = token_info.get("username", "-")
880 if token_info.get("project_name"):
881 cherrypy.request.login += "/" + token_info["project_name"]
882 if token_info.get("id"):
883 cherrypy.request.login += ";session=" + token_info["id"][0:12]
884
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000885 # NS Fault Management
886 @cherrypy.expose
887 def nsfm(self, version=None, topic=None, uuid=None, project_name=None, ns_id=None, *args, **kwargs):
888 if topic == 'alarms':
889 try:
890 method = cherrypy.request.method
891 role_permission = self._check_valid_url_method(method, "nsfm", version, topic, None, None, *args)
892 query_string_operations = self._extract_query_string_operations(kwargs, method)
893
894 self.authenticator.authorize(role_permission, query_string_operations, None)
895
896 # to handle get request
897 if cherrypy.request.method == 'GET':
898 # if request is on basis of uuid
899 if uuid and uuid != 'None':
900 try:
901 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
902 alarm_action = self.engine.db.get_one("alarms_action", {"uuid": uuid})
903 alarm.update(alarm_action)
904 vnf = self.engine.db.get_one("vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]})
905 alarm["vnf-id"] = vnf["_id"]
906 return self._format_out(str(alarm))
907 except Exception:
908 return self._format_out("Please provide valid alarm uuid")
909 elif ns_id and ns_id != 'None':
910 # if request is on basis of ns_id
911 try:
912 alarms = self.engine.db.get_list("alarms", {"tags.ns_id": ns_id})
913 for alarm in alarms:
914 alarm_action = self.engine.db.get_one("alarms_action", {"uuid": alarm['uuid']})
915 alarm.update(alarm_action)
916 return self._format_out(str(alarms))
917 except Exception:
918 return self._format_out("Please provide valid ns id")
919 else:
920 # to return only alarm which are related to given project
921 project = self.engine.db.get_one("projects", {"name": project_name})
922 project_id = project.get('_id')
923 ns_list = self.engine.db.get_list("nsrs", {"_admin.projects_read": project_id})
924 ns_ids = []
925 for ns in ns_list:
926 ns_ids.append(ns.get("_id"))
927 alarms = self.engine.db.get_list("alarms")
928 alarm_list = [alarm for alarm in alarms if alarm["tags"]["ns_id"] in ns_ids]
929 for alrm in alarm_list:
930 action = self.engine.db.get_one("alarms_action", {"uuid": alrm.get("uuid")})
931 alrm.update(action)
932 return self._format_out(str(alarm_list))
933 # to handle patch request for alarm update
934 elif cherrypy.request.method == 'PATCH':
935 data = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
936 try:
937 # check if uuid is valid
938 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
939 except Exception:
940 return self._format_out("Please provide valid alarm uuid.")
941 if data.get("is_enable") is not None:
942 if data.get("is_enable"):
943 alarm_status = 'ok'
944 else:
945 alarm_status = 'disabled'
946 self.engine.db.set_one("alarms", {"uuid": data.get("uuid")},
947 {"alarm_status": alarm_status})
948 else:
949 self.engine.db.set_one("alarms", {"uuid": data.get("uuid")},
950 {"threshold": data.get("threshold")})
951 return self._format_out("Alarm updated")
952 except Exception as e:
953 cherrypy.response.status = e.http_code.value
954 if isinstance(e, (NbiException, EngineException, DbException, FsException, MsgException, AuthException,
955 ValidationError, AuthconnException)):
956 http_code_value = cherrypy.response.status = e.http_code.value
957 http_code_name = e.http_code.name
958 cherrypy.log("Exception {}".format(e))
959 else:
960 http_code_value = cherrypy.response.status = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
961 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
962 http_code_name = HTTPStatus.BAD_REQUEST.name
963 problem_details = {
964 "code": http_code_name,
965 "status": http_code_value,
966 "detail": str(e),
967 }
968 return self._format_out(problem_details)
969
tierno55945e72018-04-06 16:40:27 +0200970 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +0100971 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +0000972 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100973 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +0100974 indata = self._format_in(kwargs)
975 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +0100976 raise NbiException(
977 "Expected application/yaml or application/json Content-Type",
978 HTTPStatus.BAD_REQUEST,
979 )
tiernoa5035702019-07-29 08:54:42 +0000980
981 if method == "GET":
982 token_info = self.authenticator.authorize()
983 # for logging
984 self._format_login(token_info)
985 if token_id:
986 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +0100987 else:
tiernoa5035702019-07-29 08:54:42 +0000988 outdata = self.authenticator.get_token_list(token_info)
989 elif method == "POST":
990 try:
991 token_info = self.authenticator.authorize()
992 except Exception:
993 token_info = None
994 if kwargs:
995 indata.update(kwargs)
996 # This is needed to log the user when authentication fails
997 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +0100998 outdata = token_info = self.authenticator.new_token(
999 token_info, indata, cherrypy.request.remote
1000 )
1001 cherrypy.session["Authorization"] = outdata["_id"]
tiernoa5035702019-07-29 08:54:42 +00001002 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
1003 # for logging
1004 self._format_login(token_info)
selvi.ja9a1fc82022-04-04 06:54:30 +00001005 # password expiry check
1006 if self.authenticator.check_password_expiry(outdata):
1007 outdata = {"id": outdata["id"],
1008 "message": "change_password",
1009 "user_id": outdata["user_id"]
1010 }
tiernoa5035702019-07-29 08:54:42 +00001011 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1012 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
1013 elif method == "DELETE":
1014 if not token_id and "id" in kwargs:
1015 token_id = kwargs["id"]
1016 elif not token_id:
1017 token_info = self.authenticator.authorize()
1018 # for logging
1019 self._format_login(token_info)
1020 token_id = token_info["_id"]
1021 outdata = self.authenticator.del_token(token_id)
1022 token_info = None
garciadeblas4568a372021-03-24 09:19:48 +01001023 cherrypy.session["Authorization"] = "logout"
tiernoa5035702019-07-29 08:54:42 +00001024 # cherrypy.response.cookie["Authorization"] = token_id
1025 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1026 else:
garciadeblas4568a372021-03-24 09:19:48 +01001027 raise NbiException(
1028 "Method {} not allowed for token".format(method),
1029 HTTPStatus.METHOD_NOT_ALLOWED,
1030 )
tiernoa5035702019-07-29 08:54:42 +00001031 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001032
1033 @cherrypy.expose
1034 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001035 if not cherrypy.config.get("server.enable_test") or (
1036 isinstance(cherrypy.config["server.enable_test"], str)
1037 and cherrypy.config["server.enable_test"].lower() == "false"
1038 ):
tierno4836bac2020-01-15 14:41:48 +00001039 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1040 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001041 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001042 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001043 return (
1044 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1045 "sleep/<time>\nmessage/topic\n</pre></html>"
1046 )
tiernof27c79b2018-03-12 17:08:42 +01001047
1048 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001049 try:
1050 # self.engine.load_dbase(cherrypy.request.app.config)
1051 self.engine.create_admin()
1052 return "Done. User 'admin', password 'admin' created"
1053 except Exception:
1054 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1055 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001056 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001057 return cherrypy.lib.static.serve_file(
1058 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1059 "text/plain",
1060 "attachment",
1061 )
tiernof27c79b2018-03-12 17:08:42 +01001062 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001063 f_path = (
1064 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1065 )
tiernof27c79b2018-03-12 17:08:42 +01001066 f = open(f_path, "r")
1067 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001068 return f
tierno55945e72018-04-06 16:40:27 +02001069
tiernof27c79b2018-03-12 17:08:42 +01001070 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001071 deleted_info = self.engine.db.del_list(args[1], kwargs)
1072 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1073 elif len(args) and args[0] == "fs-clear":
1074 if len(args) >= 2:
1075 folders = (args[1],)
1076 else:
1077 folders = self.engine.fs.dir_ls(".")
1078 for folder in folders:
1079 self.engine.fs.file_delete(folder)
1080 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001081 elif args and args[0] == "login":
1082 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001083 cherrypy.response.headers[
1084 "WWW-Authenticate"
1085 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001086 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1087 elif args and args[0] == "login2":
1088 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001089 cherrypy.response.headers[
1090 "WWW-Authenticate"
1091 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001092 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1093 elif args and args[0] == "sleep":
1094 sleep_time = 5
1095 try:
1096 sleep_time = int(args[1])
1097 except Exception:
1098 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1099 return self._format_out("Database already initialized")
1100 thread_info = cherrypy.thread_data
1101 print(thread_info)
1102 time.sleep(sleep_time)
1103 # thread_info
1104 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001105 main_topic = args[1]
1106 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001107 try:
garciadeblas4568a372021-03-24 09:19:48 +01001108 if cherrypy.request.method == "POST":
delacruzramob19cadc2019-10-08 10:18:02 +02001109 to_send = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
tierno55945e72018-04-06 16:40:27 +02001110 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001111 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001112 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001113 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001114 for k, v in kwargs.items():
tiernof1509b22020-05-12 14:32:37 +00001115 v_dict = yaml.load(v, Loader=yaml.SafeLoader)
1116 self.engine.msg.write(main_topic, k, v_dict)
1117 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001118 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001119 return_text += "Error: " + str(e)
1120 return_text += "</pre></html>\n"
1121 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001122
1123 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001124 "<html><pre>\nheaders:\n args: {}\n".format(args)
1125 + " kwargs: {}\n".format(kwargs)
1126 + " headers: {}\n".format(cherrypy.request.headers)
1127 + " path_info: {}\n".format(cherrypy.request.path_info)
1128 + " query_string: {}\n".format(cherrypy.request.query_string)
1129 + " session: {}\n".format(cherrypy.session)
1130 + " cookie: {}\n".format(cherrypy.request.cookie)
1131 + " method: {}\n".format(cherrypy.request.method)
1132 + " session: {}\n".format(cherrypy.session.get("fieldname"))
1133 + " body:\n"
1134 )
tiernoc94c3df2018-02-09 15:38:54 +01001135 return_text += " length: {}\n".format(cherrypy.request.body.length)
1136 if cherrypy.request.body.length:
1137 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001138 str(
1139 cherrypy.request.body.read(
1140 int(cherrypy.request.headers.get("Content-Length", 0))
1141 )
1142 )
1143 )
tiernoc94c3df2018-02-09 15:38:54 +01001144 if thread_info:
1145 return_text += "thread: {}\n".format(thread_info)
1146 return_text += "</pre></html>"
1147 return return_text
1148
tierno701018c2019-06-25 11:13:14 +00001149 @staticmethod
1150 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001151 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001152 raise NbiException(
1153 "URL must contain at least 'main_topic/version/topic'",
1154 HTTPStatus.METHOD_NOT_ALLOWED,
1155 )
tiernof27c79b2018-03-12 17:08:42 +01001156
tierno701018c2019-06-25 11:13:14 +00001157 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001158 for arg in args:
1159 if arg is None:
1160 break
1161 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001162 raise NbiException(
1163 "URL contains unexpected extra items '{}'".format(arg),
1164 HTTPStatus.METHOD_NOT_ALLOWED,
1165 )
tiernof27c79b2018-03-12 17:08:42 +01001166
1167 if arg in reference:
1168 reference = reference[arg]
1169 elif "<ID>" in reference:
1170 reference = reference["<ID>"]
1171 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001172 # if there is content
1173 if reference["*"]:
1174 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001175 break
1176 else:
garciadeblas4568a372021-03-24 09:19:48 +01001177 raise NbiException(
1178 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1179 )
tiernof27c79b2018-03-12 17:08:42 +01001180 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001181 raise NbiException(
1182 "Method {} not supported yet for this URL".format(method),
1183 HTTPStatus.NOT_IMPLEMENTED,
1184 )
tierno2236d202018-05-16 19:05:16 +02001185 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001186 raise NbiException(
1187 "Method {} not supported for this URL".format(method),
1188 HTTPStatus.METHOD_NOT_ALLOWED,
1189 )
tierno701018c2019-06-25 11:13:14 +00001190 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001191
1192 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001193 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001194 """
1195 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001196 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001197 :param version:
tiernob24258a2018-10-04 18:39:49 +02001198 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001199 :param id:
1200 :return: None
1201 """
1202 # 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 +01001203 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1204 main_topic, version, topic, id
1205 )
tiernof27c79b2018-03-12 17:08:42 +01001206 return
1207
tierno65ca36d2019-02-12 19:27:52 +01001208 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001209 def _extract_query_string_operations(kwargs, method):
1210 """
1211
1212 :param kwargs:
1213 :return:
1214 """
1215 query_string_operations = []
1216 if kwargs:
1217 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1218 if qs in kwargs and kwargs[qs].lower() != "false":
1219 query_string_operations.append(qs.lower() + ":" + method.lower())
1220 return query_string_operations
1221
1222 @staticmethod
1223 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001224 """
1225 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1226 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001227 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001228 :param kwargs: query string input.
1229 :param method: http method: GET, POSST, PUT, ...
1230 :param _id:
1231 :return: admin_query dictionary with keys:
1232 public: True, False or None
1233 force: True or False
1234 project_id: tuple with projects used for accessing an element
1235 set_project: tuple with projects that a created element will belong to
1236 method: show, list, delete, write
1237 """
garciadeblas4568a372021-03-24 09:19:48 +01001238 admin_query = {
1239 "force": False,
1240 "project_id": (token_info["project_id"],),
1241 "username": token_info["username"],
1242 "admin": token_info["admin"],
1243 "public": None,
1244 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1245 }
tierno65ca36d2019-02-12 19:27:52 +01001246 if kwargs:
1247 # FORCE
1248 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001249 if (
1250 kwargs["FORCE"].lower() != "false"
1251 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001252 admin_query["force"] = True
1253 del kwargs["FORCE"]
1254 # PUBLIC
1255 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001256 if (
1257 kwargs["PUBLIC"].lower() != "false"
1258 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001259 admin_query["public"] = True
1260 else:
1261 admin_query["public"] = False
1262 del kwargs["PUBLIC"]
1263 # ADMIN
1264 if "ADMIN" in kwargs:
1265 behave_as = kwargs.pop("ADMIN")
1266 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001267 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001268 raise NbiException(
1269 "Only admin projects can use 'ADMIN' query string",
1270 HTTPStatus.UNAUTHORIZED,
1271 )
1272 if (
1273 not behave_as or behave_as.lower() == "true"
1274 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001275 admin_query["project_id"] = ()
1276 elif isinstance(behave_as, (list, tuple)):
1277 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001278 else: # isinstance(behave_as, str)
1279 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001280 if "SET_PROJECT" in kwargs:
1281 set_project = kwargs.pop("SET_PROJECT")
1282 if not set_project:
1283 admin_query["set_project"] = list(admin_query["project_id"])
1284 else:
1285 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001286 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001287 if admin_query["project_id"]:
1288 for p in set_project:
1289 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001290 raise NbiException(
1291 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1292 "'ADMIN='{p}'".format(p=p),
1293 HTTPStatus.UNAUTHORIZED,
1294 )
tierno65ca36d2019-02-12 19:27:52 +01001295 admin_query["set_project"] = set_project
1296
1297 # PROJECT_READ
1298 # if "PROJECT_READ" in kwargs:
1299 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001300 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001301 if method == "GET":
1302 if _id:
1303 admin_query["method"] = "show"
1304 else:
1305 admin_query["method"] = "list"
1306 elif method == "DELETE":
1307 admin_query["method"] = "delete"
1308 else:
1309 admin_query["method"] = "write"
1310 return admin_query
1311
tiernoc94c3df2018-02-09 15:38:54 +01001312 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001313 def default(
1314 self,
1315 main_topic=None,
1316 version=None,
1317 topic=None,
1318 _id=None,
1319 item=None,
1320 *args,
1321 **kwargs
1322 ):
tierno701018c2019-06-25 11:13:14 +00001323 token_info = None
tiernof27c79b2018-03-12 17:08:42 +01001324 outdata = None
1325 _format = None
tierno0f98af52018-03-19 10:28:22 +01001326 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001327 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001328 rollback = []
tierno701018c2019-06-25 11:13:14 +00001329 engine_session = None
tiernoc94c3df2018-02-09 15:38:54 +01001330 try:
tiernob24258a2018-10-04 18:39:49 +02001331 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001332 raise NbiException(
1333 "URL must contain at least 'main_topic/version/topic'",
1334 HTTPStatus.METHOD_NOT_ALLOWED,
1335 )
1336 if main_topic not in (
1337 "admin",
1338 "vnfpkgm",
1339 "nsd",
1340 "nslcm",
1341 "pdu",
1342 "nst",
1343 "nsilcm",
1344 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001345 "vnflcm",
garciadeblas4568a372021-03-24 09:19:48 +01001346 ):
1347 raise NbiException(
1348 "URL main_topic '{}' not supported".format(main_topic),
1349 HTTPStatus.METHOD_NOT_ALLOWED,
1350 )
1351 if version != "v1":
1352 raise NbiException(
1353 "URL version '{}' not supported".format(version),
1354 HTTPStatus.METHOD_NOT_ALLOWED,
1355 )
tiernoc94c3df2018-02-09 15:38:54 +01001356
garciadeblas4568a372021-03-24 09:19:48 +01001357 if (
1358 kwargs
1359 and "METHOD" in kwargs
1360 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1361 ):
tiernof27c79b2018-03-12 17:08:42 +01001362 method = kwargs.pop("METHOD")
1363 else:
1364 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001365
garciadeblas4568a372021-03-24 09:19:48 +01001366 role_permission = self._check_valid_url_method(
1367 method, main_topic, version, topic, _id, item, *args
1368 )
1369 query_string_operations = self._extract_query_string_operations(
1370 kwargs, method
1371 )
tiernob24258a2018-10-04 18:39:49 +02001372 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001373 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001374 token_info = self.authenticator.authorize(
1375 role_permission, query_string_operations, _id
1376 )
tierno12eac3c2020-03-19 23:22:08 +00001377 if main_topic == "admin" and topic == "domains":
1378 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001379 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001380 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001381 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301382
vijay.r35ef2f72019-04-30 17:55:49 +05301383 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001384 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001385
tiernob24258a2018-10-04 18:39:49 +02001386 if main_topic == "nsd":
1387 engine_topic = "nsds"
1388 elif main_topic == "vnfpkgm":
1389 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001390 if topic == "vnfpkg_op_occs":
1391 engine_topic = "vnfpkgops"
1392 if topic == "vnf_packages" and item == "action":
1393 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001394 elif main_topic == "nslcm":
1395 engine_topic = "nsrs"
1396 if topic == "ns_lcm_op_occs":
1397 engine_topic = "nslcmops"
1398 if topic == "vnfrs" or topic == "vnf_instances":
1399 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001400 elif main_topic == "vnflcm":
1401 if topic == "vnf_lcm_op_occs":
1402 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001403 elif main_topic == "nst":
1404 engine_topic = "nsts"
1405 elif main_topic == "nsilcm":
1406 engine_topic = "nsis"
1407 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001408 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001409 elif main_topic == "pdu":
1410 engine_topic = "pdus"
garciadeblas4568a372021-03-24 09:19:48 +01001411 if (
1412 engine_topic == "vims"
1413 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001414 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001415
preethika.p329b8182020-04-22 12:25:39 +05301416 if topic == "subscriptions":
1417 engine_topic = main_topic + "_" + topic
1418
tiernoc94c3df2018-02-09 15:38:54 +01001419 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001420 if item in (
1421 "nsd_content",
1422 "package_content",
1423 "artifacts",
1424 "vnfd",
1425 "nsd",
1426 "nst",
1427 "nst_content",
1428 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001429 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001430 path = "$DESCRIPTOR"
1431 elif args:
1432 path = args
tiernob24258a2018-10-04 18:39:49 +02001433 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001434 path = ()
1435 else:
1436 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001437 file, _format = self.engine.get_file(
1438 engine_session,
1439 engine_topic,
1440 _id,
1441 path,
1442 cherrypy.request.headers.get("Accept"),
1443 )
tiernof27c79b2018-03-12 17:08:42 +01001444 outdata = file
1445 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001446 outdata = self.engine.get_item_list(
1447 engine_session, engine_topic, kwargs, api_req=True
1448 )
tiernoc94c3df2018-02-09 15:38:54 +01001449 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301450 if item == "reports":
1451 # TODO check that project_id (_id in this context) has permissions
1452 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301453 filter_q = None
1454 if "vcaStatusRefresh" in kwargs:
1455 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
1456 outdata = self.engine.get_item(engine_session, engine_topic, _id, filter_q, True)
delacruzramo271d2002019-12-02 21:00:37 +01001457
tiernof27c79b2018-03-12 17:08:42 +01001458 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001459 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001460 if topic in (
1461 "ns_descriptors_content",
1462 "vnf_packages_content",
1463 "netslice_templates_content",
1464 ):
tiernof27c79b2018-03-12 17:08:42 +01001465 _id = cherrypy.request.headers.get("Transaction-Id")
1466 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001467 _id, _ = self.engine.new_item(
1468 rollback,
1469 engine_session,
1470 engine_topic,
1471 {},
1472 None,
1473 cherrypy.request.headers,
1474 )
1475 completed = self.engine.upload_content(
1476 engine_session,
1477 engine_topic,
1478 _id,
1479 indata,
1480 kwargs,
1481 cherrypy.request.headers,
1482 )
tiernof27c79b2018-03-12 17:08:42 +01001483 if completed:
tiernob24258a2018-10-04 18:39:49 +02001484 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001485 else:
1486 cherrypy.response.headers["Transaction-Id"] = _id
1487 outdata = {"id": _id}
tiernob24258a2018-10-04 18:39:49 +02001488 elif topic == "ns_instances_content":
1489 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001490 _id, _ = self.engine.new_item(
1491 rollback, engine_session, engine_topic, indata, kwargs
1492 )
tiernob24258a2018-10-04 18:39:49 +02001493 # creates nslcmop
1494 indata["lcmOperationType"] = "instantiate"
1495 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001496 nslcmop_id, _ = self.engine.new_item(
1497 rollback, engine_session, "nslcmops", indata, None
1498 )
tiernob24258a2018-10-04 18:39:49 +02001499 self._set_location_header(main_topic, version, topic, _id)
kuuse078f55e2019-05-16 19:24:21 +02001500 outdata = {"id": _id, "nslcmop_id": nslcmop_id}
tiernob24258a2018-10-04 18:39:49 +02001501 elif topic == "ns_instances" and item:
1502 indata["lcmOperationType"] = item
1503 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001504 _id, _ = self.engine.new_item(
1505 rollback, engine_session, "nslcmops", indata, kwargs
1506 )
1507 self._set_location_header(
1508 main_topic, version, "ns_lcm_op_occs", _id
1509 )
tierno65acb4d2018-04-06 16:42:40 +02001510 outdata = {"id": _id}
1511 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001512 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001513 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001514 _id, _ = self.engine.new_item(
1515 rollback, engine_session, engine_topic, indata, kwargs
1516 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001517 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001518 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001519 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001520 nsilcmop_id, _ = self.engine.new_item(
1521 rollback, engine_session, "nsilcmops", indata, kwargs
1522 )
kuuse078f55e2019-05-16 19:24:21 +02001523 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001524 elif topic == "netslice_instances" and item:
1525 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001526 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001527 _id, _ = self.engine.new_item(
1528 rollback, engine_session, "nsilcmops", indata, kwargs
1529 )
1530 self._set_location_header(
1531 main_topic, version, "nsi_lcm_op_occs", _id
1532 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001533 outdata = {"id": _id}
1534 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001535 elif topic == "vnf_packages" and item == "action":
1536 indata["lcmOperationType"] = item
1537 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001538 _id, _ = self.engine.new_item(
1539 rollback, engine_session, "vnfpkgops", indata, kwargs
1540 )
1541 self._set_location_header(
1542 main_topic, version, "vnfpkg_op_occs", _id
1543 )
delacruzramo271d2002019-12-02 21:00:37 +01001544 outdata = {"id": _id}
1545 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301546 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001547 _id, _ = self.engine.new_item(
1548 rollback, engine_session, engine_topic, indata, kwargs
1549 )
preethika.p329b8182020-04-22 12:25:39 +05301550 self._set_location_header(main_topic, version, topic, _id)
1551 link = {}
1552 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001553 outdata = {
1554 "id": _id,
1555 "filter": indata["filter"],
1556 "callbackUri": indata["CallbackUri"],
1557 "_links": link,
1558 }
preethika.p329b8182020-04-22 12:25:39 +05301559 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001560 elif topic == "vnf_instances" and item:
1561 indata["lcmOperationType"] = item
1562 indata["vnfInstanceId"] = _id
1563 _id, _ = self.engine.new_item(rollback, engine_session, "vnflcmops", indata, kwargs)
1564 self._set_location_header(main_topic, version, "vnf_lcm_op_occs", _id)
1565 outdata = {"id": _id}
1566 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernof27c79b2018-03-12 17:08:42 +01001567 else:
garciadeblas4568a372021-03-24 09:19:48 +01001568 _id, op_id = self.engine.new_item(
1569 rollback,
1570 engine_session,
1571 engine_topic,
1572 indata,
1573 kwargs,
1574 cherrypy.request.headers,
1575 )
tiernob24258a2018-10-04 18:39:49 +02001576 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001577 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00001578 if op_id:
1579 outdata["op_id"] = op_id
1580 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001581 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02001582
tiernoc94c3df2018-02-09 15:38:54 +01001583 elif method == "DELETE":
1584 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001585 outdata = self.engine.del_item_list(
1586 engine_session, engine_topic, kwargs
1587 )
tierno09c073e2018-04-26 13:36:48 +02001588 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01001589 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00001590 # for NS NSI generate an operation
1591 op_id = None
tierno701018c2019-06-25 11:13:14 +00001592 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02001593 nslcmop_desc = {
1594 "lcmOperationType": "terminate",
1595 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001596 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02001597 }
garciadeblas4568a372021-03-24 09:19:48 +01001598 op_id, _ = self.engine.new_item(
1599 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
1600 )
tierno22577432020-04-08 15:16:57 +00001601 if op_id:
1602 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001603 elif (
1604 topic == "netslice_instances_content"
1605 and not engine_session["force"]
1606 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001607 nsilcmop_desc = {
1608 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02001609 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001610 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02001611 }
garciadeblas4568a372021-03-24 09:19:48 +01001612 op_id, _ = self.engine.new_item(
1613 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
1614 )
tierno22577432020-04-08 15:16:57 +00001615 if op_id:
1616 outdata = {"_id": op_id}
1617 # if there is not any deletion in process, delete
1618 if not op_id:
1619 op_id = self.engine.del_item(engine_session, engine_topic, _id)
1620 if op_id:
1621 outdata = {"op_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001622 cherrypy.response.status = (
1623 HTTPStatus.ACCEPTED.value
1624 if op_id
1625 else HTTPStatus.NO_CONTENT.value
1626 )
tierno09c073e2018-04-26 13:36:48 +02001627
tierno7ae10112018-05-18 14:36:02 +02001628 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00001629 op_id = None
tierno701018c2019-06-25 11:13:14 +00001630 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01001631 raise NbiException(
1632 "Nothing to update. Provide payload and/or query string",
1633 HTTPStatus.BAD_REQUEST,
1634 )
1635 if (
1636 item in ("nsd_content", "package_content", "nst_content")
1637 and method == "PUT"
1638 ):
1639 completed = self.engine.upload_content(
1640 engine_session,
1641 engine_topic,
1642 _id,
1643 indata,
1644 kwargs,
1645 cherrypy.request.headers,
1646 )
tiernof27c79b2018-03-12 17:08:42 +01001647 if not completed:
1648 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01001649 else:
garciadeblas4568a372021-03-24 09:19:48 +01001650 op_id = self.engine.edit_item(
1651 engine_session, engine_topic, _id, indata, kwargs
1652 )
tiernobdebce92019-07-01 15:36:49 +00001653
1654 if op_id:
1655 cherrypy.response.status = HTTPStatus.ACCEPTED.value
1656 outdata = {"op_id": op_id}
1657 else:
1658 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
1659 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01001660 else:
garciadeblas4568a372021-03-24 09:19:48 +01001661 raise NbiException(
1662 "Method {} not allowed".format(method),
1663 HTTPStatus.METHOD_NOT_ALLOWED,
1664 )
tiernoa6bb45d2019-06-14 09:45:39 +00001665
1666 # if Role information changes, it is needed to reload the information of roles
1667 if topic == "roles" and method != "GET":
1668 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01001669
garciadeblas4568a372021-03-24 09:19:48 +01001670 if (
1671 topic == "projects"
1672 and method == "DELETE"
1673 or topic in ["users", "roles"]
1674 and method in ["PUT", "PATCH", "DELETE"]
1675 ):
delacruzramoad682a52019-12-10 16:26:34 +01001676 self.authenticator.remove_token_from_cache()
1677
tierno701018c2019-06-25 11:13:14 +00001678 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02001679 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01001680 if isinstance(
1681 e,
1682 (
1683 NbiException,
1684 EngineException,
1685 DbException,
1686 FsException,
1687 MsgException,
1688 AuthException,
1689 ValidationError,
1690 AuthconnException,
1691 ),
1692 ):
tiernob24258a2018-10-04 18:39:49 +02001693 http_code_value = cherrypy.response.status = e.http_code.value
1694 http_code_name = e.http_code.name
1695 cherrypy.log("Exception {}".format(e))
1696 else:
garciadeblas4568a372021-03-24 09:19:48 +01001697 http_code_value = (
1698 cherrypy.response.status
1699 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01001700 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02001701 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02001702 if hasattr(outdata, "close"): # is an open file
1703 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02001704 error_text = str(e)
1705 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02001706 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02001707 try:
tiernocc103432018-10-19 14:10:35 +02001708 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01001709 self.engine.db.set_one(
1710 rollback_item["topic"],
1711 {"_id": rollback_item["_id"]},
1712 rollback_item["content"],
1713 fail_on_empty=False,
1714 )
preethika.p329b8182020-04-22 12:25:39 +05301715 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01001716 self.engine.db.del_list(
1717 rollback_item["topic"],
1718 rollback_item["filter"],
1719 fail_on_empty=False,
1720 )
tiernocc103432018-10-19 14:10:35 +02001721 else:
garciadeblas4568a372021-03-24 09:19:48 +01001722 self.engine.db.del_one(
1723 rollback_item["topic"],
1724 {"_id": rollback_item["_id"]},
1725 fail_on_empty=False,
1726 )
tierno3ace63c2018-05-03 17:51:43 +02001727 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01001728 rollback_error_text = "Rollback Exception {}: {}".format(
1729 rollback_item, e2
1730 )
tiernob24258a2018-10-04 18:39:49 +02001731 cherrypy.log(rollback_error_text)
1732 error_text += ". " + rollback_error_text
1733 # if isinstance(e, MsgException):
1734 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
1735 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01001736 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02001737 "code": http_code_name,
1738 "status": http_code_value,
1739 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01001740 }
tierno701018c2019-06-25 11:13:14 +00001741 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001742 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00001743 finally:
1744 if token_info:
1745 self._format_login(token_info)
1746 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
1747 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
1748 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01001749 cherrypy.request.login += ";{}={}".format(
1750 logging_id, outdata[logging_id][:36]
1751 )
tiernoc94c3df2018-02-09 15:38:54 +01001752
1753
tiernoc94c3df2018-02-09 15:38:54 +01001754def _start_service():
1755 """
1756 Callback function called when cherrypy.engine starts
1757 Override configuration with env variables
1758 Set database, storage, message configuration
1759 Init database with admin/admin user password
1760 """
tierno932499c2019-01-28 17:28:10 +00001761 global nbi_server
1762 global subscription_thread
tiernoc94c3df2018-02-09 15:38:54 +01001763 cherrypy.log.error("Starting osm_nbi")
1764 # update general cherrypy configuration
1765 update_dict = {}
1766
garciadeblas4568a372021-03-24 09:19:48 +01001767 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01001768 for k, v in environ.items():
1769 if not k.startswith("OSMNBI_"):
1770 continue
tiernoe1281182018-05-22 12:24:36 +02001771 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01001772 if not k2:
1773 continue
1774 try:
1775 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01001776 if k == "OSMNBI_STATIC_DIR":
1777 engine_config["/static"]["tools.staticdir.dir"] = v
1778 engine_config["/static"]["tools.staticdir.on"] = True
1779 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
1780 update_dict["server.socket_port"] = int(v)
1781 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
1782 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02001783 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01001784 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001785 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02001786 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001787 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01001788 engine_config[k1][k2] = int(v)
1789 else:
1790 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001791
tiernoc94c3df2018-02-09 15:38:54 +01001792 except ValueError as e:
1793 cherrypy.log.error("Ignoring environ '{}': " + str(e))
1794 except Exception as e:
1795 cherrypy.log.warn("skipping environ '{}' on exception '{}'".format(k, e))
1796
1797 if update_dict:
1798 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02001799 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001800
1801 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01001802 log_format_simple = (
1803 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
1804 )
1805 log_formatter_simple = logging.Formatter(
1806 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
1807 )
tiernoc94c3df2018-02-09 15:38:54 +01001808 logger_server = logging.getLogger("cherrypy.error")
1809 logger_access = logging.getLogger("cherrypy.access")
1810 logger_cherry = logging.getLogger("cherrypy")
1811 logger_nbi = logging.getLogger("nbi")
1812
tiernof5298be2018-05-16 14:43:57 +02001813 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01001814 file_handler = logging.handlers.RotatingFileHandler(
1815 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
1816 )
tiernoc94c3df2018-02-09 15:38:54 +01001817 file_handler.setFormatter(log_formatter_simple)
1818 logger_cherry.addHandler(file_handler)
1819 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02001820 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01001821 for format_, logger in {
1822 "nbi.server %(filename)s:%(lineno)s": logger_server,
1823 "nbi.access %(filename)s:%(lineno)s": logger_access,
1824 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
1825 }.items():
tiernob24258a2018-10-04 18:39:49 +02001826 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01001827 log_formatter_cherry = logging.Formatter(
1828 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
1829 )
tiernob24258a2018-10-04 18:39:49 +02001830 str_handler = logging.StreamHandler()
1831 str_handler.setFormatter(log_formatter_cherry)
1832 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01001833
tiernof5298be2018-05-16 14:43:57 +02001834 if engine_config["global"].get("log.level"):
1835 logger_cherry.setLevel(engine_config["global"]["log.level"])
1836 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01001837
1838 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01001839 for k1, logname in {
1840 "message": "nbi.msg",
1841 "database": "nbi.db",
1842 "storage": "nbi.fs",
1843 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01001844 engine_config[k1]["logger_name"] = logname
1845 logger_module = logging.getLogger(logname)
1846 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01001847 file_handler = logging.handlers.RotatingFileHandler(
1848 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
1849 )
tiernoc94c3df2018-02-09 15:38:54 +01001850 file_handler.setFormatter(log_formatter_simple)
1851 logger_module.addHandler(file_handler)
1852 if "loglevel" in engine_config[k1]:
1853 logger_module.setLevel(engine_config[k1]["loglevel"])
1854 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01001855 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
1856 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
1857 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
1858 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
1859 target_version=auth_database_version
1860 )
tiernobee508e2019-01-21 11:21:49 +00001861
tierno932499c2019-01-28 17:28:10 +00001862 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01001863 subscription_thread = SubscriptionThread(
1864 config=engine_config, engine=nbi_server.engine
1865 )
tierno932499c2019-01-28 17:28:10 +00001866 subscription_thread.start()
1867 # Do not capture except SubscriptionException
1868
tiernob2e48bd2020-02-04 15:47:18 +00001869 backend = engine_config["authentication"]["backend"]
garciadeblas4568a372021-03-24 09:19:48 +01001870 cherrypy.log.error(
1871 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
1872 nbi_version, nbi_version_date, backend
1873 )
1874 )
tiernoc94c3df2018-02-09 15:38:54 +01001875
1876
1877def _stop_service():
1878 """
1879 Callback function called when cherrypy.engine stops
1880 TODO: Ending database connections.
1881 """
tierno932499c2019-01-28 17:28:10 +00001882 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01001883 if subscription_thread:
1884 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00001885 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01001886 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01001887 cherrypy.log.error("Stopping osm_nbi")
1888
tierno2236d202018-05-16 19:05:16 +02001889
tiernof5298be2018-05-16 14:43:57 +02001890def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00001891 global nbi_server
tiernoc94c3df2018-02-09 15:38:54 +01001892 # conf = {
1893 # '/': {
1894 # #'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
1895 # 'tools.sessions.on': True,
1896 # 'tools.response_headers.on': True,
1897 # # 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
1898 # }
1899 # }
1900 # cherrypy.Server.ssl_module = 'builtin'
1901 # cherrypy.Server.ssl_certificate = "http/cert.pem"
1902 # cherrypy.Server.ssl_private_key = "http/privkey.pem"
1903 # cherrypy.Server.thread_pool = 10
1904 # cherrypy.config.update({'Server.socket_port': config["port"], 'Server.socket_host': config["host"]})
1905
1906 # cherrypy.config.update({'tools.auth_basic.on': True,
1907 # 'tools.auth_basic.realm': 'localhost',
1908 # 'tools.auth_basic.checkpassword': validate_password})
tierno932499c2019-01-28 17:28:10 +00001909 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01001910 cherrypy.engine.subscribe("start", _start_service)
1911 cherrypy.engine.subscribe("stop", _stop_service)
1912 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02001913
1914
1915def usage():
garciadeblas4568a372021-03-24 09:19:48 +01001916 print(
1917 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02001918 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
1919 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01001920 """.format(
1921 sys.argv[0]
1922 )
1923 )
tierno2236d202018-05-16 19:05:16 +02001924 # --log-socket-host HOST: send logs to this host")
1925 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01001926
1927
garciadeblas4568a372021-03-24 09:19:48 +01001928if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02001929 try:
1930 # load parameters and configuration
1931 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
1932 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
1933 config_file = None
1934 for o, a in opts:
1935 if o in ("-h", "--help"):
1936 usage()
1937 sys.exit()
1938 elif o in ("-c", "--config"):
1939 config_file = a
1940 # elif o == "--log-socket-port":
1941 # log_socket_port = a
1942 # elif o == "--log-socket-host":
1943 # log_socket_host = a
1944 # elif o == "--log-file":
1945 # log_file = a
1946 else:
1947 assert False, "Unhandled option"
1948 if config_file:
1949 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01001950 print(
1951 "configuration file '{}' that not exist".format(config_file),
1952 file=sys.stderr,
1953 )
tiernof5298be2018-05-16 14:43:57 +02001954 exit(1)
1955 else:
garciadeblas4568a372021-03-24 09:19:48 +01001956 for config_file in (
1957 __file__[: __file__.rfind(".")] + ".cfg",
1958 "./nbi.cfg",
1959 "/etc/osm/nbi.cfg",
1960 ):
tiernof5298be2018-05-16 14:43:57 +02001961 if path.isfile(config_file):
1962 break
1963 else:
garciadeblas4568a372021-03-24 09:19:48 +01001964 print(
1965 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
1966 file=sys.stderr,
1967 )
tiernof5298be2018-05-16 14:43:57 +02001968 exit(1)
1969 nbi(config_file)
1970 except getopt.GetoptError as e:
1971 print(str(e), file=sys.stderr)
1972 # usage()
1973 exit(1)