blob: 29fcacd2a8a5a58b93869dbe41d1a086c4bb4f33 [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
88 heal 5
aticig544a2ae2022-04-05 09:00:17 +030089 update 05
tiernoc94c3df2018-02-09 15:38:54 +010090 /ns_lcm_op_occs 5 5
91 /<nsLcmOpOccId> 5 5 5
92 TO BE COMPLETED 5 5
tiernof759d822018-06-11 18:54:54 +020093 /vnf_instances (also vnfrs for compatibility) O
94 /<vnfInstanceId> O
tiernof27c79b2018-03-12 17:08:42 +010095 /subscriptions 5 5
96 /<subscriptionId> 5 X
garciadeblas9750c5a2018-10-15 16:20:35 +020097
tiernocb83c942018-09-24 17:28:13 +020098 /pdu/v1
tierno032916c2019-03-22 13:27:12 +000099 /pdu_descriptors O O
tiernocb83c942018-09-24 17:28:13 +0200100 /<id> O O O O
garciadeblas9750c5a2018-10-15 16:20:35 +0200101
tiernof27c79b2018-03-12 17:08:42 +0100102 /admin/v1
103 /tokens O O
tierno2236d202018-05-16 19:05:16 +0200104 /<id> O O
tiernof27c79b2018-03-12 17:08:42 +0100105 /users O O
tiernocd54a4a2018-09-12 16:40:35 +0200106 /<id> O O O O
tiernof27c79b2018-03-12 17:08:42 +0100107 /projects O O
tierno2236d202018-05-16 19:05:16 +0200108 /<id> O O
tierno55ba2e62018-12-11 17:22:22 +0000109 /vim_accounts (also vims for compatibility) O O
110 /<id> O O O
111 /wim_accounts O O
tierno2236d202018-05-16 19:05:16 +0200112 /<id> O O O
tierno0f98af52018-03-19 10:28:22 +0100113 /sdns O O
tierno2236d202018-05-16 19:05:16 +0200114 /<id> O O O
delacruzramofe598fe2019-10-23 18:25:11 +0200115 /k8sclusters O O
116 /<id> O O O
117 /k8srepos O O
118 /<id> O O
Felipe Vicensb66b0412020-05-06 10:11:00 +0200119 /osmrepos O O
120 /<id> O O
tiernoc94c3df2018-02-09 15:38:54 +0100121
garciadeblas9750c5a2018-10-15 16:20:35 +0200122 /nst/v1 O O
123 /netslice_templates_content O O
124 /<nstInfoId> O O O O
125 /netslice_templates O O
126 /<nstInfoId> O O O
127 /nst_content O O
128 /nst O
129 /artifacts[/<artifactPath>] O
130 /subscriptions X X
131 /<subscriptionId> X X
132
133 /nsilcm/v1
134 /netslice_instances_content O O
135 /<SliceInstanceId> O O
136 /netslice_instances O O
137 /<SliceInstanceId> O O
138 instantiate O
139 terminate O
140 action O
141 /nsi_lcm_op_occs O O
142 /<nsiLcmOpOccId> O O O
143 /subscriptions X X
144 /<subscriptionId> X X
145
tierno2236d202018-05-16 19:05:16 +0200146query string:
147 Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force.
tierno36ec8602018-11-02 17:27:11 +0100148 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
149 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
150 op := "eq" | "neq" (or "ne") | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
151 attrName := string
tierno2236d202018-05-16 19:05:16 +0200152 For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any
153 item of the array, that is, pass if any item of the array pass the filter.
154 It allows both ne and neq for not equal
155 TODO: 4.3.3 Attribute selectors
156 all_fields, fields=x,y,.., exclude_default, exclude_fields=x,y,...
tiernoc94c3df2018-02-09 15:38:54 +0100157 (none) … same as “exclude_default”
158 all_fields … all attributes.
tierno2236d202018-05-16 19:05:16 +0200159 fields=<list> … all attributes except all complex attributes with minimum cardinality of zero that are not
160 conditionally mandatory, and that are not provided in <list>.
161 exclude_fields=<list> … all attributes except those complex attributes with a minimum cardinality of zero that
162 are not conditionally mandatory, and that are provided in <list>.
163 exclude_default … all attributes except those complex attributes with a minimum cardinality of zero that are not
164 conditionally mandatory, and that are part of the "default exclude set" defined in the present specification for
165 the particular resource
166 exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality
167 of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the
168 present specification for the particular resource, but that are not part of <list>
tierno65ca36d2019-02-12 19:27:52 +0100169 Additionally it admits some administrator values:
170 FORCE: To force operations skipping dependency checkings
171 ADMIN: To act as an administrator or a different project
172 PUBLIC: To get public descriptors or set a descriptor as public
173 SET_PROJECT: To make a descriptor available for other project
174
tiernoc94c3df2018-02-09 15:38:54 +0100175Header field name Reference Example Descriptions
176 Accept IETF RFC 7231 [19] application/json Content-Types that are acceptable for the response.
177 This header field shall be present if the response is expected to have a non-empty message body.
178 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the request.
179 This header field shall be present if the request has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200180 Authorization IETF RFC 7235 [22] Bearer mF_9.B5f-4.1JqM The authorization token for the request.
181 Details are specified in clause 4.5.3.
tiernoc94c3df2018-02-09 15:38:54 +0100182 Range IETF RFC 7233 [21] 1000-2000 Requested range of bytes from a file
183Header field name Reference Example Descriptions
184 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the response.
185 This header field shall be present if the response has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200186 Location IETF RFC 7231 [19] http://www.example.com/vnflcm/v1/vnf_instances/123 Used in redirection, or when a
187 new resource has been created.
tiernoc94c3df2018-02-09 15:38:54 +0100188 This header field shall be present if the response status code is 201 or 3xx.
tierno2236d202018-05-16 19:05:16 +0200189 In the present document this header field is also used if the response status code is 202 and a new resource was
190 created.
191 WWW-Authenticate IETF RFC 7235 [22] Bearer realm="example" Challenge if the corresponding HTTP request has not
192 provided authorization, or error details if the corresponding HTTP request has provided an invalid authorization
193 token.
194 Accept-Ranges IETF RFC 7233 [21] bytes Used by the Server to signal whether or not it supports ranges for
195 certain resources.
196 Content-Range IETF RFC 7233 [21] bytes 21010-47021/ 47022 Signals the byte range that is contained in the
197 response, and the total length of the file.
tiernoc94c3df2018-02-09 15:38:54 +0100198 Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT
tiernoc94c3df2018-02-09 15:38:54 +0100199"""
200
tierno701018c2019-06-25 11:13:14 +0000201valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC")
202# ^ Contains possible administrative query string words:
203# ADMIN=True(by default)|Project|Project-list: See all elements, or elements of a project
204# (not owned by my session project).
205# PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public
206# FORCE=True(by default)|False: Force edition/deletion operations
207# SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio
208
209valid_url_methods = {
210 # contains allowed URL and methods, and the role_permission name
211 "admin": {
212 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100213 "tokens": {
214 "METHODS": ("GET", "POST", "DELETE"),
215 "ROLE_PERMISSION": "tokens:",
216 "<ID>": {"METHODS": ("GET", "DELETE"), "ROLE_PERMISSION": "tokens:id:"},
217 },
218 "users": {
219 "METHODS": ("GET", "POST"),
220 "ROLE_PERMISSION": "users:",
221 "<ID>": {
222 "METHODS": ("GET", "DELETE", "PATCH"),
223 "ROLE_PERMISSION": "users:id:",
224 },
225 },
226 "projects": {
227 "METHODS": ("GET", "POST"),
228 "ROLE_PERMISSION": "projects:",
229 "<ID>": {
230 "METHODS": ("GET", "DELETE", "PATCH"),
231 "ROLE_PERMISSION": "projects:id:",
232 },
233 },
234 "roles": {
235 "METHODS": ("GET", "POST"),
236 "ROLE_PERMISSION": "roles:",
237 "<ID>": {
238 "METHODS": ("GET", "DELETE", "PATCH"),
239 "ROLE_PERMISSION": "roles:id:",
240 },
241 },
242 "vims": {
243 "METHODS": ("GET", "POST"),
244 "ROLE_PERMISSION": "vims:",
245 "<ID>": {
246 "METHODS": ("GET", "DELETE", "PATCH"),
247 "ROLE_PERMISSION": "vims:id:",
248 },
249 },
250 "vim_accounts": {
251 "METHODS": ("GET", "POST"),
252 "ROLE_PERMISSION": "vim_accounts:",
253 "<ID>": {
254 "METHODS": ("GET", "DELETE", "PATCH"),
255 "ROLE_PERMISSION": "vim_accounts:id:",
256 },
257 },
258 "wim_accounts": {
259 "METHODS": ("GET", "POST"),
260 "ROLE_PERMISSION": "wim_accounts:",
261 "<ID>": {
262 "METHODS": ("GET", "DELETE", "PATCH"),
263 "ROLE_PERMISSION": "wim_accounts:id:",
264 },
265 },
266 "sdns": {
267 "METHODS": ("GET", "POST"),
268 "ROLE_PERMISSION": "sdn_controllers:",
269 "<ID>": {
270 "METHODS": ("GET", "DELETE", "PATCH"),
271 "ROLE_PERMISSION": "sdn_controllers:id:",
272 },
273 },
274 "k8sclusters": {
275 "METHODS": ("GET", "POST"),
276 "ROLE_PERMISSION": "k8sclusters:",
277 "<ID>": {
278 "METHODS": ("GET", "DELETE", "PATCH"),
279 "ROLE_PERMISSION": "k8sclusters:id:",
280 },
281 },
282 "vca": {
283 "METHODS": ("GET", "POST"),
284 "ROLE_PERMISSION": "vca:",
285 "<ID>": {
286 "METHODS": ("GET", "DELETE", "PATCH"),
287 "ROLE_PERMISSION": "vca:id:",
288 },
289 },
290 "k8srepos": {
291 "METHODS": ("GET", "POST"),
292 "ROLE_PERMISSION": "k8srepos:",
293 "<ID>": {
294 "METHODS": ("GET", "DELETE"),
295 "ROLE_PERMISSION": "k8srepos:id:",
296 },
297 },
298 "osmrepos": {
299 "METHODS": ("GET", "POST"),
300 "ROLE_PERMISSION": "osmrepos:",
301 "<ID>": {
302 "METHODS": ("GET", "DELETE", "PATCH"),
303 "ROLE_PERMISSION": "osmrepos:id:",
304 },
305 },
306 "domains": {
307 "METHODS": ("GET",),
308 "ROLE_PERMISSION": "domains:",
309 },
tierno701018c2019-06-25 11:13:14 +0000310 }
311 },
312 "pdu": {
313 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100314 "pdu_descriptors": {
315 "METHODS": ("GET", "POST"),
316 "ROLE_PERMISSION": "pduds:",
317 "<ID>": {
318 "METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"),
319 "ROLE_PERMISSION": "pduds:id:",
320 },
321 },
tierno701018c2019-06-25 11:13:14 +0000322 }
323 },
324 "nsd": {
325 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100326 "ns_descriptors_content": {
327 "METHODS": ("GET", "POST"),
328 "ROLE_PERMISSION": "nsds:",
329 "<ID>": {
330 "METHODS": ("GET", "PUT", "DELETE"),
331 "ROLE_PERMISSION": "nsds:id:",
332 },
333 },
334 "ns_descriptors": {
335 "METHODS": ("GET", "POST"),
336 "ROLE_PERMISSION": "nsds:",
337 "<ID>": {
338 "METHODS": ("GET", "DELETE", "PATCH"),
339 "ROLE_PERMISSION": "nsds:id:",
340 "nsd_content": {
341 "METHODS": ("GET", "PUT"),
342 "ROLE_PERMISSION": "nsds:id:content:",
343 },
344 "nsd": {
345 "METHODS": ("GET",), # descriptor inside package
346 "ROLE_PERMISSION": "nsds:id:content:",
347 },
348 "artifacts": {
349 "METHODS": ("GET",),
350 "ROLE_PERMISSION": "nsds:id:nsd_artifact:",
351 "*": None,
352 },
353 },
354 },
355 "pnf_descriptors": {
356 "TODO": ("GET", "POST"),
357 "<ID>": {
358 "TODO": ("GET", "DELETE", "PATCH"),
359 "pnfd_content": {"TODO": ("GET", "PUT")},
360 },
361 },
362 "subscriptions": {
363 "TODO": ("GET", "POST"),
364 "<ID>": {"TODO": ("GET", "DELETE")},
365 },
tierno701018c2019-06-25 11:13:14 +0000366 }
367 },
368 "vnfpkgm": {
369 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100370 "vnf_packages_content": {
371 "METHODS": ("GET", "POST"),
372 "ROLE_PERMISSION": "vnfds:",
373 "<ID>": {
374 "METHODS": ("GET", "PUT", "DELETE"),
375 "ROLE_PERMISSION": "vnfds:id:",
376 },
377 },
378 "vnf_packages": {
379 "METHODS": ("GET", "POST"),
380 "ROLE_PERMISSION": "vnfds:",
381 "<ID>": {
382 "METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo
383 "ROLE_PERMISSION": "vnfds:id:",
384 "package_content": {
385 "METHODS": ("GET", "PUT"), # package
386 "ROLE_PERMISSION": "vnfds:id:",
387 "upload_from_uri": {
388 "METHODS": (),
389 "TODO": ("POST",),
390 "ROLE_PERMISSION": "vnfds:id:upload:",
391 },
392 },
393 "vnfd": {
394 "METHODS": ("GET",), # descriptor inside package
395 "ROLE_PERMISSION": "vnfds:id:content:",
396 },
397 "artifacts": {
398 "METHODS": ("GET",),
399 "ROLE_PERMISSION": "vnfds:id:vnfd_artifact:",
400 "*": None,
401 },
402 "action": {
403 "METHODS": ("POST",),
404 "ROLE_PERMISSION": "vnfds:id:action:",
405 },
406 },
407 },
408 "subscriptions": {
409 "TODO": ("GET", "POST"),
410 "<ID>": {"TODO": ("GET", "DELETE")},
411 },
412 "vnfpkg_op_occs": {
413 "METHODS": ("GET",),
414 "ROLE_PERMISSION": "vnfds:vnfpkgops:",
415 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnfds:vnfpkgops:id:"},
416 },
tierno701018c2019-06-25 11:13:14 +0000417 }
418 },
419 "nslcm": {
420 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100421 "ns_instances_content": {
422 "METHODS": ("GET", "POST"),
423 "ROLE_PERMISSION": "ns_instances:",
424 "<ID>": {
425 "METHODS": ("GET", "DELETE"),
426 "ROLE_PERMISSION": "ns_instances:id:",
427 },
428 },
429 "ns_instances": {
430 "METHODS": ("GET", "POST"),
431 "ROLE_PERMISSION": "ns_instances:",
432 "<ID>": {
433 "METHODS": ("GET", "DELETE"),
434 "ROLE_PERMISSION": "ns_instances:id:",
435 "scale": {
436 "METHODS": ("POST",),
437 "ROLE_PERMISSION": "ns_instances:id:scale:",
438 },
439 "terminate": {
440 "METHODS": ("POST",),
441 "ROLE_PERMISSION": "ns_instances:id:terminate:",
442 },
443 "instantiate": {
444 "METHODS": ("POST",),
445 "ROLE_PERMISSION": "ns_instances:id:instantiate:",
446 },
447 "action": {
448 "METHODS": ("POST",),
449 "ROLE_PERMISSION": "ns_instances:id:action:",
450 },
aticig544a2ae2022-04-05 09:00:17 +0300451 "update": {
452 "METHODS": ("POST",),
453 "ROLE_PERMISSION": "ns_instances:id:update:",
454 },
garciadeblas4568a372021-03-24 09:19:48 +0100455 },
456 },
457 "ns_lcm_op_occs": {
458 "METHODS": ("GET",),
459 "ROLE_PERMISSION": "ns_instances:opps:",
460 "<ID>": {
461 "METHODS": ("GET",),
462 "ROLE_PERMISSION": "ns_instances:opps:id:",
463 },
464 },
465 "vnfrs": {
466 "METHODS": ("GET",),
467 "ROLE_PERMISSION": "vnf_instances:",
468 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
469 },
470 "vnf_instances": {
471 "METHODS": ("GET",),
472 "ROLE_PERMISSION": "vnf_instances:",
473 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
474 },
475 "subscriptions": {
476 "METHODS": ("GET", "POST"),
477 "ROLE_PERMISSION": "ns_subscriptions:",
478 "<ID>": {
479 "METHODS": ("GET", "DELETE"),
480 "ROLE_PERMISSION": "ns_subscriptions:id:",
481 },
482 },
tierno701018c2019-06-25 11:13:14 +0000483 }
484 },
485 "nst": {
486 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100487 "netslice_templates_content": {
488 "METHODS": ("GET", "POST"),
489 "ROLE_PERMISSION": "slice_templates:",
490 "<ID>": {
491 "METHODS": ("GET", "PUT", "DELETE"),
492 "ROLE_PERMISSION": "slice_templates:id:",
493 },
494 },
495 "netslice_templates": {
496 "METHODS": ("GET", "POST"),
497 "ROLE_PERMISSION": "slice_templates:",
498 "<ID>": {
499 "METHODS": ("GET", "DELETE"),
500 "TODO": ("PATCH",),
501 "ROLE_PERMISSION": "slice_templates:id:",
502 "nst_content": {
503 "METHODS": ("GET", "PUT"),
504 "ROLE_PERMISSION": "slice_templates:id:content:",
505 },
506 "nst": {
507 "METHODS": ("GET",), # descriptor inside package
508 "ROLE_PERMISSION": "slice_templates:id:content:",
509 },
510 "artifacts": {
511 "METHODS": ("GET",),
512 "ROLE_PERMISSION": "slice_templates:id:content:",
513 "*": None,
514 },
515 },
516 },
517 "subscriptions": {
518 "TODO": ("GET", "POST"),
519 "<ID>": {"TODO": ("GET", "DELETE")},
520 },
tierno701018c2019-06-25 11:13:14 +0000521 }
522 },
523 "nsilcm": {
524 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100525 "netslice_instances_content": {
526 "METHODS": ("GET", "POST"),
527 "ROLE_PERMISSION": "slice_instances:",
528 "<ID>": {
529 "METHODS": ("GET", "DELETE"),
530 "ROLE_PERMISSION": "slice_instances:id:",
531 },
532 },
533 "netslice_instances": {
534 "METHODS": ("GET", "POST"),
535 "ROLE_PERMISSION": "slice_instances:",
536 "<ID>": {
537 "METHODS": ("GET", "DELETE"),
538 "ROLE_PERMISSION": "slice_instances:id:",
539 "terminate": {
540 "METHODS": ("POST",),
541 "ROLE_PERMISSION": "slice_instances:id:terminate:",
542 },
543 "instantiate": {
544 "METHODS": ("POST",),
545 "ROLE_PERMISSION": "slice_instances:id:instantiate:",
546 },
547 "action": {
548 "METHODS": ("POST",),
549 "ROLE_PERMISSION": "slice_instances:id:action:",
550 },
551 },
552 },
553 "nsi_lcm_op_occs": {
554 "METHODS": ("GET",),
555 "ROLE_PERMISSION": "slice_instances:opps:",
556 "<ID>": {
557 "METHODS": ("GET",),
558 "ROLE_PERMISSION": "slice_instances:opps:id:",
559 },
560 },
tierno701018c2019-06-25 11:13:14 +0000561 }
562 },
563 "nspm": {
564 "v1": {
565 "pm_jobs": {
566 "<ID>": {
567 "reports": {
garciadeblas4568a372021-03-24 09:19:48 +0100568 "<ID>": {
569 "METHODS": ("GET",),
570 "ROLE_PERMISSION": "reports:id:",
571 }
tierno701018c2019-06-25 11:13:14 +0000572 }
573 },
574 },
575 },
576 },
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000577 "nsfm": {
578 "v1": {
579 "alarms": {"METHODS": ("GET", "PATCH"),
580 "ROLE_PERMISSION": "alarms:",
581 "<ID>": {"METHODS": ("GET", "PATCH"),
582 "ROLE_PERMISSION": "alarms:id:",
583 },
584 }
585 },
586 },
tierno701018c2019-06-25 11:13:14 +0000587}
588
tiernoc94c3df2018-02-09 15:38:54 +0100589
590class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100591 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
592 Exception.__init__(self, message)
593 self.http_code = http_code
594
595
596class Server(object):
597 instance = 0
598 # to decode bytes to str
599 reader = getreader("utf-8")
600
601 def __init__(self):
602 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000603 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100604 self.engine = Engine(self.authenticator)
tiernoc94c3df2018-02-09 15:38:54 +0100605
tiernoc94c3df2018-02-09 15:38:54 +0100606 def _format_in(self, kwargs):
607 try:
608 indata = None
609 if cherrypy.request.body.length:
610 error_text = "Invalid input format "
611
612 if "Content-Type" in cherrypy.request.headers:
613 if "application/json" in cherrypy.request.headers["Content-Type"]:
614 error_text = "Invalid json format "
615 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100616 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100617 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
618 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100619 indata = yaml.load(
620 cherrypy.request.body, Loader=yaml.SafeLoader
621 )
gcalvinode4adfe2018-10-30 11:46:09 +0100622 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100623 elif (
624 "application/binary" in cherrypy.request.headers["Content-Type"]
625 or "application/gzip"
626 in cherrypy.request.headers["Content-Type"]
627 or "application/zip" in cherrypy.request.headers["Content-Type"]
628 or "text/plain" in cherrypy.request.headers["Content-Type"]
629 ):
tiernof27c79b2018-03-12 17:08:42 +0100630 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100631 elif (
632 "multipart/form-data"
633 in cherrypy.request.headers["Content-Type"]
634 ):
tiernoc94c3df2018-02-09 15:38:54 +0100635 if "descriptor_file" in kwargs:
636 filecontent = kwargs.pop("descriptor_file")
637 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100638 raise NbiException(
639 "empty file or content", HTTPStatus.BAD_REQUEST
640 )
tiernof27c79b2018-03-12 17:08:42 +0100641 indata = filecontent.file # .read()
tiernoc94c3df2018-02-09 15:38:54 +0100642 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100643 cherrypy.request.headers[
644 "Content-Type"
645 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100646 else:
647 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
648 # "Only 'Content-Type' of type 'application/json' or
649 # 'application/yaml' for input format are available")
650 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100651 indata = yaml.load(
652 cherrypy.request.body, Loader=yaml.SafeLoader
653 )
gcalvinode4adfe2018-10-30 11:46:09 +0100654 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100655 else:
656 error_text = "Invalid yaml format "
delacruzramob19cadc2019-10-08 10:18:02 +0200657 indata = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
gcalvinode4adfe2018-10-30 11:46:09 +0100658 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100659 if not indata:
660 indata = {}
661
tiernoc94c3df2018-02-09 15:38:54 +0100662 format_yaml = False
663 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
664 format_yaml = True
665
666 for k, v in kwargs.items():
667 if isinstance(v, str):
668 if v == "":
669 kwargs[k] = None
670 elif format_yaml:
671 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200672 kwargs[k] = yaml.load(v, Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200673 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100674 pass
garciadeblas4568a372021-03-24 09:19:48 +0100675 elif (
676 k.endswith(".gt")
677 or k.endswith(".lt")
678 or k.endswith(".gte")
679 or k.endswith(".lte")
680 ):
tiernoc94c3df2018-02-09 15:38:54 +0100681 try:
682 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200683 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100684 try:
685 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200686 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100687 pass
688 elif v.find(",") > 0:
689 kwargs[k] = v.split(",")
690 elif isinstance(v, (list, tuple)):
691 for index in range(0, len(v)):
692 if v[index] == "":
693 v[index] = None
694 elif format_yaml:
695 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200696 v[index] = yaml.load(v[index], Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200697 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100698 pass
699
tiernof27c79b2018-03-12 17:08:42 +0100700 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100701 except (ValueError, yaml.YAMLError) as exc:
702 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
703 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100704 raise NbiException(
705 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
706 )
tiernob92094f2018-05-11 13:44:22 +0200707 except Exception as exc:
708 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100709
710 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000711 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100712 """
713 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100714 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000715 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000716 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100717 :return: None
718 """
tierno0f98af52018-03-19 10:28:22 +0100719 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100720 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100721 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100722 return html.format(
723 data, cherrypy.request, cherrypy.response, token_info
724 )
tierno09c073e2018-04-26 13:36:48 +0200725 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100726 return
727 elif hasattr(data, "read"): # file object
728 if _format:
729 cherrypy.response.headers["Content-Type"] = _format
730 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100731 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100732 else:
garciadeblas4568a372021-03-24 09:19:48 +0100733 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100734 # TODO check that cherrypy close file. If not implement pending things to close per thread next
735 return data
tierno0f98af52018-03-19 10:28:22 +0100736 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000737 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100738 return html.format(
739 data, cherrypy.request, cherrypy.response, token_info
740 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000741 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100742 pass
garciadeblas4568a372021-03-24 09:19:48 +0100743 elif "application/json" in accept or (
744 cherrypy.response.status and cherrypy.response.status >= 300
745 ):
746 cherrypy.response.headers[
747 "Content-Type"
748 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000749 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100750 return a.encode("utf8")
751 cherrypy.response.headers["Content-Type"] = "application/yaml"
752 return yaml.safe_dump(
753 data,
754 explicit_start=True,
755 indent=4,
756 default_flow_style=False,
757 tags=False,
758 encoding="utf-8",
759 allow_unicode=True,
760 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +0100761
762 @cherrypy.expose
763 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +0000764 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100765 try:
766 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +0000767 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +0100768 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +0100769 else:
garciadeblas4568a372021-03-24 09:19:48 +0100770 raise cherrypy.HTTPError(
771 HTTPStatus.METHOD_NOT_ALLOWED.value,
772 "Method {} not allowed for tokens".format(cherrypy.request.method),
773 )
tiernoc94c3df2018-02-09 15:38:54 +0100774
tierno701018c2019-06-25 11:13:14 +0000775 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100776
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100777 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +0000778 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +0100779 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +0000780 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100781
782 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +0200783 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +0200784 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +0200785 try:
786 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +0100787 raise NbiException(
788 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
789 )
tierno55945e72018-04-06 16:40:27 +0200790 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +0100791 raise NbiException(
792 "Invalid URL or query string for version",
793 HTTPStatus.METHOD_NOT_ALLOWED,
794 )
tierno9c630112019-08-29 14:21:41 +0000795 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +0000796 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
797 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +0200798 except NbiException as e:
799 cherrypy.response.status = e.http_code.value
800 problem_details = {
801 "code": e.http_code.name,
802 "status": e.http_code.value,
803 "detail": str(e),
804 }
805 return self._format_out(problem_details, None)
806
tierno12eac3c2020-03-19 23:22:08 +0000807 def domain(self):
808 try:
809 domains = {
garciadeblas4568a372021-03-24 09:19:48 +0100810 "user_domain_name": cherrypy.tree.apps["/osm"]
811 .config["authentication"]
812 .get("user_domain_name"),
813 "project_domain_name": cherrypy.tree.apps["/osm"]
814 .config["authentication"]
815 .get("project_domain_name"),
816 }
tierno12eac3c2020-03-19 23:22:08 +0000817 return self._format_out(domains)
818 except NbiException as e:
819 cherrypy.response.status = e.http_code.value
820 problem_details = {
821 "code": e.http_code.name,
822 "status": e.http_code.value,
823 "detail": str(e),
824 }
825 return self._format_out(problem_details, None)
826
tiernoa5035702019-07-29 08:54:42 +0000827 @staticmethod
828 def _format_login(token_info):
829 """
830 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
831 log this information
832 :param token_info: Dictionary with token content
833 :return: None
834 """
835 cherrypy.request.login = token_info.get("username", "-")
836 if token_info.get("project_name"):
837 cherrypy.request.login += "/" + token_info["project_name"]
838 if token_info.get("id"):
839 cherrypy.request.login += ";session=" + token_info["id"][0:12]
840
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000841 # NS Fault Management
842 @cherrypy.expose
843 def nsfm(self, version=None, topic=None, uuid=None, project_name=None, ns_id=None, *args, **kwargs):
844 if topic == 'alarms':
845 try:
846 method = cherrypy.request.method
847 role_permission = self._check_valid_url_method(method, "nsfm", version, topic, None, None, *args)
848 query_string_operations = self._extract_query_string_operations(kwargs, method)
849
850 self.authenticator.authorize(role_permission, query_string_operations, None)
851
852 # to handle get request
853 if cherrypy.request.method == 'GET':
854 # if request is on basis of uuid
855 if uuid and uuid != 'None':
856 try:
857 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
858 alarm_action = self.engine.db.get_one("alarms_action", {"uuid": uuid})
859 alarm.update(alarm_action)
860 vnf = self.engine.db.get_one("vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]})
861 alarm["vnf-id"] = vnf["_id"]
862 return self._format_out(str(alarm))
863 except Exception:
864 return self._format_out("Please provide valid alarm uuid")
865 elif ns_id and ns_id != 'None':
866 # if request is on basis of ns_id
867 try:
868 alarms = self.engine.db.get_list("alarms", {"tags.ns_id": ns_id})
869 for alarm in alarms:
870 alarm_action = self.engine.db.get_one("alarms_action", {"uuid": alarm['uuid']})
871 alarm.update(alarm_action)
872 return self._format_out(str(alarms))
873 except Exception:
874 return self._format_out("Please provide valid ns id")
875 else:
876 # to return only alarm which are related to given project
877 project = self.engine.db.get_one("projects", {"name": project_name})
878 project_id = project.get('_id')
879 ns_list = self.engine.db.get_list("nsrs", {"_admin.projects_read": project_id})
880 ns_ids = []
881 for ns in ns_list:
882 ns_ids.append(ns.get("_id"))
883 alarms = self.engine.db.get_list("alarms")
884 alarm_list = [alarm for alarm in alarms if alarm["tags"]["ns_id"] in ns_ids]
885 for alrm in alarm_list:
886 action = self.engine.db.get_one("alarms_action", {"uuid": alrm.get("uuid")})
887 alrm.update(action)
888 return self._format_out(str(alarm_list))
889 # to handle patch request for alarm update
890 elif cherrypy.request.method == 'PATCH':
891 data = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
892 try:
893 # check if uuid is valid
894 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
895 except Exception:
896 return self._format_out("Please provide valid alarm uuid.")
897 if data.get("is_enable") is not None:
898 if data.get("is_enable"):
899 alarm_status = 'ok'
900 else:
901 alarm_status = 'disabled'
902 self.engine.db.set_one("alarms", {"uuid": data.get("uuid")},
903 {"alarm_status": alarm_status})
904 else:
905 self.engine.db.set_one("alarms", {"uuid": data.get("uuid")},
906 {"threshold": data.get("threshold")})
907 return self._format_out("Alarm updated")
908 except Exception as e:
909 cherrypy.response.status = e.http_code.value
910 if isinstance(e, (NbiException, EngineException, DbException, FsException, MsgException, AuthException,
911 ValidationError, AuthconnException)):
912 http_code_value = cherrypy.response.status = e.http_code.value
913 http_code_name = e.http_code.name
914 cherrypy.log("Exception {}".format(e))
915 else:
916 http_code_value = cherrypy.response.status = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
917 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
918 http_code_name = HTTPStatus.BAD_REQUEST.name
919 problem_details = {
920 "code": http_code_name,
921 "status": http_code_value,
922 "detail": str(e),
923 }
924 return self._format_out(problem_details)
925
tierno55945e72018-04-06 16:40:27 +0200926 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +0100927 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +0000928 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100929 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +0100930 indata = self._format_in(kwargs)
931 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +0100932 raise NbiException(
933 "Expected application/yaml or application/json Content-Type",
934 HTTPStatus.BAD_REQUEST,
935 )
tiernoa5035702019-07-29 08:54:42 +0000936
937 if method == "GET":
938 token_info = self.authenticator.authorize()
939 # for logging
940 self._format_login(token_info)
941 if token_id:
942 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +0100943 else:
tiernoa5035702019-07-29 08:54:42 +0000944 outdata = self.authenticator.get_token_list(token_info)
945 elif method == "POST":
946 try:
947 token_info = self.authenticator.authorize()
948 except Exception:
949 token_info = None
950 if kwargs:
951 indata.update(kwargs)
952 # This is needed to log the user when authentication fails
953 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +0100954 outdata = token_info = self.authenticator.new_token(
955 token_info, indata, cherrypy.request.remote
956 )
957 cherrypy.session["Authorization"] = outdata["_id"]
tiernoa5035702019-07-29 08:54:42 +0000958 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
959 # for logging
960 self._format_login(token_info)
selvi.ja9a1fc82022-04-04 06:54:30 +0000961 # password expiry check
962 if self.authenticator.check_password_expiry(outdata):
963 outdata = {"id": outdata["id"],
964 "message": "change_password",
965 "user_id": outdata["user_id"]
966 }
tiernoa5035702019-07-29 08:54:42 +0000967 # cherrypy.response.cookie["Authorization"] = outdata["id"]
968 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
969 elif method == "DELETE":
970 if not token_id and "id" in kwargs:
971 token_id = kwargs["id"]
972 elif not token_id:
973 token_info = self.authenticator.authorize()
974 # for logging
975 self._format_login(token_info)
976 token_id = token_info["_id"]
977 outdata = self.authenticator.del_token(token_id)
978 token_info = None
garciadeblas4568a372021-03-24 09:19:48 +0100979 cherrypy.session["Authorization"] = "logout"
tiernoa5035702019-07-29 08:54:42 +0000980 # cherrypy.response.cookie["Authorization"] = token_id
981 # cherrypy.response.cookie["Authorization"]['expires'] = 0
982 else:
garciadeblas4568a372021-03-24 09:19:48 +0100983 raise NbiException(
984 "Method {} not allowed for token".format(method),
985 HTTPStatus.METHOD_NOT_ALLOWED,
986 )
tiernoa5035702019-07-29 08:54:42 +0000987 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100988
989 @cherrypy.expose
990 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +0100991 if not cherrypy.config.get("server.enable_test") or (
992 isinstance(cherrypy.config["server.enable_test"], str)
993 and cherrypy.config["server.enable_test"].lower() == "false"
994 ):
tierno4836bac2020-01-15 14:41:48 +0000995 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
996 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +0100997 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +0100998 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +0100999 return (
1000 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
1001 "sleep/<time>\nmessage/topic\n</pre></html>"
1002 )
tiernof27c79b2018-03-12 17:08:42 +01001003
1004 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001005 try:
1006 # self.engine.load_dbase(cherrypy.request.app.config)
1007 self.engine.create_admin()
1008 return "Done. User 'admin', password 'admin' created"
1009 except Exception:
1010 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1011 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001012 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001013 return cherrypy.lib.static.serve_file(
1014 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1015 "text/plain",
1016 "attachment",
1017 )
tiernof27c79b2018-03-12 17:08:42 +01001018 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001019 f_path = (
1020 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1021 )
tiernof27c79b2018-03-12 17:08:42 +01001022 f = open(f_path, "r")
1023 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001024 return f
tierno55945e72018-04-06 16:40:27 +02001025
tiernof27c79b2018-03-12 17:08:42 +01001026 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001027 deleted_info = self.engine.db.del_list(args[1], kwargs)
1028 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1029 elif len(args) and args[0] == "fs-clear":
1030 if len(args) >= 2:
1031 folders = (args[1],)
1032 else:
1033 folders = self.engine.fs.dir_ls(".")
1034 for folder in folders:
1035 self.engine.fs.file_delete(folder)
1036 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001037 elif args and args[0] == "login":
1038 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001039 cherrypy.response.headers[
1040 "WWW-Authenticate"
1041 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001042 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1043 elif args and args[0] == "login2":
1044 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001045 cherrypy.response.headers[
1046 "WWW-Authenticate"
1047 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001048 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1049 elif args and args[0] == "sleep":
1050 sleep_time = 5
1051 try:
1052 sleep_time = int(args[1])
1053 except Exception:
1054 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1055 return self._format_out("Database already initialized")
1056 thread_info = cherrypy.thread_data
1057 print(thread_info)
1058 time.sleep(sleep_time)
1059 # thread_info
1060 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001061 main_topic = args[1]
1062 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001063 try:
garciadeblas4568a372021-03-24 09:19:48 +01001064 if cherrypy.request.method == "POST":
delacruzramob19cadc2019-10-08 10:18:02 +02001065 to_send = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
tierno55945e72018-04-06 16:40:27 +02001066 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001067 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001068 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001069 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001070 for k, v in kwargs.items():
tiernof1509b22020-05-12 14:32:37 +00001071 v_dict = yaml.load(v, Loader=yaml.SafeLoader)
1072 self.engine.msg.write(main_topic, k, v_dict)
1073 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001074 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001075 return_text += "Error: " + str(e)
1076 return_text += "</pre></html>\n"
1077 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001078
1079 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001080 "<html><pre>\nheaders:\n args: {}\n".format(args)
1081 + " kwargs: {}\n".format(kwargs)
1082 + " headers: {}\n".format(cherrypy.request.headers)
1083 + " path_info: {}\n".format(cherrypy.request.path_info)
1084 + " query_string: {}\n".format(cherrypy.request.query_string)
1085 + " session: {}\n".format(cherrypy.session)
1086 + " cookie: {}\n".format(cherrypy.request.cookie)
1087 + " method: {}\n".format(cherrypy.request.method)
1088 + " session: {}\n".format(cherrypy.session.get("fieldname"))
1089 + " body:\n"
1090 )
tiernoc94c3df2018-02-09 15:38:54 +01001091 return_text += " length: {}\n".format(cherrypy.request.body.length)
1092 if cherrypy.request.body.length:
1093 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001094 str(
1095 cherrypy.request.body.read(
1096 int(cherrypy.request.headers.get("Content-Length", 0))
1097 )
1098 )
1099 )
tiernoc94c3df2018-02-09 15:38:54 +01001100 if thread_info:
1101 return_text += "thread: {}\n".format(thread_info)
1102 return_text += "</pre></html>"
1103 return return_text
1104
tierno701018c2019-06-25 11:13:14 +00001105 @staticmethod
1106 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001107 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001108 raise NbiException(
1109 "URL must contain at least 'main_topic/version/topic'",
1110 HTTPStatus.METHOD_NOT_ALLOWED,
1111 )
tiernof27c79b2018-03-12 17:08:42 +01001112
tierno701018c2019-06-25 11:13:14 +00001113 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001114 for arg in args:
1115 if arg is None:
1116 break
1117 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001118 raise NbiException(
1119 "URL contains unexpected extra items '{}'".format(arg),
1120 HTTPStatus.METHOD_NOT_ALLOWED,
1121 )
tiernof27c79b2018-03-12 17:08:42 +01001122
1123 if arg in reference:
1124 reference = reference[arg]
1125 elif "<ID>" in reference:
1126 reference = reference["<ID>"]
1127 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001128 # if there is content
1129 if reference["*"]:
1130 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001131 break
1132 else:
garciadeblas4568a372021-03-24 09:19:48 +01001133 raise NbiException(
1134 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1135 )
tiernof27c79b2018-03-12 17:08:42 +01001136 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001137 raise NbiException(
1138 "Method {} not supported yet for this URL".format(method),
1139 HTTPStatus.NOT_IMPLEMENTED,
1140 )
tierno2236d202018-05-16 19:05:16 +02001141 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001142 raise NbiException(
1143 "Method {} not supported for this URL".format(method),
1144 HTTPStatus.METHOD_NOT_ALLOWED,
1145 )
tierno701018c2019-06-25 11:13:14 +00001146 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001147
1148 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001149 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001150 """
1151 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001152 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001153 :param version:
tiernob24258a2018-10-04 18:39:49 +02001154 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001155 :param id:
1156 :return: None
1157 """
1158 # 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 +01001159 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1160 main_topic, version, topic, id
1161 )
tiernof27c79b2018-03-12 17:08:42 +01001162 return
1163
tierno65ca36d2019-02-12 19:27:52 +01001164 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001165 def _extract_query_string_operations(kwargs, method):
1166 """
1167
1168 :param kwargs:
1169 :return:
1170 """
1171 query_string_operations = []
1172 if kwargs:
1173 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1174 if qs in kwargs and kwargs[qs].lower() != "false":
1175 query_string_operations.append(qs.lower() + ":" + method.lower())
1176 return query_string_operations
1177
1178 @staticmethod
1179 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001180 """
1181 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1182 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001183 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001184 :param kwargs: query string input.
1185 :param method: http method: GET, POSST, PUT, ...
1186 :param _id:
1187 :return: admin_query dictionary with keys:
1188 public: True, False or None
1189 force: True or False
1190 project_id: tuple with projects used for accessing an element
1191 set_project: tuple with projects that a created element will belong to
1192 method: show, list, delete, write
1193 """
garciadeblas4568a372021-03-24 09:19:48 +01001194 admin_query = {
1195 "force": False,
1196 "project_id": (token_info["project_id"],),
1197 "username": token_info["username"],
1198 "admin": token_info["admin"],
1199 "public": None,
1200 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1201 }
tierno65ca36d2019-02-12 19:27:52 +01001202 if kwargs:
1203 # FORCE
1204 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001205 if (
1206 kwargs["FORCE"].lower() != "false"
1207 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001208 admin_query["force"] = True
1209 del kwargs["FORCE"]
1210 # PUBLIC
1211 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001212 if (
1213 kwargs["PUBLIC"].lower() != "false"
1214 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001215 admin_query["public"] = True
1216 else:
1217 admin_query["public"] = False
1218 del kwargs["PUBLIC"]
1219 # ADMIN
1220 if "ADMIN" in kwargs:
1221 behave_as = kwargs.pop("ADMIN")
1222 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001223 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001224 raise NbiException(
1225 "Only admin projects can use 'ADMIN' query string",
1226 HTTPStatus.UNAUTHORIZED,
1227 )
1228 if (
1229 not behave_as or behave_as.lower() == "true"
1230 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001231 admin_query["project_id"] = ()
1232 elif isinstance(behave_as, (list, tuple)):
1233 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001234 else: # isinstance(behave_as, str)
1235 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001236 if "SET_PROJECT" in kwargs:
1237 set_project = kwargs.pop("SET_PROJECT")
1238 if not set_project:
1239 admin_query["set_project"] = list(admin_query["project_id"])
1240 else:
1241 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001242 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001243 if admin_query["project_id"]:
1244 for p in set_project:
1245 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001246 raise NbiException(
1247 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1248 "'ADMIN='{p}'".format(p=p),
1249 HTTPStatus.UNAUTHORIZED,
1250 )
tierno65ca36d2019-02-12 19:27:52 +01001251 admin_query["set_project"] = set_project
1252
1253 # PROJECT_READ
1254 # if "PROJECT_READ" in kwargs:
1255 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001256 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001257 if method == "GET":
1258 if _id:
1259 admin_query["method"] = "show"
1260 else:
1261 admin_query["method"] = "list"
1262 elif method == "DELETE":
1263 admin_query["method"] = "delete"
1264 else:
1265 admin_query["method"] = "write"
1266 return admin_query
1267
tiernoc94c3df2018-02-09 15:38:54 +01001268 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001269 def default(
1270 self,
1271 main_topic=None,
1272 version=None,
1273 topic=None,
1274 _id=None,
1275 item=None,
1276 *args,
1277 **kwargs
1278 ):
tierno701018c2019-06-25 11:13:14 +00001279 token_info = None
tiernof27c79b2018-03-12 17:08:42 +01001280 outdata = None
1281 _format = None
tierno0f98af52018-03-19 10:28:22 +01001282 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001283 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001284 rollback = []
tierno701018c2019-06-25 11:13:14 +00001285 engine_session = None
tiernoc94c3df2018-02-09 15:38:54 +01001286 try:
tiernob24258a2018-10-04 18:39:49 +02001287 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001288 raise NbiException(
1289 "URL must contain at least 'main_topic/version/topic'",
1290 HTTPStatus.METHOD_NOT_ALLOWED,
1291 )
1292 if main_topic not in (
1293 "admin",
1294 "vnfpkgm",
1295 "nsd",
1296 "nslcm",
1297 "pdu",
1298 "nst",
1299 "nsilcm",
1300 "nspm",
1301 ):
1302 raise NbiException(
1303 "URL main_topic '{}' not supported".format(main_topic),
1304 HTTPStatus.METHOD_NOT_ALLOWED,
1305 )
1306 if version != "v1":
1307 raise NbiException(
1308 "URL version '{}' not supported".format(version),
1309 HTTPStatus.METHOD_NOT_ALLOWED,
1310 )
tiernoc94c3df2018-02-09 15:38:54 +01001311
garciadeblas4568a372021-03-24 09:19:48 +01001312 if (
1313 kwargs
1314 and "METHOD" in kwargs
1315 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1316 ):
tiernof27c79b2018-03-12 17:08:42 +01001317 method = kwargs.pop("METHOD")
1318 else:
1319 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001320
garciadeblas4568a372021-03-24 09:19:48 +01001321 role_permission = self._check_valid_url_method(
1322 method, main_topic, version, topic, _id, item, *args
1323 )
1324 query_string_operations = self._extract_query_string_operations(
1325 kwargs, method
1326 )
tiernob24258a2018-10-04 18:39:49 +02001327 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001328 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001329 token_info = self.authenticator.authorize(
1330 role_permission, query_string_operations, _id
1331 )
tierno12eac3c2020-03-19 23:22:08 +00001332 if main_topic == "admin" and topic == "domains":
1333 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001334 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001335 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001336 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301337
vijay.r35ef2f72019-04-30 17:55:49 +05301338 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001339 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001340
tiernob24258a2018-10-04 18:39:49 +02001341 if main_topic == "nsd":
1342 engine_topic = "nsds"
1343 elif main_topic == "vnfpkgm":
1344 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001345 if topic == "vnfpkg_op_occs":
1346 engine_topic = "vnfpkgops"
1347 if topic == "vnf_packages" and item == "action":
1348 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001349 elif main_topic == "nslcm":
1350 engine_topic = "nsrs"
1351 if topic == "ns_lcm_op_occs":
1352 engine_topic = "nslcmops"
1353 if topic == "vnfrs" or topic == "vnf_instances":
1354 engine_topic = "vnfrs"
garciadeblas9750c5a2018-10-15 16:20:35 +02001355 elif main_topic == "nst":
1356 engine_topic = "nsts"
1357 elif main_topic == "nsilcm":
1358 engine_topic = "nsis"
1359 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001360 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001361 elif main_topic == "pdu":
1362 engine_topic = "pdus"
garciadeblas4568a372021-03-24 09:19:48 +01001363 if (
1364 engine_topic == "vims"
1365 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001366 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001367
preethika.p329b8182020-04-22 12:25:39 +05301368 if topic == "subscriptions":
1369 engine_topic = main_topic + "_" + topic
1370
tiernoc94c3df2018-02-09 15:38:54 +01001371 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001372 if item in (
1373 "nsd_content",
1374 "package_content",
1375 "artifacts",
1376 "vnfd",
1377 "nsd",
1378 "nst",
1379 "nst_content",
1380 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001381 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001382 path = "$DESCRIPTOR"
1383 elif args:
1384 path = args
tiernob24258a2018-10-04 18:39:49 +02001385 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001386 path = ()
1387 else:
1388 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001389 file, _format = self.engine.get_file(
1390 engine_session,
1391 engine_topic,
1392 _id,
1393 path,
1394 cherrypy.request.headers.get("Accept"),
1395 )
tiernof27c79b2018-03-12 17:08:42 +01001396 outdata = file
1397 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001398 outdata = self.engine.get_item_list(
1399 engine_session, engine_topic, kwargs, api_req=True
1400 )
tiernoc94c3df2018-02-09 15:38:54 +01001401 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301402 if item == "reports":
1403 # TODO check that project_id (_id in this context) has permissions
1404 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301405 filter_q = None
1406 if "vcaStatusRefresh" in kwargs:
1407 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
1408 outdata = self.engine.get_item(engine_session, engine_topic, _id, filter_q, True)
delacruzramo271d2002019-12-02 21:00:37 +01001409
tiernof27c79b2018-03-12 17:08:42 +01001410 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001411 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001412 if topic in (
1413 "ns_descriptors_content",
1414 "vnf_packages_content",
1415 "netslice_templates_content",
1416 ):
tiernof27c79b2018-03-12 17:08:42 +01001417 _id = cherrypy.request.headers.get("Transaction-Id")
1418 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001419 _id, _ = self.engine.new_item(
1420 rollback,
1421 engine_session,
1422 engine_topic,
1423 {},
1424 None,
1425 cherrypy.request.headers,
1426 )
1427 completed = self.engine.upload_content(
1428 engine_session,
1429 engine_topic,
1430 _id,
1431 indata,
1432 kwargs,
1433 cherrypy.request.headers,
1434 )
tiernof27c79b2018-03-12 17:08:42 +01001435 if completed:
tiernob24258a2018-10-04 18:39:49 +02001436 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001437 else:
1438 cherrypy.response.headers["Transaction-Id"] = _id
1439 outdata = {"id": _id}
tiernob24258a2018-10-04 18:39:49 +02001440 elif topic == "ns_instances_content":
1441 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001442 _id, _ = self.engine.new_item(
1443 rollback, engine_session, engine_topic, indata, kwargs
1444 )
tiernob24258a2018-10-04 18:39:49 +02001445 # creates nslcmop
1446 indata["lcmOperationType"] = "instantiate"
1447 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001448 nslcmop_id, _ = self.engine.new_item(
1449 rollback, engine_session, "nslcmops", indata, None
1450 )
tiernob24258a2018-10-04 18:39:49 +02001451 self._set_location_header(main_topic, version, topic, _id)
kuuse078f55e2019-05-16 19:24:21 +02001452 outdata = {"id": _id, "nslcmop_id": nslcmop_id}
tiernob24258a2018-10-04 18:39:49 +02001453 elif topic == "ns_instances" and item:
1454 indata["lcmOperationType"] = item
1455 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001456 _id, _ = self.engine.new_item(
1457 rollback, engine_session, "nslcmops", indata, kwargs
1458 )
1459 self._set_location_header(
1460 main_topic, version, "ns_lcm_op_occs", _id
1461 )
tierno65acb4d2018-04-06 16:42:40 +02001462 outdata = {"id": _id}
1463 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001464 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001465 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001466 _id, _ = self.engine.new_item(
1467 rollback, engine_session, engine_topic, indata, kwargs
1468 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001469 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001470 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001471 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001472 nsilcmop_id, _ = self.engine.new_item(
1473 rollback, engine_session, "nsilcmops", indata, kwargs
1474 )
kuuse078f55e2019-05-16 19:24:21 +02001475 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001476 elif topic == "netslice_instances" and item:
1477 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001478 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001479 _id, _ = self.engine.new_item(
1480 rollback, engine_session, "nsilcmops", indata, kwargs
1481 )
1482 self._set_location_header(
1483 main_topic, version, "nsi_lcm_op_occs", _id
1484 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001485 outdata = {"id": _id}
1486 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001487 elif topic == "vnf_packages" and item == "action":
1488 indata["lcmOperationType"] = item
1489 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001490 _id, _ = self.engine.new_item(
1491 rollback, engine_session, "vnfpkgops", indata, kwargs
1492 )
1493 self._set_location_header(
1494 main_topic, version, "vnfpkg_op_occs", _id
1495 )
delacruzramo271d2002019-12-02 21:00:37 +01001496 outdata = {"id": _id}
1497 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301498 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001499 _id, _ = self.engine.new_item(
1500 rollback, engine_session, engine_topic, indata, kwargs
1501 )
preethika.p329b8182020-04-22 12:25:39 +05301502 self._set_location_header(main_topic, version, topic, _id)
1503 link = {}
1504 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001505 outdata = {
1506 "id": _id,
1507 "filter": indata["filter"],
1508 "callbackUri": indata["CallbackUri"],
1509 "_links": link,
1510 }
preethika.p329b8182020-04-22 12:25:39 +05301511 cherrypy.response.status = HTTPStatus.CREATED.value
tiernof27c79b2018-03-12 17:08:42 +01001512 else:
garciadeblas4568a372021-03-24 09:19:48 +01001513 _id, op_id = self.engine.new_item(
1514 rollback,
1515 engine_session,
1516 engine_topic,
1517 indata,
1518 kwargs,
1519 cherrypy.request.headers,
1520 )
tiernob24258a2018-10-04 18:39:49 +02001521 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001522 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00001523 if op_id:
1524 outdata["op_id"] = op_id
1525 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001526 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02001527
tiernoc94c3df2018-02-09 15:38:54 +01001528 elif method == "DELETE":
1529 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001530 outdata = self.engine.del_item_list(
1531 engine_session, engine_topic, kwargs
1532 )
tierno09c073e2018-04-26 13:36:48 +02001533 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01001534 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00001535 # for NS NSI generate an operation
1536 op_id = None
tierno701018c2019-06-25 11:13:14 +00001537 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02001538 nslcmop_desc = {
1539 "lcmOperationType": "terminate",
1540 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001541 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02001542 }
garciadeblas4568a372021-03-24 09:19:48 +01001543 op_id, _ = self.engine.new_item(
1544 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
1545 )
tierno22577432020-04-08 15:16:57 +00001546 if op_id:
1547 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001548 elif (
1549 topic == "netslice_instances_content"
1550 and not engine_session["force"]
1551 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001552 nsilcmop_desc = {
1553 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02001554 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001555 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02001556 }
garciadeblas4568a372021-03-24 09:19:48 +01001557 op_id, _ = self.engine.new_item(
1558 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
1559 )
tierno22577432020-04-08 15:16:57 +00001560 if op_id:
1561 outdata = {"_id": op_id}
1562 # if there is not any deletion in process, delete
1563 if not op_id:
1564 op_id = self.engine.del_item(engine_session, engine_topic, _id)
1565 if op_id:
1566 outdata = {"op_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001567 cherrypy.response.status = (
1568 HTTPStatus.ACCEPTED.value
1569 if op_id
1570 else HTTPStatus.NO_CONTENT.value
1571 )
tierno09c073e2018-04-26 13:36:48 +02001572
tierno7ae10112018-05-18 14:36:02 +02001573 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00001574 op_id = None
tierno701018c2019-06-25 11:13:14 +00001575 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01001576 raise NbiException(
1577 "Nothing to update. Provide payload and/or query string",
1578 HTTPStatus.BAD_REQUEST,
1579 )
1580 if (
1581 item in ("nsd_content", "package_content", "nst_content")
1582 and method == "PUT"
1583 ):
1584 completed = self.engine.upload_content(
1585 engine_session,
1586 engine_topic,
1587 _id,
1588 indata,
1589 kwargs,
1590 cherrypy.request.headers,
1591 )
tiernof27c79b2018-03-12 17:08:42 +01001592 if not completed:
1593 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01001594 else:
garciadeblas4568a372021-03-24 09:19:48 +01001595 op_id = self.engine.edit_item(
1596 engine_session, engine_topic, _id, indata, kwargs
1597 )
tiernobdebce92019-07-01 15:36:49 +00001598
1599 if op_id:
1600 cherrypy.response.status = HTTPStatus.ACCEPTED.value
1601 outdata = {"op_id": op_id}
1602 else:
1603 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
1604 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01001605 else:
garciadeblas4568a372021-03-24 09:19:48 +01001606 raise NbiException(
1607 "Method {} not allowed".format(method),
1608 HTTPStatus.METHOD_NOT_ALLOWED,
1609 )
tiernoa6bb45d2019-06-14 09:45:39 +00001610
1611 # if Role information changes, it is needed to reload the information of roles
1612 if topic == "roles" and method != "GET":
1613 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01001614
garciadeblas4568a372021-03-24 09:19:48 +01001615 if (
1616 topic == "projects"
1617 and method == "DELETE"
1618 or topic in ["users", "roles"]
1619 and method in ["PUT", "PATCH", "DELETE"]
1620 ):
delacruzramoad682a52019-12-10 16:26:34 +01001621 self.authenticator.remove_token_from_cache()
1622
tierno701018c2019-06-25 11:13:14 +00001623 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02001624 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01001625 if isinstance(
1626 e,
1627 (
1628 NbiException,
1629 EngineException,
1630 DbException,
1631 FsException,
1632 MsgException,
1633 AuthException,
1634 ValidationError,
1635 AuthconnException,
1636 ),
1637 ):
tiernob24258a2018-10-04 18:39:49 +02001638 http_code_value = cherrypy.response.status = e.http_code.value
1639 http_code_name = e.http_code.name
1640 cherrypy.log("Exception {}".format(e))
1641 else:
garciadeblas4568a372021-03-24 09:19:48 +01001642 http_code_value = (
1643 cherrypy.response.status
1644 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01001645 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02001646 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02001647 if hasattr(outdata, "close"): # is an open file
1648 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02001649 error_text = str(e)
1650 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02001651 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02001652 try:
tiernocc103432018-10-19 14:10:35 +02001653 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01001654 self.engine.db.set_one(
1655 rollback_item["topic"],
1656 {"_id": rollback_item["_id"]},
1657 rollback_item["content"],
1658 fail_on_empty=False,
1659 )
preethika.p329b8182020-04-22 12:25:39 +05301660 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01001661 self.engine.db.del_list(
1662 rollback_item["topic"],
1663 rollback_item["filter"],
1664 fail_on_empty=False,
1665 )
tiernocc103432018-10-19 14:10:35 +02001666 else:
garciadeblas4568a372021-03-24 09:19:48 +01001667 self.engine.db.del_one(
1668 rollback_item["topic"],
1669 {"_id": rollback_item["_id"]},
1670 fail_on_empty=False,
1671 )
tierno3ace63c2018-05-03 17:51:43 +02001672 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01001673 rollback_error_text = "Rollback Exception {}: {}".format(
1674 rollback_item, e2
1675 )
tiernob24258a2018-10-04 18:39:49 +02001676 cherrypy.log(rollback_error_text)
1677 error_text += ". " + rollback_error_text
1678 # if isinstance(e, MsgException):
1679 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
1680 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01001681 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02001682 "code": http_code_name,
1683 "status": http_code_value,
1684 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01001685 }
tierno701018c2019-06-25 11:13:14 +00001686 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001687 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00001688 finally:
1689 if token_info:
1690 self._format_login(token_info)
1691 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
1692 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
1693 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01001694 cherrypy.request.login += ";{}={}".format(
1695 logging_id, outdata[logging_id][:36]
1696 )
tiernoc94c3df2018-02-09 15:38:54 +01001697
1698
tiernoc94c3df2018-02-09 15:38:54 +01001699def _start_service():
1700 """
1701 Callback function called when cherrypy.engine starts
1702 Override configuration with env variables
1703 Set database, storage, message configuration
1704 Init database with admin/admin user password
1705 """
tierno932499c2019-01-28 17:28:10 +00001706 global nbi_server
1707 global subscription_thread
tiernoc94c3df2018-02-09 15:38:54 +01001708 cherrypy.log.error("Starting osm_nbi")
1709 # update general cherrypy configuration
1710 update_dict = {}
1711
garciadeblas4568a372021-03-24 09:19:48 +01001712 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01001713 for k, v in environ.items():
1714 if not k.startswith("OSMNBI_"):
1715 continue
tiernoe1281182018-05-22 12:24:36 +02001716 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01001717 if not k2:
1718 continue
1719 try:
1720 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01001721 if k == "OSMNBI_STATIC_DIR":
1722 engine_config["/static"]["tools.staticdir.dir"] = v
1723 engine_config["/static"]["tools.staticdir.on"] = True
1724 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
1725 update_dict["server.socket_port"] = int(v)
1726 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
1727 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02001728 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01001729 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001730 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02001731 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001732 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01001733 engine_config[k1][k2] = int(v)
1734 else:
1735 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001736
tiernoc94c3df2018-02-09 15:38:54 +01001737 except ValueError as e:
1738 cherrypy.log.error("Ignoring environ '{}': " + str(e))
1739 except Exception as e:
1740 cherrypy.log.warn("skipping environ '{}' on exception '{}'".format(k, e))
1741
1742 if update_dict:
1743 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02001744 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001745
1746 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01001747 log_format_simple = (
1748 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
1749 )
1750 log_formatter_simple = logging.Formatter(
1751 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
1752 )
tiernoc94c3df2018-02-09 15:38:54 +01001753 logger_server = logging.getLogger("cherrypy.error")
1754 logger_access = logging.getLogger("cherrypy.access")
1755 logger_cherry = logging.getLogger("cherrypy")
1756 logger_nbi = logging.getLogger("nbi")
1757
tiernof5298be2018-05-16 14:43:57 +02001758 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01001759 file_handler = logging.handlers.RotatingFileHandler(
1760 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
1761 )
tiernoc94c3df2018-02-09 15:38:54 +01001762 file_handler.setFormatter(log_formatter_simple)
1763 logger_cherry.addHandler(file_handler)
1764 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02001765 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01001766 for format_, logger in {
1767 "nbi.server %(filename)s:%(lineno)s": logger_server,
1768 "nbi.access %(filename)s:%(lineno)s": logger_access,
1769 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
1770 }.items():
tiernob24258a2018-10-04 18:39:49 +02001771 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01001772 log_formatter_cherry = logging.Formatter(
1773 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
1774 )
tiernob24258a2018-10-04 18:39:49 +02001775 str_handler = logging.StreamHandler()
1776 str_handler.setFormatter(log_formatter_cherry)
1777 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01001778
tiernof5298be2018-05-16 14:43:57 +02001779 if engine_config["global"].get("log.level"):
1780 logger_cherry.setLevel(engine_config["global"]["log.level"])
1781 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01001782
1783 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01001784 for k1, logname in {
1785 "message": "nbi.msg",
1786 "database": "nbi.db",
1787 "storage": "nbi.fs",
1788 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01001789 engine_config[k1]["logger_name"] = logname
1790 logger_module = logging.getLogger(logname)
1791 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01001792 file_handler = logging.handlers.RotatingFileHandler(
1793 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
1794 )
tiernoc94c3df2018-02-09 15:38:54 +01001795 file_handler.setFormatter(log_formatter_simple)
1796 logger_module.addHandler(file_handler)
1797 if "loglevel" in engine_config[k1]:
1798 logger_module.setLevel(engine_config[k1]["loglevel"])
1799 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01001800 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
1801 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
1802 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
1803 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
1804 target_version=auth_database_version
1805 )
tiernobee508e2019-01-21 11:21:49 +00001806
tierno932499c2019-01-28 17:28:10 +00001807 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01001808 subscription_thread = SubscriptionThread(
1809 config=engine_config, engine=nbi_server.engine
1810 )
tierno932499c2019-01-28 17:28:10 +00001811 subscription_thread.start()
1812 # Do not capture except SubscriptionException
1813
tiernob2e48bd2020-02-04 15:47:18 +00001814 backend = engine_config["authentication"]["backend"]
garciadeblas4568a372021-03-24 09:19:48 +01001815 cherrypy.log.error(
1816 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
1817 nbi_version, nbi_version_date, backend
1818 )
1819 )
tiernoc94c3df2018-02-09 15:38:54 +01001820
1821
1822def _stop_service():
1823 """
1824 Callback function called when cherrypy.engine stops
1825 TODO: Ending database connections.
1826 """
tierno932499c2019-01-28 17:28:10 +00001827 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01001828 if subscription_thread:
1829 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00001830 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01001831 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01001832 cherrypy.log.error("Stopping osm_nbi")
1833
tierno2236d202018-05-16 19:05:16 +02001834
tiernof5298be2018-05-16 14:43:57 +02001835def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00001836 global nbi_server
tiernoc94c3df2018-02-09 15:38:54 +01001837 # conf = {
1838 # '/': {
1839 # #'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
1840 # 'tools.sessions.on': True,
1841 # 'tools.response_headers.on': True,
1842 # # 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
1843 # }
1844 # }
1845 # cherrypy.Server.ssl_module = 'builtin'
1846 # cherrypy.Server.ssl_certificate = "http/cert.pem"
1847 # cherrypy.Server.ssl_private_key = "http/privkey.pem"
1848 # cherrypy.Server.thread_pool = 10
1849 # cherrypy.config.update({'Server.socket_port': config["port"], 'Server.socket_host': config["host"]})
1850
1851 # cherrypy.config.update({'tools.auth_basic.on': True,
1852 # 'tools.auth_basic.realm': 'localhost',
1853 # 'tools.auth_basic.checkpassword': validate_password})
tierno932499c2019-01-28 17:28:10 +00001854 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01001855 cherrypy.engine.subscribe("start", _start_service)
1856 cherrypy.engine.subscribe("stop", _stop_service)
1857 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02001858
1859
1860def usage():
garciadeblas4568a372021-03-24 09:19:48 +01001861 print(
1862 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02001863 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
1864 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01001865 """.format(
1866 sys.argv[0]
1867 )
1868 )
tierno2236d202018-05-16 19:05:16 +02001869 # --log-socket-host HOST: send logs to this host")
1870 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01001871
1872
garciadeblas4568a372021-03-24 09:19:48 +01001873if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02001874 try:
1875 # load parameters and configuration
1876 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
1877 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
1878 config_file = None
1879 for o, a in opts:
1880 if o in ("-h", "--help"):
1881 usage()
1882 sys.exit()
1883 elif o in ("-c", "--config"):
1884 config_file = a
1885 # elif o == "--log-socket-port":
1886 # log_socket_port = a
1887 # elif o == "--log-socket-host":
1888 # log_socket_host = a
1889 # elif o == "--log-file":
1890 # log_file = a
1891 else:
1892 assert False, "Unhandled option"
1893 if config_file:
1894 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01001895 print(
1896 "configuration file '{}' that not exist".format(config_file),
1897 file=sys.stderr,
1898 )
tiernof5298be2018-05-16 14:43:57 +02001899 exit(1)
1900 else:
garciadeblas4568a372021-03-24 09:19:48 +01001901 for config_file in (
1902 __file__[: __file__.rfind(".")] + ".cfg",
1903 "./nbi.cfg",
1904 "/etc/osm/nbi.cfg",
1905 ):
tiernof5298be2018-05-16 14:43:57 +02001906 if path.isfile(config_file):
1907 break
1908 else:
garciadeblas4568a372021-03-24 09:19:48 +01001909 print(
1910 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
1911 file=sys.stderr,
1912 )
tiernof5298be2018-05-16 14:43:57 +02001913 exit(1)
1914 nbi(config_file)
1915 except getopt.GetoptError as e:
1916 print(str(e), file=sys.stderr)
1917 # usage()
1918 exit(1)