blob: f19f44d073d61209aafcafba073f5af059f234bc [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
tierno65acb4d2018-04-06 16:42:40 +020089 heal 5
aticig544a2ae2022-04-05 09:00:17 +030090 update 05
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
175
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:",
436 "scale": {
437 "METHODS": ("POST",),
438 "ROLE_PERMISSION": "ns_instances:id:scale:",
439 },
440 "terminate": {
441 "METHODS": ("POST",),
442 "ROLE_PERMISSION": "ns_instances:id:terminate:",
443 },
444 "instantiate": {
445 "METHODS": ("POST",),
446 "ROLE_PERMISSION": "ns_instances:id:instantiate:",
447 },
elumalai8e3806c2022-04-28 17:26:24 +0530448 "migrate": {
449 "METHODS": ("POST",),
450 "ROLE_PERMISSION": "ns_instances:id:migrate:",
451 },
garciadeblas4568a372021-03-24 09:19:48 +0100452 "action": {
453 "METHODS": ("POST",),
454 "ROLE_PERMISSION": "ns_instances:id:action:",
455 },
aticig544a2ae2022-04-05 09:00:17 +0300456 "update": {
457 "METHODS": ("POST",),
458 "ROLE_PERMISSION": "ns_instances:id:update:",
459 },
garciadeblas4568a372021-03-24 09:19:48 +0100460 },
461 },
462 "ns_lcm_op_occs": {
463 "METHODS": ("GET",),
464 "ROLE_PERMISSION": "ns_instances:opps:",
465 "<ID>": {
466 "METHODS": ("GET",),
467 "ROLE_PERMISSION": "ns_instances:opps:id:",
468 },
469 },
470 "vnfrs": {
471 "METHODS": ("GET",),
472 "ROLE_PERMISSION": "vnf_instances:",
473 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
474 },
475 "vnf_instances": {
476 "METHODS": ("GET",),
477 "ROLE_PERMISSION": "vnf_instances:",
478 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
479 },
480 "subscriptions": {
481 "METHODS": ("GET", "POST"),
482 "ROLE_PERMISSION": "ns_subscriptions:",
483 "<ID>": {
484 "METHODS": ("GET", "DELETE"),
485 "ROLE_PERMISSION": "ns_subscriptions:id:",
486 },
487 },
tierno701018c2019-06-25 11:13:14 +0000488 }
489 },
almagiae47b9132022-05-17 14:12:22 +0200490 "vnflcm": {
491 "v1": {
492 "vnf_instances": {"METHODS": ("GET", "POST"),
493 "ROLE_PERMISSION": "vnflcm_instances:",
494 "<ID>": {"METHODS": ("GET", "DELETE"),
495 "ROLE_PERMISSION": "vnflcm_instances:id:",
496 "scale": {"METHODS": ("POST",),
497 "ROLE_PERMISSION": "vnflcm_instances:id:scale:"
498 },
499 "terminate": {"METHODS": ("POST",),
500 "ROLE_PERMISSION": "vnflcm_instances:id:terminate:"
501 },
502 "instantiate": {"METHODS": ("POST",),
503 "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:"
504 },
505 }
506 },
507 "vnf_lcm_op_occs": {"METHODS": ("GET",),
508 "ROLE_PERMISSION": "vnf_instances:opps:",
509 "<ID>": {"METHODS": ("GET",),
510 "ROLE_PERMISSION": "vnf_instances:opps:id:"
511 },
512 },
selvi.jf1004592022-04-29 05:42:35 +0000513 "subscriptions": {"METHODS": ("GET", "POST"),
514 "ROLE_PERMISSION": "vnflcm_subscriptions:",
515 "<ID>": {"METHODS": ("GET", "DELETE"),
516 "ROLE_PERMISSION": "vnflcm_subscriptions:id:"
517 }
518 },
almagiae47b9132022-05-17 14:12:22 +0200519 }
520 },
tierno701018c2019-06-25 11:13:14 +0000521 "nst": {
522 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100523 "netslice_templates_content": {
524 "METHODS": ("GET", "POST"),
525 "ROLE_PERMISSION": "slice_templates:",
526 "<ID>": {
527 "METHODS": ("GET", "PUT", "DELETE"),
528 "ROLE_PERMISSION": "slice_templates:id:",
529 },
530 },
531 "netslice_templates": {
532 "METHODS": ("GET", "POST"),
533 "ROLE_PERMISSION": "slice_templates:",
534 "<ID>": {
535 "METHODS": ("GET", "DELETE"),
536 "TODO": ("PATCH",),
537 "ROLE_PERMISSION": "slice_templates:id:",
538 "nst_content": {
539 "METHODS": ("GET", "PUT"),
540 "ROLE_PERMISSION": "slice_templates:id:content:",
541 },
542 "nst": {
543 "METHODS": ("GET",), # descriptor inside package
544 "ROLE_PERMISSION": "slice_templates:id:content:",
545 },
546 "artifacts": {
547 "METHODS": ("GET",),
548 "ROLE_PERMISSION": "slice_templates:id:content:",
549 "*": None,
550 },
551 },
552 },
553 "subscriptions": {
554 "TODO": ("GET", "POST"),
555 "<ID>": {"TODO": ("GET", "DELETE")},
556 },
tierno701018c2019-06-25 11:13:14 +0000557 }
558 },
559 "nsilcm": {
560 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100561 "netslice_instances_content": {
562 "METHODS": ("GET", "POST"),
563 "ROLE_PERMISSION": "slice_instances:",
564 "<ID>": {
565 "METHODS": ("GET", "DELETE"),
566 "ROLE_PERMISSION": "slice_instances:id:",
567 },
568 },
569 "netslice_instances": {
570 "METHODS": ("GET", "POST"),
571 "ROLE_PERMISSION": "slice_instances:",
572 "<ID>": {
573 "METHODS": ("GET", "DELETE"),
574 "ROLE_PERMISSION": "slice_instances:id:",
575 "terminate": {
576 "METHODS": ("POST",),
577 "ROLE_PERMISSION": "slice_instances:id:terminate:",
578 },
579 "instantiate": {
580 "METHODS": ("POST",),
581 "ROLE_PERMISSION": "slice_instances:id:instantiate:",
582 },
583 "action": {
584 "METHODS": ("POST",),
585 "ROLE_PERMISSION": "slice_instances:id:action:",
586 },
587 },
588 },
589 "nsi_lcm_op_occs": {
590 "METHODS": ("GET",),
591 "ROLE_PERMISSION": "slice_instances:opps:",
592 "<ID>": {
593 "METHODS": ("GET",),
594 "ROLE_PERMISSION": "slice_instances:opps:id:",
595 },
596 },
tierno701018c2019-06-25 11:13:14 +0000597 }
598 },
599 "nspm": {
600 "v1": {
601 "pm_jobs": {
602 "<ID>": {
603 "reports": {
garciadeblas4568a372021-03-24 09:19:48 +0100604 "<ID>": {
605 "METHODS": ("GET",),
606 "ROLE_PERMISSION": "reports:id:",
607 }
tierno701018c2019-06-25 11:13:14 +0000608 }
609 },
610 },
611 },
612 },
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000613 "nsfm": {
614 "v1": {
615 "alarms": {"METHODS": ("GET", "PATCH"),
616 "ROLE_PERMISSION": "alarms:",
617 "<ID>": {"METHODS": ("GET", "PATCH"),
618 "ROLE_PERMISSION": "alarms:id:",
619 },
620 }
621 },
622 },
tierno701018c2019-06-25 11:13:14 +0000623}
624
tiernoc94c3df2018-02-09 15:38:54 +0100625
626class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100627 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
628 Exception.__init__(self, message)
629 self.http_code = http_code
630
631
632class Server(object):
633 instance = 0
634 # to decode bytes to str
635 reader = getreader("utf-8")
636
637 def __init__(self):
638 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000639 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100640 self.engine = Engine(self.authenticator)
tiernoc94c3df2018-02-09 15:38:54 +0100641
tiernoc94c3df2018-02-09 15:38:54 +0100642 def _format_in(self, kwargs):
643 try:
644 indata = None
645 if cherrypy.request.body.length:
646 error_text = "Invalid input format "
647
648 if "Content-Type" in cherrypy.request.headers:
649 if "application/json" in cherrypy.request.headers["Content-Type"]:
650 error_text = "Invalid json format "
651 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100652 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100653 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
654 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100655 indata = yaml.load(
656 cherrypy.request.body, Loader=yaml.SafeLoader
657 )
gcalvinode4adfe2018-10-30 11:46:09 +0100658 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100659 elif (
660 "application/binary" in cherrypy.request.headers["Content-Type"]
661 or "application/gzip"
662 in cherrypy.request.headers["Content-Type"]
663 or "application/zip" in cherrypy.request.headers["Content-Type"]
664 or "text/plain" in cherrypy.request.headers["Content-Type"]
665 ):
tiernof27c79b2018-03-12 17:08:42 +0100666 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100667 elif (
668 "multipart/form-data"
669 in cherrypy.request.headers["Content-Type"]
670 ):
tiernoc94c3df2018-02-09 15:38:54 +0100671 if "descriptor_file" in kwargs:
672 filecontent = kwargs.pop("descriptor_file")
673 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100674 raise NbiException(
675 "empty file or content", HTTPStatus.BAD_REQUEST
676 )
tiernof27c79b2018-03-12 17:08:42 +0100677 indata = filecontent.file # .read()
tiernoc94c3df2018-02-09 15:38:54 +0100678 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100679 cherrypy.request.headers[
680 "Content-Type"
681 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100682 else:
683 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
684 # "Only 'Content-Type' of type 'application/json' or
685 # 'application/yaml' for input format are available")
686 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100687 indata = yaml.load(
688 cherrypy.request.body, Loader=yaml.SafeLoader
689 )
gcalvinode4adfe2018-10-30 11:46:09 +0100690 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100691 else:
692 error_text = "Invalid yaml format "
delacruzramob19cadc2019-10-08 10:18:02 +0200693 indata = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
gcalvinode4adfe2018-10-30 11:46:09 +0100694 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100695 if not indata:
696 indata = {}
697
tiernoc94c3df2018-02-09 15:38:54 +0100698 format_yaml = False
699 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
700 format_yaml = True
701
702 for k, v in kwargs.items():
703 if isinstance(v, str):
704 if v == "":
705 kwargs[k] = None
706 elif format_yaml:
707 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200708 kwargs[k] = yaml.load(v, Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200709 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100710 pass
garciadeblas4568a372021-03-24 09:19:48 +0100711 elif (
712 k.endswith(".gt")
713 or k.endswith(".lt")
714 or k.endswith(".gte")
715 or k.endswith(".lte")
716 ):
tiernoc94c3df2018-02-09 15:38:54 +0100717 try:
718 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200719 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100720 try:
721 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200722 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100723 pass
724 elif v.find(",") > 0:
725 kwargs[k] = v.split(",")
726 elif isinstance(v, (list, tuple)):
727 for index in range(0, len(v)):
728 if v[index] == "":
729 v[index] = None
730 elif format_yaml:
731 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200732 v[index] = yaml.load(v[index], Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200733 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100734 pass
735
tiernof27c79b2018-03-12 17:08:42 +0100736 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100737 except (ValueError, yaml.YAMLError) as exc:
738 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
739 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100740 raise NbiException(
741 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
742 )
tiernob92094f2018-05-11 13:44:22 +0200743 except Exception as exc:
744 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100745
746 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000747 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100748 """
749 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100750 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000751 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000752 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100753 :return: None
754 """
tierno0f98af52018-03-19 10:28:22 +0100755 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100756 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100757 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100758 return html.format(
759 data, cherrypy.request, cherrypy.response, token_info
760 )
tierno09c073e2018-04-26 13:36:48 +0200761 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100762 return
763 elif hasattr(data, "read"): # file object
764 if _format:
765 cherrypy.response.headers["Content-Type"] = _format
766 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100767 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100768 else:
garciadeblas4568a372021-03-24 09:19:48 +0100769 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100770 # TODO check that cherrypy close file. If not implement pending things to close per thread next
771 return data
tierno0f98af52018-03-19 10:28:22 +0100772 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000773 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100774 return html.format(
775 data, cherrypy.request, cherrypy.response, token_info
776 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000777 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100778 pass
garciadeblas4568a372021-03-24 09:19:48 +0100779 elif "application/json" in accept or (
780 cherrypy.response.status and cherrypy.response.status >= 300
781 ):
782 cherrypy.response.headers[
783 "Content-Type"
784 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000785 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100786 return a.encode("utf8")
787 cherrypy.response.headers["Content-Type"] = "application/yaml"
788 return yaml.safe_dump(
789 data,
790 explicit_start=True,
791 indent=4,
792 default_flow_style=False,
793 tags=False,
794 encoding="utf-8",
795 allow_unicode=True,
796 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +0100797
798 @cherrypy.expose
799 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +0000800 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100801 try:
802 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +0000803 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +0100804 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +0100805 else:
garciadeblas4568a372021-03-24 09:19:48 +0100806 raise cherrypy.HTTPError(
807 HTTPStatus.METHOD_NOT_ALLOWED.value,
808 "Method {} not allowed for tokens".format(cherrypy.request.method),
809 )
tiernoc94c3df2018-02-09 15:38:54 +0100810
tierno701018c2019-06-25 11:13:14 +0000811 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100812
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100813 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +0000814 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +0100815 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +0000816 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100817
818 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +0200819 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +0200820 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +0200821 try:
822 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +0100823 raise NbiException(
824 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
825 )
tierno55945e72018-04-06 16:40:27 +0200826 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +0100827 raise NbiException(
828 "Invalid URL or query string for version",
829 HTTPStatus.METHOD_NOT_ALLOWED,
830 )
tierno9c630112019-08-29 14:21:41 +0000831 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +0000832 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
833 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +0200834 except NbiException as e:
835 cherrypy.response.status = e.http_code.value
836 problem_details = {
837 "code": e.http_code.name,
838 "status": e.http_code.value,
839 "detail": str(e),
840 }
841 return self._format_out(problem_details, None)
842
tierno12eac3c2020-03-19 23:22:08 +0000843 def domain(self):
844 try:
845 domains = {
garciadeblas4568a372021-03-24 09:19:48 +0100846 "user_domain_name": cherrypy.tree.apps["/osm"]
847 .config["authentication"]
848 .get("user_domain_name"),
849 "project_domain_name": cherrypy.tree.apps["/osm"]
850 .config["authentication"]
851 .get("project_domain_name"),
852 }
tierno12eac3c2020-03-19 23:22:08 +0000853 return self._format_out(domains)
854 except NbiException as e:
855 cherrypy.response.status = e.http_code.value
856 problem_details = {
857 "code": e.http_code.name,
858 "status": e.http_code.value,
859 "detail": str(e),
860 }
861 return self._format_out(problem_details, None)
862
tiernoa5035702019-07-29 08:54:42 +0000863 @staticmethod
864 def _format_login(token_info):
865 """
866 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
867 log this information
868 :param token_info: Dictionary with token content
869 :return: None
870 """
871 cherrypy.request.login = token_info.get("username", "-")
872 if token_info.get("project_name"):
873 cherrypy.request.login += "/" + token_info["project_name"]
874 if token_info.get("id"):
875 cherrypy.request.login += ";session=" + token_info["id"][0:12]
876
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000877 # NS Fault Management
878 @cherrypy.expose
879 def nsfm(self, version=None, topic=None, uuid=None, project_name=None, ns_id=None, *args, **kwargs):
880 if topic == 'alarms':
881 try:
882 method = cherrypy.request.method
883 role_permission = self._check_valid_url_method(method, "nsfm", version, topic, None, None, *args)
884 query_string_operations = self._extract_query_string_operations(kwargs, method)
885
886 self.authenticator.authorize(role_permission, query_string_operations, None)
887
888 # to handle get request
889 if cherrypy.request.method == 'GET':
890 # if request is on basis of uuid
891 if uuid and uuid != 'None':
892 try:
893 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
894 alarm_action = self.engine.db.get_one("alarms_action", {"uuid": uuid})
895 alarm.update(alarm_action)
896 vnf = self.engine.db.get_one("vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]})
897 alarm["vnf-id"] = vnf["_id"]
898 return self._format_out(str(alarm))
899 except Exception:
900 return self._format_out("Please provide valid alarm uuid")
901 elif ns_id and ns_id != 'None':
902 # if request is on basis of ns_id
903 try:
904 alarms = self.engine.db.get_list("alarms", {"tags.ns_id": ns_id})
905 for alarm in alarms:
906 alarm_action = self.engine.db.get_one("alarms_action", {"uuid": alarm['uuid']})
907 alarm.update(alarm_action)
908 return self._format_out(str(alarms))
909 except Exception:
910 return self._format_out("Please provide valid ns id")
911 else:
912 # to return only alarm which are related to given project
913 project = self.engine.db.get_one("projects", {"name": project_name})
914 project_id = project.get('_id')
915 ns_list = self.engine.db.get_list("nsrs", {"_admin.projects_read": project_id})
916 ns_ids = []
917 for ns in ns_list:
918 ns_ids.append(ns.get("_id"))
919 alarms = self.engine.db.get_list("alarms")
920 alarm_list = [alarm for alarm in alarms if alarm["tags"]["ns_id"] in ns_ids]
921 for alrm in alarm_list:
922 action = self.engine.db.get_one("alarms_action", {"uuid": alrm.get("uuid")})
923 alrm.update(action)
924 return self._format_out(str(alarm_list))
925 # to handle patch request for alarm update
926 elif cherrypy.request.method == 'PATCH':
927 data = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
928 try:
929 # check if uuid is valid
930 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
931 except Exception:
932 return self._format_out("Please provide valid alarm uuid.")
933 if data.get("is_enable") is not None:
934 if data.get("is_enable"):
935 alarm_status = 'ok'
936 else:
937 alarm_status = 'disabled'
938 self.engine.db.set_one("alarms", {"uuid": data.get("uuid")},
939 {"alarm_status": alarm_status})
940 else:
941 self.engine.db.set_one("alarms", {"uuid": data.get("uuid")},
942 {"threshold": data.get("threshold")})
943 return self._format_out("Alarm updated")
944 except Exception as e:
945 cherrypy.response.status = e.http_code.value
946 if isinstance(e, (NbiException, EngineException, DbException, FsException, MsgException, AuthException,
947 ValidationError, AuthconnException)):
948 http_code_value = cherrypy.response.status = e.http_code.value
949 http_code_name = e.http_code.name
950 cherrypy.log("Exception {}".format(e))
951 else:
952 http_code_value = cherrypy.response.status = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
953 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
954 http_code_name = HTTPStatus.BAD_REQUEST.name
955 problem_details = {
956 "code": http_code_name,
957 "status": http_code_value,
958 "detail": str(e),
959 }
960 return self._format_out(problem_details)
961
tierno55945e72018-04-06 16:40:27 +0200962 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +0100963 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +0000964 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100965 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +0100966 indata = self._format_in(kwargs)
967 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +0100968 raise NbiException(
969 "Expected application/yaml or application/json Content-Type",
970 HTTPStatus.BAD_REQUEST,
971 )
tiernoa5035702019-07-29 08:54:42 +0000972
973 if method == "GET":
974 token_info = self.authenticator.authorize()
975 # for logging
976 self._format_login(token_info)
977 if token_id:
978 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +0100979 else:
tiernoa5035702019-07-29 08:54:42 +0000980 outdata = self.authenticator.get_token_list(token_info)
981 elif method == "POST":
982 try:
983 token_info = self.authenticator.authorize()
984 except Exception:
985 token_info = None
986 if kwargs:
987 indata.update(kwargs)
988 # This is needed to log the user when authentication fails
989 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +0100990 outdata = token_info = self.authenticator.new_token(
991 token_info, indata, cherrypy.request.remote
992 )
993 cherrypy.session["Authorization"] = outdata["_id"]
tiernoa5035702019-07-29 08:54:42 +0000994 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
995 # for logging
996 self._format_login(token_info)
selvi.ja9a1fc82022-04-04 06:54:30 +0000997 # password expiry check
998 if self.authenticator.check_password_expiry(outdata):
999 outdata = {"id": outdata["id"],
1000 "message": "change_password",
1001 "user_id": outdata["user_id"]
1002 }
tiernoa5035702019-07-29 08:54:42 +00001003 # cherrypy.response.cookie["Authorization"] = outdata["id"]
1004 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
1005 elif method == "DELETE":
1006 if not token_id and "id" in kwargs:
1007 token_id = kwargs["id"]
1008 elif not token_id:
1009 token_info = self.authenticator.authorize()
1010 # for logging
1011 self._format_login(token_info)
1012 token_id = token_info["_id"]
1013 outdata = self.authenticator.del_token(token_id)
1014 token_info = None
garciadeblas4568a372021-03-24 09:19:48 +01001015 cherrypy.session["Authorization"] = "logout"
tiernoa5035702019-07-29 08:54:42 +00001016 # cherrypy.response.cookie["Authorization"] = token_id
1017 # cherrypy.response.cookie["Authorization"]['expires'] = 0
1018 else:
garciadeblas4568a372021-03-24 09:19:48 +01001019 raise NbiException(
1020 "Method {} not allowed for token".format(method),
1021 HTTPStatus.METHOD_NOT_ALLOWED,
1022 )
tiernoa5035702019-07-29 08:54:42 +00001023 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001024
1025 @cherrypy.expose
1026 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +01001027 if not cherrypy.config.get("server.enable_test") or (
1028 isinstance(cherrypy.config["server.enable_test"], str)
1029 and cherrypy.config["server.enable_test"].lower() == "false"
1030 ):
tierno4836bac2020-01-15 14:41:48 +00001031 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
1032 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +01001033 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +01001034 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +01001035 return (
1036 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1037 "sleep/<time>\nmessage/topic\n</pre></html>"
1038 )
tiernof27c79b2018-03-12 17:08:42 +01001039
1040 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001041 try:
1042 # self.engine.load_dbase(cherrypy.request.app.config)
1043 self.engine.create_admin()
1044 return "Done. User 'admin', password 'admin' created"
1045 except Exception:
1046 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1047 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001048 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001049 return cherrypy.lib.static.serve_file(
1050 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1051 "text/plain",
1052 "attachment",
1053 )
tiernof27c79b2018-03-12 17:08:42 +01001054 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001055 f_path = (
1056 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1057 )
tiernof27c79b2018-03-12 17:08:42 +01001058 f = open(f_path, "r")
1059 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001060 return f
tierno55945e72018-04-06 16:40:27 +02001061
tiernof27c79b2018-03-12 17:08:42 +01001062 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001063 deleted_info = self.engine.db.del_list(args[1], kwargs)
1064 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1065 elif len(args) and args[0] == "fs-clear":
1066 if len(args) >= 2:
1067 folders = (args[1],)
1068 else:
1069 folders = self.engine.fs.dir_ls(".")
1070 for folder in folders:
1071 self.engine.fs.file_delete(folder)
1072 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001073 elif args and args[0] == "login":
1074 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001075 cherrypy.response.headers[
1076 "WWW-Authenticate"
1077 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001078 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1079 elif args and args[0] == "login2":
1080 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001081 cherrypy.response.headers[
1082 "WWW-Authenticate"
1083 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001084 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1085 elif args and args[0] == "sleep":
1086 sleep_time = 5
1087 try:
1088 sleep_time = int(args[1])
1089 except Exception:
1090 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1091 return self._format_out("Database already initialized")
1092 thread_info = cherrypy.thread_data
1093 print(thread_info)
1094 time.sleep(sleep_time)
1095 # thread_info
1096 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001097 main_topic = args[1]
1098 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001099 try:
garciadeblas4568a372021-03-24 09:19:48 +01001100 if cherrypy.request.method == "POST":
delacruzramob19cadc2019-10-08 10:18:02 +02001101 to_send = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
tierno55945e72018-04-06 16:40:27 +02001102 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001103 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001104 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001105 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001106 for k, v in kwargs.items():
tiernof1509b22020-05-12 14:32:37 +00001107 v_dict = yaml.load(v, Loader=yaml.SafeLoader)
1108 self.engine.msg.write(main_topic, k, v_dict)
1109 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001110 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001111 return_text += "Error: " + str(e)
1112 return_text += "</pre></html>\n"
1113 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001114
1115 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001116 "<html><pre>\nheaders:\n args: {}\n".format(args)
1117 + " kwargs: {}\n".format(kwargs)
1118 + " headers: {}\n".format(cherrypy.request.headers)
1119 + " path_info: {}\n".format(cherrypy.request.path_info)
1120 + " query_string: {}\n".format(cherrypy.request.query_string)
1121 + " session: {}\n".format(cherrypy.session)
1122 + " cookie: {}\n".format(cherrypy.request.cookie)
1123 + " method: {}\n".format(cherrypy.request.method)
1124 + " session: {}\n".format(cherrypy.session.get("fieldname"))
1125 + " body:\n"
1126 )
tiernoc94c3df2018-02-09 15:38:54 +01001127 return_text += " length: {}\n".format(cherrypy.request.body.length)
1128 if cherrypy.request.body.length:
1129 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001130 str(
1131 cherrypy.request.body.read(
1132 int(cherrypy.request.headers.get("Content-Length", 0))
1133 )
1134 )
1135 )
tiernoc94c3df2018-02-09 15:38:54 +01001136 if thread_info:
1137 return_text += "thread: {}\n".format(thread_info)
1138 return_text += "</pre></html>"
1139 return return_text
1140
tierno701018c2019-06-25 11:13:14 +00001141 @staticmethod
1142 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001143 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001144 raise NbiException(
1145 "URL must contain at least 'main_topic/version/topic'",
1146 HTTPStatus.METHOD_NOT_ALLOWED,
1147 )
tiernof27c79b2018-03-12 17:08:42 +01001148
tierno701018c2019-06-25 11:13:14 +00001149 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001150 for arg in args:
1151 if arg is None:
1152 break
1153 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001154 raise NbiException(
1155 "URL contains unexpected extra items '{}'".format(arg),
1156 HTTPStatus.METHOD_NOT_ALLOWED,
1157 )
tiernof27c79b2018-03-12 17:08:42 +01001158
1159 if arg in reference:
1160 reference = reference[arg]
1161 elif "<ID>" in reference:
1162 reference = reference["<ID>"]
1163 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001164 # if there is content
1165 if reference["*"]:
1166 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001167 break
1168 else:
garciadeblas4568a372021-03-24 09:19:48 +01001169 raise NbiException(
1170 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1171 )
tiernof27c79b2018-03-12 17:08:42 +01001172 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001173 raise NbiException(
1174 "Method {} not supported yet for this URL".format(method),
1175 HTTPStatus.NOT_IMPLEMENTED,
1176 )
tierno2236d202018-05-16 19:05:16 +02001177 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001178 raise NbiException(
1179 "Method {} not supported for this URL".format(method),
1180 HTTPStatus.METHOD_NOT_ALLOWED,
1181 )
tierno701018c2019-06-25 11:13:14 +00001182 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001183
1184 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001185 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001186 """
1187 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001188 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001189 :param version:
tiernob24258a2018-10-04 18:39:49 +02001190 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001191 :param id:
1192 :return: None
1193 """
1194 # 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 +01001195 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1196 main_topic, version, topic, id
1197 )
tiernof27c79b2018-03-12 17:08:42 +01001198 return
1199
tierno65ca36d2019-02-12 19:27:52 +01001200 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001201 def _extract_query_string_operations(kwargs, method):
1202 """
1203
1204 :param kwargs:
1205 :return:
1206 """
1207 query_string_operations = []
1208 if kwargs:
1209 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1210 if qs in kwargs and kwargs[qs].lower() != "false":
1211 query_string_operations.append(qs.lower() + ":" + method.lower())
1212 return query_string_operations
1213
1214 @staticmethod
1215 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001216 """
1217 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1218 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001219 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001220 :param kwargs: query string input.
1221 :param method: http method: GET, POSST, PUT, ...
1222 :param _id:
1223 :return: admin_query dictionary with keys:
1224 public: True, False or None
1225 force: True or False
1226 project_id: tuple with projects used for accessing an element
1227 set_project: tuple with projects that a created element will belong to
1228 method: show, list, delete, write
1229 """
garciadeblas4568a372021-03-24 09:19:48 +01001230 admin_query = {
1231 "force": False,
1232 "project_id": (token_info["project_id"],),
1233 "username": token_info["username"],
1234 "admin": token_info["admin"],
1235 "public": None,
1236 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1237 }
tierno65ca36d2019-02-12 19:27:52 +01001238 if kwargs:
1239 # FORCE
1240 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001241 if (
1242 kwargs["FORCE"].lower() != "false"
1243 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001244 admin_query["force"] = True
1245 del kwargs["FORCE"]
1246 # PUBLIC
1247 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001248 if (
1249 kwargs["PUBLIC"].lower() != "false"
1250 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001251 admin_query["public"] = True
1252 else:
1253 admin_query["public"] = False
1254 del kwargs["PUBLIC"]
1255 # ADMIN
1256 if "ADMIN" in kwargs:
1257 behave_as = kwargs.pop("ADMIN")
1258 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001259 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001260 raise NbiException(
1261 "Only admin projects can use 'ADMIN' query string",
1262 HTTPStatus.UNAUTHORIZED,
1263 )
1264 if (
1265 not behave_as or behave_as.lower() == "true"
1266 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001267 admin_query["project_id"] = ()
1268 elif isinstance(behave_as, (list, tuple)):
1269 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001270 else: # isinstance(behave_as, str)
1271 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001272 if "SET_PROJECT" in kwargs:
1273 set_project = kwargs.pop("SET_PROJECT")
1274 if not set_project:
1275 admin_query["set_project"] = list(admin_query["project_id"])
1276 else:
1277 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001278 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001279 if admin_query["project_id"]:
1280 for p in set_project:
1281 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001282 raise NbiException(
1283 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1284 "'ADMIN='{p}'".format(p=p),
1285 HTTPStatus.UNAUTHORIZED,
1286 )
tierno65ca36d2019-02-12 19:27:52 +01001287 admin_query["set_project"] = set_project
1288
1289 # PROJECT_READ
1290 # if "PROJECT_READ" in kwargs:
1291 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001292 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001293 if method == "GET":
1294 if _id:
1295 admin_query["method"] = "show"
1296 else:
1297 admin_query["method"] = "list"
1298 elif method == "DELETE":
1299 admin_query["method"] = "delete"
1300 else:
1301 admin_query["method"] = "write"
1302 return admin_query
1303
tiernoc94c3df2018-02-09 15:38:54 +01001304 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001305 def default(
1306 self,
1307 main_topic=None,
1308 version=None,
1309 topic=None,
1310 _id=None,
1311 item=None,
1312 *args,
1313 **kwargs
1314 ):
tierno701018c2019-06-25 11:13:14 +00001315 token_info = None
tiernof27c79b2018-03-12 17:08:42 +01001316 outdata = None
1317 _format = None
tierno0f98af52018-03-19 10:28:22 +01001318 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001319 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001320 rollback = []
tierno701018c2019-06-25 11:13:14 +00001321 engine_session = None
tiernoc94c3df2018-02-09 15:38:54 +01001322 try:
tiernob24258a2018-10-04 18:39:49 +02001323 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001324 raise NbiException(
1325 "URL must contain at least 'main_topic/version/topic'",
1326 HTTPStatus.METHOD_NOT_ALLOWED,
1327 )
1328 if main_topic not in (
1329 "admin",
1330 "vnfpkgm",
1331 "nsd",
1332 "nslcm",
1333 "pdu",
1334 "nst",
1335 "nsilcm",
1336 "nspm",
almagiae47b9132022-05-17 14:12:22 +02001337 "vnflcm",
garciadeblas4568a372021-03-24 09:19:48 +01001338 ):
1339 raise NbiException(
1340 "URL main_topic '{}' not supported".format(main_topic),
1341 HTTPStatus.METHOD_NOT_ALLOWED,
1342 )
1343 if version != "v1":
1344 raise NbiException(
1345 "URL version '{}' not supported".format(version),
1346 HTTPStatus.METHOD_NOT_ALLOWED,
1347 )
tiernoc94c3df2018-02-09 15:38:54 +01001348
garciadeblas4568a372021-03-24 09:19:48 +01001349 if (
1350 kwargs
1351 and "METHOD" in kwargs
1352 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1353 ):
tiernof27c79b2018-03-12 17:08:42 +01001354 method = kwargs.pop("METHOD")
1355 else:
1356 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001357
garciadeblas4568a372021-03-24 09:19:48 +01001358 role_permission = self._check_valid_url_method(
1359 method, main_topic, version, topic, _id, item, *args
1360 )
1361 query_string_operations = self._extract_query_string_operations(
1362 kwargs, method
1363 )
tiernob24258a2018-10-04 18:39:49 +02001364 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001365 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001366 token_info = self.authenticator.authorize(
1367 role_permission, query_string_operations, _id
1368 )
tierno12eac3c2020-03-19 23:22:08 +00001369 if main_topic == "admin" and topic == "domains":
1370 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001371 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001372 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001373 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301374
vijay.r35ef2f72019-04-30 17:55:49 +05301375 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001376 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001377
tiernob24258a2018-10-04 18:39:49 +02001378 if main_topic == "nsd":
1379 engine_topic = "nsds"
1380 elif main_topic == "vnfpkgm":
1381 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001382 if topic == "vnfpkg_op_occs":
1383 engine_topic = "vnfpkgops"
1384 if topic == "vnf_packages" and item == "action":
1385 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001386 elif main_topic == "nslcm":
1387 engine_topic = "nsrs"
1388 if topic == "ns_lcm_op_occs":
1389 engine_topic = "nslcmops"
1390 if topic == "vnfrs" or topic == "vnf_instances":
1391 engine_topic = "vnfrs"
almagiae47b9132022-05-17 14:12:22 +02001392 elif main_topic == "vnflcm":
1393 if topic == "vnf_lcm_op_occs":
1394 engine_topic = "vnflcmops"
garciadeblas9750c5a2018-10-15 16:20:35 +02001395 elif main_topic == "nst":
1396 engine_topic = "nsts"
1397 elif main_topic == "nsilcm":
1398 engine_topic = "nsis"
1399 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001400 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001401 elif main_topic == "pdu":
1402 engine_topic = "pdus"
garciadeblas4568a372021-03-24 09:19:48 +01001403 if (
1404 engine_topic == "vims"
1405 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001406 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001407
preethika.p329b8182020-04-22 12:25:39 +05301408 if topic == "subscriptions":
1409 engine_topic = main_topic + "_" + topic
1410
tiernoc94c3df2018-02-09 15:38:54 +01001411 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001412 if item in (
1413 "nsd_content",
1414 "package_content",
1415 "artifacts",
1416 "vnfd",
1417 "nsd",
1418 "nst",
1419 "nst_content",
1420 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001421 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001422 path = "$DESCRIPTOR"
1423 elif args:
1424 path = args
tiernob24258a2018-10-04 18:39:49 +02001425 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001426 path = ()
1427 else:
1428 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001429 file, _format = self.engine.get_file(
1430 engine_session,
1431 engine_topic,
1432 _id,
1433 path,
1434 cherrypy.request.headers.get("Accept"),
1435 )
tiernof27c79b2018-03-12 17:08:42 +01001436 outdata = file
1437 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001438 outdata = self.engine.get_item_list(
1439 engine_session, engine_topic, kwargs, api_req=True
1440 )
tiernoc94c3df2018-02-09 15:38:54 +01001441 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301442 if item == "reports":
1443 # TODO check that project_id (_id in this context) has permissions
1444 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301445 filter_q = None
1446 if "vcaStatusRefresh" in kwargs:
1447 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
1448 outdata = self.engine.get_item(engine_session, engine_topic, _id, filter_q, True)
delacruzramo271d2002019-12-02 21:00:37 +01001449
tiernof27c79b2018-03-12 17:08:42 +01001450 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001451 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001452 if topic in (
1453 "ns_descriptors_content",
1454 "vnf_packages_content",
1455 "netslice_templates_content",
1456 ):
tiernof27c79b2018-03-12 17:08:42 +01001457 _id = cherrypy.request.headers.get("Transaction-Id")
1458 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001459 _id, _ = self.engine.new_item(
1460 rollback,
1461 engine_session,
1462 engine_topic,
1463 {},
1464 None,
1465 cherrypy.request.headers,
1466 )
1467 completed = self.engine.upload_content(
1468 engine_session,
1469 engine_topic,
1470 _id,
1471 indata,
1472 kwargs,
1473 cherrypy.request.headers,
1474 )
tiernof27c79b2018-03-12 17:08:42 +01001475 if completed:
tiernob24258a2018-10-04 18:39:49 +02001476 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001477 else:
1478 cherrypy.response.headers["Transaction-Id"] = _id
1479 outdata = {"id": _id}
tiernob24258a2018-10-04 18:39:49 +02001480 elif topic == "ns_instances_content":
1481 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001482 _id, _ = self.engine.new_item(
1483 rollback, engine_session, engine_topic, indata, kwargs
1484 )
tiernob24258a2018-10-04 18:39:49 +02001485 # creates nslcmop
1486 indata["lcmOperationType"] = "instantiate"
1487 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001488 nslcmop_id, _ = self.engine.new_item(
1489 rollback, engine_session, "nslcmops", indata, None
1490 )
tiernob24258a2018-10-04 18:39:49 +02001491 self._set_location_header(main_topic, version, topic, _id)
kuuse078f55e2019-05-16 19:24:21 +02001492 outdata = {"id": _id, "nslcmop_id": nslcmop_id}
tiernob24258a2018-10-04 18:39:49 +02001493 elif topic == "ns_instances" and item:
1494 indata["lcmOperationType"] = item
1495 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001496 _id, _ = self.engine.new_item(
1497 rollback, engine_session, "nslcmops", indata, kwargs
1498 )
1499 self._set_location_header(
1500 main_topic, version, "ns_lcm_op_occs", _id
1501 )
tierno65acb4d2018-04-06 16:42:40 +02001502 outdata = {"id": _id}
1503 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001504 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001505 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001506 _id, _ = self.engine.new_item(
1507 rollback, engine_session, engine_topic, indata, kwargs
1508 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001509 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001510 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001511 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001512 nsilcmop_id, _ = self.engine.new_item(
1513 rollback, engine_session, "nsilcmops", indata, kwargs
1514 )
kuuse078f55e2019-05-16 19:24:21 +02001515 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001516 elif topic == "netslice_instances" and item:
1517 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001518 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001519 _id, _ = self.engine.new_item(
1520 rollback, engine_session, "nsilcmops", indata, kwargs
1521 )
1522 self._set_location_header(
1523 main_topic, version, "nsi_lcm_op_occs", _id
1524 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001525 outdata = {"id": _id}
1526 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001527 elif topic == "vnf_packages" and item == "action":
1528 indata["lcmOperationType"] = item
1529 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001530 _id, _ = self.engine.new_item(
1531 rollback, engine_session, "vnfpkgops", indata, kwargs
1532 )
1533 self._set_location_header(
1534 main_topic, version, "vnfpkg_op_occs", _id
1535 )
delacruzramo271d2002019-12-02 21:00:37 +01001536 outdata = {"id": _id}
1537 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301538 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001539 _id, _ = self.engine.new_item(
1540 rollback, engine_session, engine_topic, indata, kwargs
1541 )
preethika.p329b8182020-04-22 12:25:39 +05301542 self._set_location_header(main_topic, version, topic, _id)
1543 link = {}
1544 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001545 outdata = {
1546 "id": _id,
1547 "filter": indata["filter"],
1548 "callbackUri": indata["CallbackUri"],
1549 "_links": link,
1550 }
preethika.p329b8182020-04-22 12:25:39 +05301551 cherrypy.response.status = HTTPStatus.CREATED.value
almagiae47b9132022-05-17 14:12:22 +02001552 elif topic == "vnf_instances" and item:
1553 indata["lcmOperationType"] = item
1554 indata["vnfInstanceId"] = _id
1555 _id, _ = self.engine.new_item(rollback, engine_session, "vnflcmops", indata, kwargs)
1556 self._set_location_header(main_topic, version, "vnf_lcm_op_occs", _id)
1557 outdata = {"id": _id}
1558 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernof27c79b2018-03-12 17:08:42 +01001559 else:
garciadeblas4568a372021-03-24 09:19:48 +01001560 _id, op_id = self.engine.new_item(
1561 rollback,
1562 engine_session,
1563 engine_topic,
1564 indata,
1565 kwargs,
1566 cherrypy.request.headers,
1567 )
tiernob24258a2018-10-04 18:39:49 +02001568 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001569 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00001570 if op_id:
1571 outdata["op_id"] = op_id
1572 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001573 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02001574
tiernoc94c3df2018-02-09 15:38:54 +01001575 elif method == "DELETE":
1576 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001577 outdata = self.engine.del_item_list(
1578 engine_session, engine_topic, kwargs
1579 )
tierno09c073e2018-04-26 13:36:48 +02001580 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01001581 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00001582 # for NS NSI generate an operation
1583 op_id = None
tierno701018c2019-06-25 11:13:14 +00001584 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02001585 nslcmop_desc = {
1586 "lcmOperationType": "terminate",
1587 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001588 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02001589 }
garciadeblas4568a372021-03-24 09:19:48 +01001590 op_id, _ = self.engine.new_item(
1591 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
1592 )
tierno22577432020-04-08 15:16:57 +00001593 if op_id:
1594 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001595 elif (
1596 topic == "netslice_instances_content"
1597 and not engine_session["force"]
1598 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001599 nsilcmop_desc = {
1600 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02001601 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001602 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02001603 }
garciadeblas4568a372021-03-24 09:19:48 +01001604 op_id, _ = self.engine.new_item(
1605 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
1606 )
tierno22577432020-04-08 15:16:57 +00001607 if op_id:
1608 outdata = {"_id": op_id}
1609 # if there is not any deletion in process, delete
1610 if not op_id:
1611 op_id = self.engine.del_item(engine_session, engine_topic, _id)
1612 if op_id:
1613 outdata = {"op_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001614 cherrypy.response.status = (
1615 HTTPStatus.ACCEPTED.value
1616 if op_id
1617 else HTTPStatus.NO_CONTENT.value
1618 )
tierno09c073e2018-04-26 13:36:48 +02001619
tierno7ae10112018-05-18 14:36:02 +02001620 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00001621 op_id = None
tierno701018c2019-06-25 11:13:14 +00001622 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01001623 raise NbiException(
1624 "Nothing to update. Provide payload and/or query string",
1625 HTTPStatus.BAD_REQUEST,
1626 )
1627 if (
1628 item in ("nsd_content", "package_content", "nst_content")
1629 and method == "PUT"
1630 ):
1631 completed = self.engine.upload_content(
1632 engine_session,
1633 engine_topic,
1634 _id,
1635 indata,
1636 kwargs,
1637 cherrypy.request.headers,
1638 )
tiernof27c79b2018-03-12 17:08:42 +01001639 if not completed:
1640 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01001641 else:
garciadeblas4568a372021-03-24 09:19:48 +01001642 op_id = self.engine.edit_item(
1643 engine_session, engine_topic, _id, indata, kwargs
1644 )
tiernobdebce92019-07-01 15:36:49 +00001645
1646 if op_id:
1647 cherrypy.response.status = HTTPStatus.ACCEPTED.value
1648 outdata = {"op_id": op_id}
1649 else:
1650 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
1651 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01001652 else:
garciadeblas4568a372021-03-24 09:19:48 +01001653 raise NbiException(
1654 "Method {} not allowed".format(method),
1655 HTTPStatus.METHOD_NOT_ALLOWED,
1656 )
tiernoa6bb45d2019-06-14 09:45:39 +00001657
1658 # if Role information changes, it is needed to reload the information of roles
1659 if topic == "roles" and method != "GET":
1660 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01001661
garciadeblas4568a372021-03-24 09:19:48 +01001662 if (
1663 topic == "projects"
1664 and method == "DELETE"
1665 or topic in ["users", "roles"]
1666 and method in ["PUT", "PATCH", "DELETE"]
1667 ):
delacruzramoad682a52019-12-10 16:26:34 +01001668 self.authenticator.remove_token_from_cache()
1669
tierno701018c2019-06-25 11:13:14 +00001670 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02001671 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01001672 if isinstance(
1673 e,
1674 (
1675 NbiException,
1676 EngineException,
1677 DbException,
1678 FsException,
1679 MsgException,
1680 AuthException,
1681 ValidationError,
1682 AuthconnException,
1683 ),
1684 ):
tiernob24258a2018-10-04 18:39:49 +02001685 http_code_value = cherrypy.response.status = e.http_code.value
1686 http_code_name = e.http_code.name
1687 cherrypy.log("Exception {}".format(e))
1688 else:
garciadeblas4568a372021-03-24 09:19:48 +01001689 http_code_value = (
1690 cherrypy.response.status
1691 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01001692 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02001693 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02001694 if hasattr(outdata, "close"): # is an open file
1695 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02001696 error_text = str(e)
1697 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02001698 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02001699 try:
tiernocc103432018-10-19 14:10:35 +02001700 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01001701 self.engine.db.set_one(
1702 rollback_item["topic"],
1703 {"_id": rollback_item["_id"]},
1704 rollback_item["content"],
1705 fail_on_empty=False,
1706 )
preethika.p329b8182020-04-22 12:25:39 +05301707 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01001708 self.engine.db.del_list(
1709 rollback_item["topic"],
1710 rollback_item["filter"],
1711 fail_on_empty=False,
1712 )
tiernocc103432018-10-19 14:10:35 +02001713 else:
garciadeblas4568a372021-03-24 09:19:48 +01001714 self.engine.db.del_one(
1715 rollback_item["topic"],
1716 {"_id": rollback_item["_id"]},
1717 fail_on_empty=False,
1718 )
tierno3ace63c2018-05-03 17:51:43 +02001719 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01001720 rollback_error_text = "Rollback Exception {}: {}".format(
1721 rollback_item, e2
1722 )
tiernob24258a2018-10-04 18:39:49 +02001723 cherrypy.log(rollback_error_text)
1724 error_text += ". " + rollback_error_text
1725 # if isinstance(e, MsgException):
1726 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
1727 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01001728 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02001729 "code": http_code_name,
1730 "status": http_code_value,
1731 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01001732 }
tierno701018c2019-06-25 11:13:14 +00001733 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001734 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00001735 finally:
1736 if token_info:
1737 self._format_login(token_info)
1738 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
1739 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
1740 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01001741 cherrypy.request.login += ";{}={}".format(
1742 logging_id, outdata[logging_id][:36]
1743 )
tiernoc94c3df2018-02-09 15:38:54 +01001744
1745
tiernoc94c3df2018-02-09 15:38:54 +01001746def _start_service():
1747 """
1748 Callback function called when cherrypy.engine starts
1749 Override configuration with env variables
1750 Set database, storage, message configuration
1751 Init database with admin/admin user password
1752 """
tierno932499c2019-01-28 17:28:10 +00001753 global nbi_server
1754 global subscription_thread
tiernoc94c3df2018-02-09 15:38:54 +01001755 cherrypy.log.error("Starting osm_nbi")
1756 # update general cherrypy configuration
1757 update_dict = {}
1758
garciadeblas4568a372021-03-24 09:19:48 +01001759 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01001760 for k, v in environ.items():
1761 if not k.startswith("OSMNBI_"):
1762 continue
tiernoe1281182018-05-22 12:24:36 +02001763 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01001764 if not k2:
1765 continue
1766 try:
1767 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01001768 if k == "OSMNBI_STATIC_DIR":
1769 engine_config["/static"]["tools.staticdir.dir"] = v
1770 engine_config["/static"]["tools.staticdir.on"] = True
1771 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
1772 update_dict["server.socket_port"] = int(v)
1773 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
1774 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02001775 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01001776 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001777 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02001778 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001779 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01001780 engine_config[k1][k2] = int(v)
1781 else:
1782 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001783
tiernoc94c3df2018-02-09 15:38:54 +01001784 except ValueError as e:
1785 cherrypy.log.error("Ignoring environ '{}': " + str(e))
1786 except Exception as e:
1787 cherrypy.log.warn("skipping environ '{}' on exception '{}'".format(k, e))
1788
1789 if update_dict:
1790 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02001791 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001792
1793 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01001794 log_format_simple = (
1795 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
1796 )
1797 log_formatter_simple = logging.Formatter(
1798 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
1799 )
tiernoc94c3df2018-02-09 15:38:54 +01001800 logger_server = logging.getLogger("cherrypy.error")
1801 logger_access = logging.getLogger("cherrypy.access")
1802 logger_cherry = logging.getLogger("cherrypy")
1803 logger_nbi = logging.getLogger("nbi")
1804
tiernof5298be2018-05-16 14:43:57 +02001805 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01001806 file_handler = logging.handlers.RotatingFileHandler(
1807 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
1808 )
tiernoc94c3df2018-02-09 15:38:54 +01001809 file_handler.setFormatter(log_formatter_simple)
1810 logger_cherry.addHandler(file_handler)
1811 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02001812 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01001813 for format_, logger in {
1814 "nbi.server %(filename)s:%(lineno)s": logger_server,
1815 "nbi.access %(filename)s:%(lineno)s": logger_access,
1816 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
1817 }.items():
tiernob24258a2018-10-04 18:39:49 +02001818 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01001819 log_formatter_cherry = logging.Formatter(
1820 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
1821 )
tiernob24258a2018-10-04 18:39:49 +02001822 str_handler = logging.StreamHandler()
1823 str_handler.setFormatter(log_formatter_cherry)
1824 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01001825
tiernof5298be2018-05-16 14:43:57 +02001826 if engine_config["global"].get("log.level"):
1827 logger_cherry.setLevel(engine_config["global"]["log.level"])
1828 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01001829
1830 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01001831 for k1, logname in {
1832 "message": "nbi.msg",
1833 "database": "nbi.db",
1834 "storage": "nbi.fs",
1835 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01001836 engine_config[k1]["logger_name"] = logname
1837 logger_module = logging.getLogger(logname)
1838 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01001839 file_handler = logging.handlers.RotatingFileHandler(
1840 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
1841 )
tiernoc94c3df2018-02-09 15:38:54 +01001842 file_handler.setFormatter(log_formatter_simple)
1843 logger_module.addHandler(file_handler)
1844 if "loglevel" in engine_config[k1]:
1845 logger_module.setLevel(engine_config[k1]["loglevel"])
1846 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01001847 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
1848 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
1849 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
1850 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
1851 target_version=auth_database_version
1852 )
tiernobee508e2019-01-21 11:21:49 +00001853
tierno932499c2019-01-28 17:28:10 +00001854 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01001855 subscription_thread = SubscriptionThread(
1856 config=engine_config, engine=nbi_server.engine
1857 )
tierno932499c2019-01-28 17:28:10 +00001858 subscription_thread.start()
1859 # Do not capture except SubscriptionException
1860
tiernob2e48bd2020-02-04 15:47:18 +00001861 backend = engine_config["authentication"]["backend"]
garciadeblas4568a372021-03-24 09:19:48 +01001862 cherrypy.log.error(
1863 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
1864 nbi_version, nbi_version_date, backend
1865 )
1866 )
tiernoc94c3df2018-02-09 15:38:54 +01001867
1868
1869def _stop_service():
1870 """
1871 Callback function called when cherrypy.engine stops
1872 TODO: Ending database connections.
1873 """
tierno932499c2019-01-28 17:28:10 +00001874 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01001875 if subscription_thread:
1876 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00001877 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01001878 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01001879 cherrypy.log.error("Stopping osm_nbi")
1880
tierno2236d202018-05-16 19:05:16 +02001881
tiernof5298be2018-05-16 14:43:57 +02001882def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00001883 global nbi_server
tiernoc94c3df2018-02-09 15:38:54 +01001884 # conf = {
1885 # '/': {
1886 # #'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
1887 # 'tools.sessions.on': True,
1888 # 'tools.response_headers.on': True,
1889 # # 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
1890 # }
1891 # }
1892 # cherrypy.Server.ssl_module = 'builtin'
1893 # cherrypy.Server.ssl_certificate = "http/cert.pem"
1894 # cherrypy.Server.ssl_private_key = "http/privkey.pem"
1895 # cherrypy.Server.thread_pool = 10
1896 # cherrypy.config.update({'Server.socket_port': config["port"], 'Server.socket_host': config["host"]})
1897
1898 # cherrypy.config.update({'tools.auth_basic.on': True,
1899 # 'tools.auth_basic.realm': 'localhost',
1900 # 'tools.auth_basic.checkpassword': validate_password})
tierno932499c2019-01-28 17:28:10 +00001901 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01001902 cherrypy.engine.subscribe("start", _start_service)
1903 cherrypy.engine.subscribe("stop", _stop_service)
1904 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02001905
1906
1907def usage():
garciadeblas4568a372021-03-24 09:19:48 +01001908 print(
1909 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02001910 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
1911 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01001912 """.format(
1913 sys.argv[0]
1914 )
1915 )
tierno2236d202018-05-16 19:05:16 +02001916 # --log-socket-host HOST: send logs to this host")
1917 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01001918
1919
garciadeblas4568a372021-03-24 09:19:48 +01001920if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02001921 try:
1922 # load parameters and configuration
1923 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
1924 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
1925 config_file = None
1926 for o, a in opts:
1927 if o in ("-h", "--help"):
1928 usage()
1929 sys.exit()
1930 elif o in ("-c", "--config"):
1931 config_file = a
1932 # elif o == "--log-socket-port":
1933 # log_socket_port = a
1934 # elif o == "--log-socket-host":
1935 # log_socket_host = a
1936 # elif o == "--log-file":
1937 # log_file = a
1938 else:
1939 assert False, "Unhandled option"
1940 if config_file:
1941 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01001942 print(
1943 "configuration file '{}' that not exist".format(config_file),
1944 file=sys.stderr,
1945 )
tiernof5298be2018-05-16 14:43:57 +02001946 exit(1)
1947 else:
garciadeblas4568a372021-03-24 09:19:48 +01001948 for config_file in (
1949 __file__[: __file__.rfind(".")] + ".cfg",
1950 "./nbi.cfg",
1951 "/etc/osm/nbi.cfg",
1952 ):
tiernof5298be2018-05-16 14:43:57 +02001953 if path.isfile(config_file):
1954 break
1955 else:
garciadeblas4568a372021-03-24 09:19:48 +01001956 print(
1957 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
1958 file=sys.stderr,
1959 )
tiernof5298be2018-05-16 14:43:57 +02001960 exit(1)
1961 nbi(config_file)
1962 except getopt.GetoptError as e:
1963 print(str(e), file=sys.stderr)
1964 # usage()
1965 exit(1)