blob: 502207f31de4735443e01cef3d9a49f50ecc6364 [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
tiernoc94c3df2018-02-09 15:38:54 +010089 /ns_lcm_op_occs 5 5
90 /<nsLcmOpOccId> 5 5 5
91 TO BE COMPLETED 5 5
tiernof759d822018-06-11 18:54:54 +020092 /vnf_instances (also vnfrs for compatibility) O
93 /<vnfInstanceId> O
tiernof27c79b2018-03-12 17:08:42 +010094 /subscriptions 5 5
95 /<subscriptionId> 5 X
garciadeblas9750c5a2018-10-15 16:20:35 +020096
tiernocb83c942018-09-24 17:28:13 +020097 /pdu/v1
tierno032916c2019-03-22 13:27:12 +000098 /pdu_descriptors O O
tiernocb83c942018-09-24 17:28:13 +020099 /<id> O O O O
garciadeblas9750c5a2018-10-15 16:20:35 +0200100
tiernof27c79b2018-03-12 17:08:42 +0100101 /admin/v1
102 /tokens O O
tierno2236d202018-05-16 19:05:16 +0200103 /<id> O O
tiernof27c79b2018-03-12 17:08:42 +0100104 /users O O
tiernocd54a4a2018-09-12 16:40:35 +0200105 /<id> O O O O
tiernof27c79b2018-03-12 17:08:42 +0100106 /projects O O
tierno2236d202018-05-16 19:05:16 +0200107 /<id> O O
tierno55ba2e62018-12-11 17:22:22 +0000108 /vim_accounts (also vims for compatibility) O O
109 /<id> O O O
110 /wim_accounts O O
tierno2236d202018-05-16 19:05:16 +0200111 /<id> O O O
tierno0f98af52018-03-19 10:28:22 +0100112 /sdns O O
tierno2236d202018-05-16 19:05:16 +0200113 /<id> O O O
delacruzramofe598fe2019-10-23 18:25:11 +0200114 /k8sclusters O O
115 /<id> O O O
116 /k8srepos O O
117 /<id> O O
Felipe Vicensb66b0412020-05-06 10:11:00 +0200118 /osmrepos O O
119 /<id> O O
tiernoc94c3df2018-02-09 15:38:54 +0100120
garciadeblas9750c5a2018-10-15 16:20:35 +0200121 /nst/v1 O O
122 /netslice_templates_content O O
123 /<nstInfoId> O O O O
124 /netslice_templates O O
125 /<nstInfoId> O O O
126 /nst_content O O
127 /nst O
128 /artifacts[/<artifactPath>] O
129 /subscriptions X X
130 /<subscriptionId> X X
131
132 /nsilcm/v1
133 /netslice_instances_content O O
134 /<SliceInstanceId> O O
135 /netslice_instances O O
136 /<SliceInstanceId> O O
137 instantiate O
138 terminate O
139 action O
140 /nsi_lcm_op_occs O O
141 /<nsiLcmOpOccId> O O O
142 /subscriptions X X
143 /<subscriptionId> X X
144
tierno2236d202018-05-16 19:05:16 +0200145query string:
146 Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force.
tierno36ec8602018-11-02 17:27:11 +0100147 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
148 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
149 op := "eq" | "neq" (or "ne") | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
150 attrName := string
tierno2236d202018-05-16 19:05:16 +0200151 For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any
152 item of the array, that is, pass if any item of the array pass the filter.
153 It allows both ne and neq for not equal
154 TODO: 4.3.3 Attribute selectors
155 all_fields, fields=x,y,.., exclude_default, exclude_fields=x,y,...
tiernoc94c3df2018-02-09 15:38:54 +0100156 (none) … same as “exclude_default”
157 all_fields … all attributes.
tierno2236d202018-05-16 19:05:16 +0200158 fields=<list> … all attributes except all complex attributes with minimum cardinality of zero that are not
159 conditionally mandatory, and that are not provided in <list>.
160 exclude_fields=<list> … all attributes except those complex attributes with a minimum cardinality of zero that
161 are not conditionally mandatory, and that are provided in <list>.
162 exclude_default … all attributes except those complex attributes with a minimum cardinality of zero that are not
163 conditionally mandatory, and that are part of the "default exclude set" defined in the present specification for
164 the particular resource
165 exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality
166 of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the
167 present specification for the particular resource, but that are not part of <list>
tierno65ca36d2019-02-12 19:27:52 +0100168 Additionally it admits some administrator values:
169 FORCE: To force operations skipping dependency checkings
170 ADMIN: To act as an administrator or a different project
171 PUBLIC: To get public descriptors or set a descriptor as public
172 SET_PROJECT: To make a descriptor available for other project
173
tiernoc94c3df2018-02-09 15:38:54 +0100174Header field name Reference Example Descriptions
175 Accept IETF RFC 7231 [19] application/json Content-Types that are acceptable for the response.
176 This header field shall be present if the response is expected to have a non-empty message body.
177 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the request.
178 This header field shall be present if the request has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200179 Authorization IETF RFC 7235 [22] Bearer mF_9.B5f-4.1JqM The authorization token for the request.
180 Details are specified in clause 4.5.3.
tiernoc94c3df2018-02-09 15:38:54 +0100181 Range IETF RFC 7233 [21] 1000-2000 Requested range of bytes from a file
182Header field name Reference Example Descriptions
183 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the response.
184 This header field shall be present if the response has a non-empty message body.
tierno2236d202018-05-16 19:05:16 +0200185 Location IETF RFC 7231 [19] http://www.example.com/vnflcm/v1/vnf_instances/123 Used in redirection, or when a
186 new resource has been created.
tiernoc94c3df2018-02-09 15:38:54 +0100187 This header field shall be present if the response status code is 201 or 3xx.
tierno2236d202018-05-16 19:05:16 +0200188 In the present document this header field is also used if the response status code is 202 and a new resource was
189 created.
190 WWW-Authenticate IETF RFC 7235 [22] Bearer realm="example" Challenge if the corresponding HTTP request has not
191 provided authorization, or error details if the corresponding HTTP request has provided an invalid authorization
192 token.
193 Accept-Ranges IETF RFC 7233 [21] bytes Used by the Server to signal whether or not it supports ranges for
194 certain resources.
195 Content-Range IETF RFC 7233 [21] bytes 21010-47021/ 47022 Signals the byte range that is contained in the
196 response, and the total length of the file.
tiernoc94c3df2018-02-09 15:38:54 +0100197 Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT
tiernoc94c3df2018-02-09 15:38:54 +0100198"""
199
tierno701018c2019-06-25 11:13:14 +0000200valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC")
201# ^ Contains possible administrative query string words:
202# ADMIN=True(by default)|Project|Project-list: See all elements, or elements of a project
203# (not owned by my session project).
204# PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public
205# FORCE=True(by default)|False: Force edition/deletion operations
206# SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio
207
208valid_url_methods = {
209 # contains allowed URL and methods, and the role_permission name
210 "admin": {
211 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100212 "tokens": {
213 "METHODS": ("GET", "POST", "DELETE"),
214 "ROLE_PERMISSION": "tokens:",
215 "<ID>": {"METHODS": ("GET", "DELETE"), "ROLE_PERMISSION": "tokens:id:"},
216 },
217 "users": {
218 "METHODS": ("GET", "POST"),
219 "ROLE_PERMISSION": "users:",
220 "<ID>": {
221 "METHODS": ("GET", "DELETE", "PATCH"),
222 "ROLE_PERMISSION": "users:id:",
223 },
224 },
225 "projects": {
226 "METHODS": ("GET", "POST"),
227 "ROLE_PERMISSION": "projects:",
228 "<ID>": {
229 "METHODS": ("GET", "DELETE", "PATCH"),
230 "ROLE_PERMISSION": "projects:id:",
231 },
232 },
233 "roles": {
234 "METHODS": ("GET", "POST"),
235 "ROLE_PERMISSION": "roles:",
236 "<ID>": {
237 "METHODS": ("GET", "DELETE", "PATCH"),
238 "ROLE_PERMISSION": "roles:id:",
239 },
240 },
241 "vims": {
242 "METHODS": ("GET", "POST"),
243 "ROLE_PERMISSION": "vims:",
244 "<ID>": {
245 "METHODS": ("GET", "DELETE", "PATCH"),
246 "ROLE_PERMISSION": "vims:id:",
247 },
248 },
249 "vim_accounts": {
250 "METHODS": ("GET", "POST"),
251 "ROLE_PERMISSION": "vim_accounts:",
252 "<ID>": {
253 "METHODS": ("GET", "DELETE", "PATCH"),
254 "ROLE_PERMISSION": "vim_accounts:id:",
255 },
256 },
257 "wim_accounts": {
258 "METHODS": ("GET", "POST"),
259 "ROLE_PERMISSION": "wim_accounts:",
260 "<ID>": {
261 "METHODS": ("GET", "DELETE", "PATCH"),
262 "ROLE_PERMISSION": "wim_accounts:id:",
263 },
264 },
265 "sdns": {
266 "METHODS": ("GET", "POST"),
267 "ROLE_PERMISSION": "sdn_controllers:",
268 "<ID>": {
269 "METHODS": ("GET", "DELETE", "PATCH"),
270 "ROLE_PERMISSION": "sdn_controllers:id:",
271 },
272 },
273 "k8sclusters": {
274 "METHODS": ("GET", "POST"),
275 "ROLE_PERMISSION": "k8sclusters:",
276 "<ID>": {
277 "METHODS": ("GET", "DELETE", "PATCH"),
278 "ROLE_PERMISSION": "k8sclusters:id:",
279 },
280 },
281 "vca": {
282 "METHODS": ("GET", "POST"),
283 "ROLE_PERMISSION": "vca:",
284 "<ID>": {
285 "METHODS": ("GET", "DELETE", "PATCH"),
286 "ROLE_PERMISSION": "vca:id:",
287 },
288 },
289 "k8srepos": {
290 "METHODS": ("GET", "POST"),
291 "ROLE_PERMISSION": "k8srepos:",
292 "<ID>": {
293 "METHODS": ("GET", "DELETE"),
294 "ROLE_PERMISSION": "k8srepos:id:",
295 },
296 },
297 "osmrepos": {
298 "METHODS": ("GET", "POST"),
299 "ROLE_PERMISSION": "osmrepos:",
300 "<ID>": {
301 "METHODS": ("GET", "DELETE", "PATCH"),
302 "ROLE_PERMISSION": "osmrepos:id:",
303 },
304 },
305 "domains": {
306 "METHODS": ("GET",),
307 "ROLE_PERMISSION": "domains:",
308 },
tierno701018c2019-06-25 11:13:14 +0000309 }
310 },
311 "pdu": {
312 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100313 "pdu_descriptors": {
314 "METHODS": ("GET", "POST"),
315 "ROLE_PERMISSION": "pduds:",
316 "<ID>": {
317 "METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"),
318 "ROLE_PERMISSION": "pduds:id:",
319 },
320 },
tierno701018c2019-06-25 11:13:14 +0000321 }
322 },
323 "nsd": {
324 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100325 "ns_descriptors_content": {
326 "METHODS": ("GET", "POST"),
327 "ROLE_PERMISSION": "nsds:",
328 "<ID>": {
329 "METHODS": ("GET", "PUT", "DELETE"),
330 "ROLE_PERMISSION": "nsds:id:",
331 },
332 },
333 "ns_descriptors": {
334 "METHODS": ("GET", "POST"),
335 "ROLE_PERMISSION": "nsds:",
336 "<ID>": {
337 "METHODS": ("GET", "DELETE", "PATCH"),
338 "ROLE_PERMISSION": "nsds:id:",
339 "nsd_content": {
340 "METHODS": ("GET", "PUT"),
341 "ROLE_PERMISSION": "nsds:id:content:",
342 },
343 "nsd": {
344 "METHODS": ("GET",), # descriptor inside package
345 "ROLE_PERMISSION": "nsds:id:content:",
346 },
347 "artifacts": {
348 "METHODS": ("GET",),
349 "ROLE_PERMISSION": "nsds:id:nsd_artifact:",
350 "*": None,
351 },
352 },
353 },
354 "pnf_descriptors": {
355 "TODO": ("GET", "POST"),
356 "<ID>": {
357 "TODO": ("GET", "DELETE", "PATCH"),
358 "pnfd_content": {"TODO": ("GET", "PUT")},
359 },
360 },
361 "subscriptions": {
362 "TODO": ("GET", "POST"),
363 "<ID>": {"TODO": ("GET", "DELETE")},
364 },
tierno701018c2019-06-25 11:13:14 +0000365 }
366 },
367 "vnfpkgm": {
368 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100369 "vnf_packages_content": {
370 "METHODS": ("GET", "POST"),
371 "ROLE_PERMISSION": "vnfds:",
372 "<ID>": {
373 "METHODS": ("GET", "PUT", "DELETE"),
374 "ROLE_PERMISSION": "vnfds:id:",
375 },
376 },
377 "vnf_packages": {
378 "METHODS": ("GET", "POST"),
379 "ROLE_PERMISSION": "vnfds:",
380 "<ID>": {
381 "METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo
382 "ROLE_PERMISSION": "vnfds:id:",
383 "package_content": {
384 "METHODS": ("GET", "PUT"), # package
385 "ROLE_PERMISSION": "vnfds:id:",
386 "upload_from_uri": {
387 "METHODS": (),
388 "TODO": ("POST",),
389 "ROLE_PERMISSION": "vnfds:id:upload:",
390 },
391 },
392 "vnfd": {
393 "METHODS": ("GET",), # descriptor inside package
394 "ROLE_PERMISSION": "vnfds:id:content:",
395 },
396 "artifacts": {
397 "METHODS": ("GET",),
398 "ROLE_PERMISSION": "vnfds:id:vnfd_artifact:",
399 "*": None,
400 },
401 "action": {
402 "METHODS": ("POST",),
403 "ROLE_PERMISSION": "vnfds:id:action:",
404 },
405 },
406 },
407 "subscriptions": {
408 "TODO": ("GET", "POST"),
409 "<ID>": {"TODO": ("GET", "DELETE")},
410 },
411 "vnfpkg_op_occs": {
412 "METHODS": ("GET",),
413 "ROLE_PERMISSION": "vnfds:vnfpkgops:",
414 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnfds:vnfpkgops:id:"},
415 },
tierno701018c2019-06-25 11:13:14 +0000416 }
417 },
418 "nslcm": {
419 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100420 "ns_instances_content": {
421 "METHODS": ("GET", "POST"),
422 "ROLE_PERMISSION": "ns_instances:",
423 "<ID>": {
424 "METHODS": ("GET", "DELETE"),
425 "ROLE_PERMISSION": "ns_instances:id:",
426 },
427 },
428 "ns_instances": {
429 "METHODS": ("GET", "POST"),
430 "ROLE_PERMISSION": "ns_instances:",
431 "<ID>": {
432 "METHODS": ("GET", "DELETE"),
433 "ROLE_PERMISSION": "ns_instances:id:",
434 "scale": {
435 "METHODS": ("POST",),
436 "ROLE_PERMISSION": "ns_instances:id:scale:",
437 },
438 "terminate": {
439 "METHODS": ("POST",),
440 "ROLE_PERMISSION": "ns_instances:id:terminate:",
441 },
442 "instantiate": {
443 "METHODS": ("POST",),
444 "ROLE_PERMISSION": "ns_instances:id:instantiate:",
445 },
446 "action": {
447 "METHODS": ("POST",),
448 "ROLE_PERMISSION": "ns_instances:id:action:",
449 },
450 },
451 },
452 "ns_lcm_op_occs": {
453 "METHODS": ("GET",),
454 "ROLE_PERMISSION": "ns_instances:opps:",
455 "<ID>": {
456 "METHODS": ("GET",),
457 "ROLE_PERMISSION": "ns_instances:opps:id:",
458 },
459 },
460 "vnfrs": {
461 "METHODS": ("GET",),
462 "ROLE_PERMISSION": "vnf_instances:",
463 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
464 },
465 "vnf_instances": {
466 "METHODS": ("GET",),
467 "ROLE_PERMISSION": "vnf_instances:",
468 "<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"},
469 },
470 "subscriptions": {
471 "METHODS": ("GET", "POST"),
472 "ROLE_PERMISSION": "ns_subscriptions:",
473 "<ID>": {
474 "METHODS": ("GET", "DELETE"),
475 "ROLE_PERMISSION": "ns_subscriptions:id:",
476 },
477 },
tierno701018c2019-06-25 11:13:14 +0000478 }
479 },
480 "nst": {
481 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100482 "netslice_templates_content": {
483 "METHODS": ("GET", "POST"),
484 "ROLE_PERMISSION": "slice_templates:",
485 "<ID>": {
486 "METHODS": ("GET", "PUT", "DELETE"),
487 "ROLE_PERMISSION": "slice_templates:id:",
488 },
489 },
490 "netslice_templates": {
491 "METHODS": ("GET", "POST"),
492 "ROLE_PERMISSION": "slice_templates:",
493 "<ID>": {
494 "METHODS": ("GET", "DELETE"),
495 "TODO": ("PATCH",),
496 "ROLE_PERMISSION": "slice_templates:id:",
497 "nst_content": {
498 "METHODS": ("GET", "PUT"),
499 "ROLE_PERMISSION": "slice_templates:id:content:",
500 },
501 "nst": {
502 "METHODS": ("GET",), # descriptor inside package
503 "ROLE_PERMISSION": "slice_templates:id:content:",
504 },
505 "artifacts": {
506 "METHODS": ("GET",),
507 "ROLE_PERMISSION": "slice_templates:id:content:",
508 "*": None,
509 },
510 },
511 },
512 "subscriptions": {
513 "TODO": ("GET", "POST"),
514 "<ID>": {"TODO": ("GET", "DELETE")},
515 },
tierno701018c2019-06-25 11:13:14 +0000516 }
517 },
518 "nsilcm": {
519 "v1": {
garciadeblas4568a372021-03-24 09:19:48 +0100520 "netslice_instances_content": {
521 "METHODS": ("GET", "POST"),
522 "ROLE_PERMISSION": "slice_instances:",
523 "<ID>": {
524 "METHODS": ("GET", "DELETE"),
525 "ROLE_PERMISSION": "slice_instances:id:",
526 },
527 },
528 "netslice_instances": {
529 "METHODS": ("GET", "POST"),
530 "ROLE_PERMISSION": "slice_instances:",
531 "<ID>": {
532 "METHODS": ("GET", "DELETE"),
533 "ROLE_PERMISSION": "slice_instances:id:",
534 "terminate": {
535 "METHODS": ("POST",),
536 "ROLE_PERMISSION": "slice_instances:id:terminate:",
537 },
538 "instantiate": {
539 "METHODS": ("POST",),
540 "ROLE_PERMISSION": "slice_instances:id:instantiate:",
541 },
542 "action": {
543 "METHODS": ("POST",),
544 "ROLE_PERMISSION": "slice_instances:id:action:",
545 },
546 },
547 },
548 "nsi_lcm_op_occs": {
549 "METHODS": ("GET",),
550 "ROLE_PERMISSION": "slice_instances:opps:",
551 "<ID>": {
552 "METHODS": ("GET",),
553 "ROLE_PERMISSION": "slice_instances:opps:id:",
554 },
555 },
tierno701018c2019-06-25 11:13:14 +0000556 }
557 },
558 "nspm": {
559 "v1": {
560 "pm_jobs": {
561 "<ID>": {
562 "reports": {
garciadeblas4568a372021-03-24 09:19:48 +0100563 "<ID>": {
564 "METHODS": ("GET",),
565 "ROLE_PERMISSION": "reports:id:",
566 }
tierno701018c2019-06-25 11:13:14 +0000567 }
568 },
569 },
570 },
571 },
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000572 "nsfm": {
573 "v1": {
574 "alarms": {"METHODS": ("GET", "PATCH"),
575 "ROLE_PERMISSION": "alarms:",
576 "<ID>": {"METHODS": ("GET", "PATCH"),
577 "ROLE_PERMISSION": "alarms:id:",
578 },
579 }
580 },
581 },
tierno701018c2019-06-25 11:13:14 +0000582}
583
tiernoc94c3df2018-02-09 15:38:54 +0100584
585class NbiException(Exception):
tiernoc94c3df2018-02-09 15:38:54 +0100586 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
587 Exception.__init__(self, message)
588 self.http_code = http_code
589
590
591class Server(object):
592 instance = 0
593 # to decode bytes to str
594 reader = getreader("utf-8")
595
596 def __init__(self):
597 self.instance += 1
tierno701018c2019-06-25 11:13:14 +0000598 self.authenticator = Authenticator(valid_url_methods, valid_query_string)
delacruzramoad682a52019-12-10 16:26:34 +0100599 self.engine = Engine(self.authenticator)
tiernoc94c3df2018-02-09 15:38:54 +0100600
tiernoc94c3df2018-02-09 15:38:54 +0100601 def _format_in(self, kwargs):
602 try:
603 indata = None
604 if cherrypy.request.body.length:
605 error_text = "Invalid input format "
606
607 if "Content-Type" in cherrypy.request.headers:
608 if "application/json" in cherrypy.request.headers["Content-Type"]:
609 error_text = "Invalid json format "
610 indata = json.load(self.reader(cherrypy.request.body))
gcalvinode4adfe2018-10-30 11:46:09 +0100611 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100612 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
613 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100614 indata = yaml.load(
615 cherrypy.request.body, Loader=yaml.SafeLoader
616 )
gcalvinode4adfe2018-10-30 11:46:09 +0100617 cherrypy.request.headers.pop("Content-File-MD5", None)
garciadeblas4568a372021-03-24 09:19:48 +0100618 elif (
619 "application/binary" in cherrypy.request.headers["Content-Type"]
620 or "application/gzip"
621 in cherrypy.request.headers["Content-Type"]
622 or "application/zip" in cherrypy.request.headers["Content-Type"]
623 or "text/plain" in cherrypy.request.headers["Content-Type"]
624 ):
tiernof27c79b2018-03-12 17:08:42 +0100625 indata = cherrypy.request.body # .read()
garciadeblas4568a372021-03-24 09:19:48 +0100626 elif (
627 "multipart/form-data"
628 in cherrypy.request.headers["Content-Type"]
629 ):
tiernoc94c3df2018-02-09 15:38:54 +0100630 if "descriptor_file" in kwargs:
631 filecontent = kwargs.pop("descriptor_file")
632 if not filecontent.file:
garciadeblas4568a372021-03-24 09:19:48 +0100633 raise NbiException(
634 "empty file or content", HTTPStatus.BAD_REQUEST
635 )
tiernof27c79b2018-03-12 17:08:42 +0100636 indata = filecontent.file # .read()
tiernoc94c3df2018-02-09 15:38:54 +0100637 if filecontent.content_type.value:
garciadeblas4568a372021-03-24 09:19:48 +0100638 cherrypy.request.headers[
639 "Content-Type"
640 ] = filecontent.content_type.value
tiernoc94c3df2018-02-09 15:38:54 +0100641 else:
642 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
643 # "Only 'Content-Type' of type 'application/json' or
644 # 'application/yaml' for input format are available")
645 error_text = "Invalid yaml format "
garciadeblas4568a372021-03-24 09:19:48 +0100646 indata = yaml.load(
647 cherrypy.request.body, Loader=yaml.SafeLoader
648 )
gcalvinode4adfe2018-10-30 11:46:09 +0100649 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100650 else:
651 error_text = "Invalid yaml format "
delacruzramob19cadc2019-10-08 10:18:02 +0200652 indata = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
gcalvinode4adfe2018-10-30 11:46:09 +0100653 cherrypy.request.headers.pop("Content-File-MD5", None)
tiernoc94c3df2018-02-09 15:38:54 +0100654 if not indata:
655 indata = {}
656
tiernoc94c3df2018-02-09 15:38:54 +0100657 format_yaml = False
658 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
659 format_yaml = True
660
661 for k, v in kwargs.items():
662 if isinstance(v, str):
663 if v == "":
664 kwargs[k] = None
665 elif format_yaml:
666 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200667 kwargs[k] = yaml.load(v, Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200668 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100669 pass
garciadeblas4568a372021-03-24 09:19:48 +0100670 elif (
671 k.endswith(".gt")
672 or k.endswith(".lt")
673 or k.endswith(".gte")
674 or k.endswith(".lte")
675 ):
tiernoc94c3df2018-02-09 15:38:54 +0100676 try:
677 kwargs[k] = int(v)
tiernoe1281182018-05-22 12:24:36 +0200678 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100679 try:
680 kwargs[k] = float(v)
tiernoe1281182018-05-22 12:24:36 +0200681 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100682 pass
683 elif v.find(",") > 0:
684 kwargs[k] = v.split(",")
685 elif isinstance(v, (list, tuple)):
686 for index in range(0, len(v)):
687 if v[index] == "":
688 v[index] = None
689 elif format_yaml:
690 try:
delacruzramob19cadc2019-10-08 10:18:02 +0200691 v[index] = yaml.load(v[index], Loader=yaml.SafeLoader)
tiernoe1281182018-05-22 12:24:36 +0200692 except Exception:
tiernoc94c3df2018-02-09 15:38:54 +0100693 pass
694
tiernof27c79b2018-03-12 17:08:42 +0100695 return indata
tiernoc94c3df2018-02-09 15:38:54 +0100696 except (ValueError, yaml.YAMLError) as exc:
697 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
698 except KeyError as exc:
garciadeblas4568a372021-03-24 09:19:48 +0100699 raise NbiException(
700 "Query string error: " + str(exc), HTTPStatus.BAD_REQUEST
701 )
tiernob92094f2018-05-11 13:44:22 +0200702 except Exception as exc:
703 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
tiernoc94c3df2018-02-09 15:38:54 +0100704
705 @staticmethod
tierno701018c2019-06-25 11:13:14 +0000706 def _format_out(data, token_info=None, _format=None):
tiernoc94c3df2018-02-09 15:38:54 +0100707 """
708 return string of dictionary data according to requested json, yaml, xml. By default json
tiernof27c79b2018-03-12 17:08:42 +0100709 :param data: response to be sent. Can be a dict, text or file
tierno701018c2019-06-25 11:13:14 +0000710 :param token_info: Contains among other username and project
tierno5792d7d2019-08-30 15:37:12 +0000711 :param _format: The format to be set as Content-Type if data is a file
tiernoc94c3df2018-02-09 15:38:54 +0100712 :return: None
713 """
tierno0f98af52018-03-19 10:28:22 +0100714 accept = cherrypy.request.headers.get("Accept")
tiernof27c79b2018-03-12 17:08:42 +0100715 if data is None:
tierno0f98af52018-03-19 10:28:22 +0100716 if accept and "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100717 return html.format(
718 data, cherrypy.request, cherrypy.response, token_info
719 )
tierno09c073e2018-04-26 13:36:48 +0200720 # cherrypy.response.status = HTTPStatus.NO_CONTENT.value
tiernof27c79b2018-03-12 17:08:42 +0100721 return
722 elif hasattr(data, "read"): # file object
723 if _format:
724 cherrypy.response.headers["Content-Type"] = _format
725 elif "b" in data.mode: # binariy asssumig zip
garciadeblas4568a372021-03-24 09:19:48 +0100726 cherrypy.response.headers["Content-Type"] = "application/zip"
tiernof27c79b2018-03-12 17:08:42 +0100727 else:
garciadeblas4568a372021-03-24 09:19:48 +0100728 cherrypy.response.headers["Content-Type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +0100729 # TODO check that cherrypy close file. If not implement pending things to close per thread next
730 return data
tierno0f98af52018-03-19 10:28:22 +0100731 if accept:
Frank Bryden02e700c2020-06-03 13:34:16 +0000732 if "text/html" in accept:
garciadeblas4568a372021-03-24 09:19:48 +0100733 return html.format(
734 data, cherrypy.request, cherrypy.response, token_info
735 )
Frank Brydenb5422da2020-08-10 11:44:11 +0000736 elif "application/yaml" in accept or "*/*" in accept:
tiernoc94c3df2018-02-09 15:38:54 +0100737 pass
garciadeblas4568a372021-03-24 09:19:48 +0100738 elif "application/json" in accept or (
739 cherrypy.response.status and cherrypy.response.status >= 300
740 ):
741 cherrypy.response.headers[
742 "Content-Type"
743 ] = "application/json; charset=utf-8"
Frank Bryden02e700c2020-06-03 13:34:16 +0000744 a = json.dumps(data, indent=4) + "\n"
garciadeblas4568a372021-03-24 09:19:48 +0100745 return a.encode("utf8")
746 cherrypy.response.headers["Content-Type"] = "application/yaml"
747 return yaml.safe_dump(
748 data,
749 explicit_start=True,
750 indent=4,
751 default_flow_style=False,
752 tags=False,
753 encoding="utf-8",
754 allow_unicode=True,
755 ) # , canonical=True, default_style='"'
tiernoc94c3df2018-02-09 15:38:54 +0100756
757 @cherrypy.expose
758 def index(self, *args, **kwargs):
tierno701018c2019-06-25 11:13:14 +0000759 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100760 try:
761 if cherrypy.request.method == "GET":
tierno701018c2019-06-25 11:13:14 +0000762 token_info = self.authenticator.authorize()
garciadeblas4568a372021-03-24 09:19:48 +0100763 outdata = token_info # Home page
tiernoc94c3df2018-02-09 15:38:54 +0100764 else:
garciadeblas4568a372021-03-24 09:19:48 +0100765 raise cherrypy.HTTPError(
766 HTTPStatus.METHOD_NOT_ALLOWED.value,
767 "Method {} not allowed for tokens".format(cherrypy.request.method),
768 )
tiernoc94c3df2018-02-09 15:38:54 +0100769
tierno701018c2019-06-25 11:13:14 +0000770 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100771
Eduardo Sousa819d34c2018-07-31 01:20:02 +0100772 except (EngineException, AuthException) as e:
tierno701018c2019-06-25 11:13:14 +0000773 # cherrypy.log("index Exception {}".format(e))
tiernoc94c3df2018-02-09 15:38:54 +0100774 cherrypy.response.status = e.http_code.value
tierno701018c2019-06-25 11:13:14 +0000775 return self._format_out("Welcome to OSM!", token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100776
777 @cherrypy.expose
tierno55945e72018-04-06 16:40:27 +0200778 def version(self, *args, **kwargs):
tiernodfe09572018-04-24 10:41:10 +0200779 # TODO consider to remove and provide version using the static version file
tierno55945e72018-04-06 16:40:27 +0200780 try:
781 if cherrypy.request.method != "GET":
garciadeblas4568a372021-03-24 09:19:48 +0100782 raise NbiException(
783 "Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED
784 )
tierno55945e72018-04-06 16:40:27 +0200785 elif args or kwargs:
garciadeblas4568a372021-03-24 09:19:48 +0100786 raise NbiException(
787 "Invalid URL or query string for version",
788 HTTPStatus.METHOD_NOT_ALLOWED,
789 )
tierno9c630112019-08-29 14:21:41 +0000790 # TODO include version of other modules, pick up from some kafka admin message
tierno5792d7d2019-08-30 15:37:12 +0000791 osm_nbi_version = {"version": nbi_version, "date": nbi_version_date}
792 return self._format_out(osm_nbi_version)
tierno55945e72018-04-06 16:40:27 +0200793 except NbiException as e:
794 cherrypy.response.status = e.http_code.value
795 problem_details = {
796 "code": e.http_code.name,
797 "status": e.http_code.value,
798 "detail": str(e),
799 }
800 return self._format_out(problem_details, None)
801
tierno12eac3c2020-03-19 23:22:08 +0000802 def domain(self):
803 try:
804 domains = {
garciadeblas4568a372021-03-24 09:19:48 +0100805 "user_domain_name": cherrypy.tree.apps["/osm"]
806 .config["authentication"]
807 .get("user_domain_name"),
808 "project_domain_name": cherrypy.tree.apps["/osm"]
809 .config["authentication"]
810 .get("project_domain_name"),
811 }
tierno12eac3c2020-03-19 23:22:08 +0000812 return self._format_out(domains)
813 except NbiException as e:
814 cherrypy.response.status = e.http_code.value
815 problem_details = {
816 "code": e.http_code.name,
817 "status": e.http_code.value,
818 "detail": str(e),
819 }
820 return self._format_out(problem_details, None)
821
tiernoa5035702019-07-29 08:54:42 +0000822 @staticmethod
823 def _format_login(token_info):
824 """
825 Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will
826 log this information
827 :param token_info: Dictionary with token content
828 :return: None
829 """
830 cherrypy.request.login = token_info.get("username", "-")
831 if token_info.get("project_name"):
832 cherrypy.request.login += "/" + token_info["project_name"]
833 if token_info.get("id"):
834 cherrypy.request.login += ";session=" + token_info["id"][0:12]
835
Atul Agarwalb6480fc2021-03-18 08:19:32 +0000836 # NS Fault Management
837 @cherrypy.expose
838 def nsfm(self, version=None, topic=None, uuid=None, project_name=None, ns_id=None, *args, **kwargs):
839 if topic == 'alarms':
840 try:
841 method = cherrypy.request.method
842 role_permission = self._check_valid_url_method(method, "nsfm", version, topic, None, None, *args)
843 query_string_operations = self._extract_query_string_operations(kwargs, method)
844
845 self.authenticator.authorize(role_permission, query_string_operations, None)
846
847 # to handle get request
848 if cherrypy.request.method == 'GET':
849 # if request is on basis of uuid
850 if uuid and uuid != 'None':
851 try:
852 alarm = self.engine.db.get_one("alarms", {"uuid": uuid})
853 alarm_action = self.engine.db.get_one("alarms_action", {"uuid": uuid})
854 alarm.update(alarm_action)
855 vnf = self.engine.db.get_one("vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]})
856 alarm["vnf-id"] = vnf["_id"]
857 return self._format_out(str(alarm))
858 except Exception:
859 return self._format_out("Please provide valid alarm uuid")
860 elif ns_id and ns_id != 'None':
861 # if request is on basis of ns_id
862 try:
863 alarms = self.engine.db.get_list("alarms", {"tags.ns_id": ns_id})
864 for alarm in alarms:
865 alarm_action = self.engine.db.get_one("alarms_action", {"uuid": alarm['uuid']})
866 alarm.update(alarm_action)
867 return self._format_out(str(alarms))
868 except Exception:
869 return self._format_out("Please provide valid ns id")
870 else:
871 # to return only alarm which are related to given project
872 project = self.engine.db.get_one("projects", {"name": project_name})
873 project_id = project.get('_id')
874 ns_list = self.engine.db.get_list("nsrs", {"_admin.projects_read": project_id})
875 ns_ids = []
876 for ns in ns_list:
877 ns_ids.append(ns.get("_id"))
878 alarms = self.engine.db.get_list("alarms")
879 alarm_list = [alarm for alarm in alarms if alarm["tags"]["ns_id"] in ns_ids]
880 for alrm in alarm_list:
881 action = self.engine.db.get_one("alarms_action", {"uuid": alrm.get("uuid")})
882 alrm.update(action)
883 return self._format_out(str(alarm_list))
884 # to handle patch request for alarm update
885 elif cherrypy.request.method == 'PATCH':
886 data = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
887 try:
888 # check if uuid is valid
889 self.engine.db.get_one("alarms", {"uuid": data.get("uuid")})
890 except Exception:
891 return self._format_out("Please provide valid alarm uuid.")
892 if data.get("is_enable") is not None:
893 if data.get("is_enable"):
894 alarm_status = 'ok'
895 else:
896 alarm_status = 'disabled'
897 self.engine.db.set_one("alarms", {"uuid": data.get("uuid")},
898 {"alarm_status": alarm_status})
899 else:
900 self.engine.db.set_one("alarms", {"uuid": data.get("uuid")},
901 {"threshold": data.get("threshold")})
902 return self._format_out("Alarm updated")
903 except Exception as e:
904 cherrypy.response.status = e.http_code.value
905 if isinstance(e, (NbiException, EngineException, DbException, FsException, MsgException, AuthException,
906 ValidationError, AuthconnException)):
907 http_code_value = cherrypy.response.status = e.http_code.value
908 http_code_name = e.http_code.name
909 cherrypy.log("Exception {}".format(e))
910 else:
911 http_code_value = cherrypy.response.status = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
912 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
913 http_code_name = HTTPStatus.BAD_REQUEST.name
914 problem_details = {
915 "code": http_code_name,
916 "status": http_code_value,
917 "detail": str(e),
918 }
919 return self._format_out(problem_details)
920
tierno55945e72018-04-06 16:40:27 +0200921 @cherrypy.expose
tiernof27c79b2018-03-12 17:08:42 +0100922 def token(self, method, token_id=None, kwargs=None):
tierno701018c2019-06-25 11:13:14 +0000923 token_info = None
tiernoc94c3df2018-02-09 15:38:54 +0100924 # self.engine.load_dbase(cherrypy.request.app.config)
tiernof27c79b2018-03-12 17:08:42 +0100925 indata = self._format_in(kwargs)
926 if not isinstance(indata, dict):
garciadeblas4568a372021-03-24 09:19:48 +0100927 raise NbiException(
928 "Expected application/yaml or application/json Content-Type",
929 HTTPStatus.BAD_REQUEST,
930 )
tiernoa5035702019-07-29 08:54:42 +0000931
932 if method == "GET":
933 token_info = self.authenticator.authorize()
934 # for logging
935 self._format_login(token_info)
936 if token_id:
937 outdata = self.authenticator.get_token(token_info, token_id)
tiernoc94c3df2018-02-09 15:38:54 +0100938 else:
tiernoa5035702019-07-29 08:54:42 +0000939 outdata = self.authenticator.get_token_list(token_info)
940 elif method == "POST":
941 try:
942 token_info = self.authenticator.authorize()
943 except Exception:
944 token_info = None
945 if kwargs:
946 indata.update(kwargs)
947 # This is needed to log the user when authentication fails
948 cherrypy.request.login = "{}".format(indata.get("username", "-"))
garciadeblas4568a372021-03-24 09:19:48 +0100949 outdata = token_info = self.authenticator.new_token(
950 token_info, indata, cherrypy.request.remote
951 )
952 cherrypy.session["Authorization"] = outdata["_id"]
tiernoa5035702019-07-29 08:54:42 +0000953 self._set_location_header("admin", "v1", "tokens", outdata["_id"])
954 # for logging
955 self._format_login(token_info)
selvi.ja9a1fc82022-04-04 06:54:30 +0000956 # password expiry check
957 if self.authenticator.check_password_expiry(outdata):
958 outdata = {"id": outdata["id"],
959 "message": "change_password",
960 "user_id": outdata["user_id"]
961 }
tiernoa5035702019-07-29 08:54:42 +0000962 # cherrypy.response.cookie["Authorization"] = outdata["id"]
963 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
964 elif method == "DELETE":
965 if not token_id and "id" in kwargs:
966 token_id = kwargs["id"]
967 elif not token_id:
968 token_info = self.authenticator.authorize()
969 # for logging
970 self._format_login(token_info)
971 token_id = token_info["_id"]
972 outdata = self.authenticator.del_token(token_id)
973 token_info = None
garciadeblas4568a372021-03-24 09:19:48 +0100974 cherrypy.session["Authorization"] = "logout"
tiernoa5035702019-07-29 08:54:42 +0000975 # cherrypy.response.cookie["Authorization"] = token_id
976 # cherrypy.response.cookie["Authorization"]['expires'] = 0
977 else:
garciadeblas4568a372021-03-24 09:19:48 +0100978 raise NbiException(
979 "Method {} not allowed for token".format(method),
980 HTTPStatus.METHOD_NOT_ALLOWED,
981 )
tiernoa5035702019-07-29 08:54:42 +0000982 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100983
984 @cherrypy.expose
985 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +0100986 if not cherrypy.config.get("server.enable_test") or (
987 isinstance(cherrypy.config["server.enable_test"], str)
988 and cherrypy.config["server.enable_test"].lower() == "false"
989 ):
tierno4836bac2020-01-15 14:41:48 +0000990 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
991 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +0100992 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +0100993 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +0100994 return (
995 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
996 "sleep/<time>\nmessage/topic\n</pre></html>"
997 )
tiernof27c79b2018-03-12 17:08:42 +0100998
999 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +01001000 try:
1001 # self.engine.load_dbase(cherrypy.request.app.config)
1002 self.engine.create_admin()
1003 return "Done. User 'admin', password 'admin' created"
1004 except Exception:
1005 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1006 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001007 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001008 return cherrypy.lib.static.serve_file(
1009 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1010 "text/plain",
1011 "attachment",
1012 )
tiernof27c79b2018-03-12 17:08:42 +01001013 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001014 f_path = (
1015 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1016 )
tiernof27c79b2018-03-12 17:08:42 +01001017 f = open(f_path, "r")
1018 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001019 return f
tierno55945e72018-04-06 16:40:27 +02001020
tiernof27c79b2018-03-12 17:08:42 +01001021 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001022 deleted_info = self.engine.db.del_list(args[1], kwargs)
1023 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1024 elif len(args) and args[0] == "fs-clear":
1025 if len(args) >= 2:
1026 folders = (args[1],)
1027 else:
1028 folders = self.engine.fs.dir_ls(".")
1029 for folder in folders:
1030 self.engine.fs.file_delete(folder)
1031 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001032 elif args and args[0] == "login":
1033 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001034 cherrypy.response.headers[
1035 "WWW-Authenticate"
1036 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001037 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1038 elif args and args[0] == "login2":
1039 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001040 cherrypy.response.headers[
1041 "WWW-Authenticate"
1042 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001043 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1044 elif args and args[0] == "sleep":
1045 sleep_time = 5
1046 try:
1047 sleep_time = int(args[1])
1048 except Exception:
1049 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1050 return self._format_out("Database already initialized")
1051 thread_info = cherrypy.thread_data
1052 print(thread_info)
1053 time.sleep(sleep_time)
1054 # thread_info
1055 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001056 main_topic = args[1]
1057 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001058 try:
garciadeblas4568a372021-03-24 09:19:48 +01001059 if cherrypy.request.method == "POST":
delacruzramob19cadc2019-10-08 10:18:02 +02001060 to_send = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
tierno55945e72018-04-06 16:40:27 +02001061 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001062 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001063 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001064 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001065 for k, v in kwargs.items():
tiernof1509b22020-05-12 14:32:37 +00001066 v_dict = yaml.load(v, Loader=yaml.SafeLoader)
1067 self.engine.msg.write(main_topic, k, v_dict)
1068 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001069 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001070 return_text += "Error: " + str(e)
1071 return_text += "</pre></html>\n"
1072 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001073
1074 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001075 "<html><pre>\nheaders:\n args: {}\n".format(args)
1076 + " kwargs: {}\n".format(kwargs)
1077 + " headers: {}\n".format(cherrypy.request.headers)
1078 + " path_info: {}\n".format(cherrypy.request.path_info)
1079 + " query_string: {}\n".format(cherrypy.request.query_string)
1080 + " session: {}\n".format(cherrypy.session)
1081 + " cookie: {}\n".format(cherrypy.request.cookie)
1082 + " method: {}\n".format(cherrypy.request.method)
1083 + " session: {}\n".format(cherrypy.session.get("fieldname"))
1084 + " body:\n"
1085 )
tiernoc94c3df2018-02-09 15:38:54 +01001086 return_text += " length: {}\n".format(cherrypy.request.body.length)
1087 if cherrypy.request.body.length:
1088 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001089 str(
1090 cherrypy.request.body.read(
1091 int(cherrypy.request.headers.get("Content-Length", 0))
1092 )
1093 )
1094 )
tiernoc94c3df2018-02-09 15:38:54 +01001095 if thread_info:
1096 return_text += "thread: {}\n".format(thread_info)
1097 return_text += "</pre></html>"
1098 return return_text
1099
tierno701018c2019-06-25 11:13:14 +00001100 @staticmethod
1101 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001102 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001103 raise NbiException(
1104 "URL must contain at least 'main_topic/version/topic'",
1105 HTTPStatus.METHOD_NOT_ALLOWED,
1106 )
tiernof27c79b2018-03-12 17:08:42 +01001107
tierno701018c2019-06-25 11:13:14 +00001108 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001109 for arg in args:
1110 if arg is None:
1111 break
1112 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001113 raise NbiException(
1114 "URL contains unexpected extra items '{}'".format(arg),
1115 HTTPStatus.METHOD_NOT_ALLOWED,
1116 )
tiernof27c79b2018-03-12 17:08:42 +01001117
1118 if arg in reference:
1119 reference = reference[arg]
1120 elif "<ID>" in reference:
1121 reference = reference["<ID>"]
1122 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001123 # if there is content
1124 if reference["*"]:
1125 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001126 break
1127 else:
garciadeblas4568a372021-03-24 09:19:48 +01001128 raise NbiException(
1129 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1130 )
tiernof27c79b2018-03-12 17:08:42 +01001131 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001132 raise NbiException(
1133 "Method {} not supported yet for this URL".format(method),
1134 HTTPStatus.NOT_IMPLEMENTED,
1135 )
tierno2236d202018-05-16 19:05:16 +02001136 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001137 raise NbiException(
1138 "Method {} not supported for this URL".format(method),
1139 HTTPStatus.METHOD_NOT_ALLOWED,
1140 )
tierno701018c2019-06-25 11:13:14 +00001141 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001142
1143 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001144 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001145 """
1146 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001147 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001148 :param version:
tiernob24258a2018-10-04 18:39:49 +02001149 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001150 :param id:
1151 :return: None
1152 """
1153 # 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 +01001154 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1155 main_topic, version, topic, id
1156 )
tiernof27c79b2018-03-12 17:08:42 +01001157 return
1158
tierno65ca36d2019-02-12 19:27:52 +01001159 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001160 def _extract_query_string_operations(kwargs, method):
1161 """
1162
1163 :param kwargs:
1164 :return:
1165 """
1166 query_string_operations = []
1167 if kwargs:
1168 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1169 if qs in kwargs and kwargs[qs].lower() != "false":
1170 query_string_operations.append(qs.lower() + ":" + method.lower())
1171 return query_string_operations
1172
1173 @staticmethod
1174 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001175 """
1176 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1177 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001178 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001179 :param kwargs: query string input.
1180 :param method: http method: GET, POSST, PUT, ...
1181 :param _id:
1182 :return: admin_query dictionary with keys:
1183 public: True, False or None
1184 force: True or False
1185 project_id: tuple with projects used for accessing an element
1186 set_project: tuple with projects that a created element will belong to
1187 method: show, list, delete, write
1188 """
garciadeblas4568a372021-03-24 09:19:48 +01001189 admin_query = {
1190 "force": False,
1191 "project_id": (token_info["project_id"],),
1192 "username": token_info["username"],
1193 "admin": token_info["admin"],
1194 "public": None,
1195 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1196 }
tierno65ca36d2019-02-12 19:27:52 +01001197 if kwargs:
1198 # FORCE
1199 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001200 if (
1201 kwargs["FORCE"].lower() != "false"
1202 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001203 admin_query["force"] = True
1204 del kwargs["FORCE"]
1205 # PUBLIC
1206 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001207 if (
1208 kwargs["PUBLIC"].lower() != "false"
1209 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001210 admin_query["public"] = True
1211 else:
1212 admin_query["public"] = False
1213 del kwargs["PUBLIC"]
1214 # ADMIN
1215 if "ADMIN" in kwargs:
1216 behave_as = kwargs.pop("ADMIN")
1217 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001218 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001219 raise NbiException(
1220 "Only admin projects can use 'ADMIN' query string",
1221 HTTPStatus.UNAUTHORIZED,
1222 )
1223 if (
1224 not behave_as or behave_as.lower() == "true"
1225 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001226 admin_query["project_id"] = ()
1227 elif isinstance(behave_as, (list, tuple)):
1228 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001229 else: # isinstance(behave_as, str)
1230 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001231 if "SET_PROJECT" in kwargs:
1232 set_project = kwargs.pop("SET_PROJECT")
1233 if not set_project:
1234 admin_query["set_project"] = list(admin_query["project_id"])
1235 else:
1236 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001237 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001238 if admin_query["project_id"]:
1239 for p in set_project:
1240 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001241 raise NbiException(
1242 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1243 "'ADMIN='{p}'".format(p=p),
1244 HTTPStatus.UNAUTHORIZED,
1245 )
tierno65ca36d2019-02-12 19:27:52 +01001246 admin_query["set_project"] = set_project
1247
1248 # PROJECT_READ
1249 # if "PROJECT_READ" in kwargs:
1250 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001251 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001252 if method == "GET":
1253 if _id:
1254 admin_query["method"] = "show"
1255 else:
1256 admin_query["method"] = "list"
1257 elif method == "DELETE":
1258 admin_query["method"] = "delete"
1259 else:
1260 admin_query["method"] = "write"
1261 return admin_query
1262
tiernoc94c3df2018-02-09 15:38:54 +01001263 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001264 def default(
1265 self,
1266 main_topic=None,
1267 version=None,
1268 topic=None,
1269 _id=None,
1270 item=None,
1271 *args,
1272 **kwargs
1273 ):
tierno701018c2019-06-25 11:13:14 +00001274 token_info = None
tiernof27c79b2018-03-12 17:08:42 +01001275 outdata = None
1276 _format = None
tierno0f98af52018-03-19 10:28:22 +01001277 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001278 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001279 rollback = []
tierno701018c2019-06-25 11:13:14 +00001280 engine_session = None
tiernoc94c3df2018-02-09 15:38:54 +01001281 try:
tiernob24258a2018-10-04 18:39:49 +02001282 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001283 raise NbiException(
1284 "URL must contain at least 'main_topic/version/topic'",
1285 HTTPStatus.METHOD_NOT_ALLOWED,
1286 )
1287 if main_topic not in (
1288 "admin",
1289 "vnfpkgm",
1290 "nsd",
1291 "nslcm",
1292 "pdu",
1293 "nst",
1294 "nsilcm",
1295 "nspm",
1296 ):
1297 raise NbiException(
1298 "URL main_topic '{}' not supported".format(main_topic),
1299 HTTPStatus.METHOD_NOT_ALLOWED,
1300 )
1301 if version != "v1":
1302 raise NbiException(
1303 "URL version '{}' not supported".format(version),
1304 HTTPStatus.METHOD_NOT_ALLOWED,
1305 )
tiernoc94c3df2018-02-09 15:38:54 +01001306
garciadeblas4568a372021-03-24 09:19:48 +01001307 if (
1308 kwargs
1309 and "METHOD" in kwargs
1310 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1311 ):
tiernof27c79b2018-03-12 17:08:42 +01001312 method = kwargs.pop("METHOD")
1313 else:
1314 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001315
garciadeblas4568a372021-03-24 09:19:48 +01001316 role_permission = self._check_valid_url_method(
1317 method, main_topic, version, topic, _id, item, *args
1318 )
1319 query_string_operations = self._extract_query_string_operations(
1320 kwargs, method
1321 )
tiernob24258a2018-10-04 18:39:49 +02001322 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001323 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001324 token_info = self.authenticator.authorize(
1325 role_permission, query_string_operations, _id
1326 )
tierno12eac3c2020-03-19 23:22:08 +00001327 if main_topic == "admin" and topic == "domains":
1328 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001329 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001330 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001331 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301332
vijay.r35ef2f72019-04-30 17:55:49 +05301333 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001334 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001335
tiernob24258a2018-10-04 18:39:49 +02001336 if main_topic == "nsd":
1337 engine_topic = "nsds"
1338 elif main_topic == "vnfpkgm":
1339 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001340 if topic == "vnfpkg_op_occs":
1341 engine_topic = "vnfpkgops"
1342 if topic == "vnf_packages" and item == "action":
1343 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001344 elif main_topic == "nslcm":
1345 engine_topic = "nsrs"
1346 if topic == "ns_lcm_op_occs":
1347 engine_topic = "nslcmops"
1348 if topic == "vnfrs" or topic == "vnf_instances":
1349 engine_topic = "vnfrs"
garciadeblas9750c5a2018-10-15 16:20:35 +02001350 elif main_topic == "nst":
1351 engine_topic = "nsts"
1352 elif main_topic == "nsilcm":
1353 engine_topic = "nsis"
1354 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001355 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001356 elif main_topic == "pdu":
1357 engine_topic = "pdus"
garciadeblas4568a372021-03-24 09:19:48 +01001358 if (
1359 engine_topic == "vims"
1360 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001361 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001362
preethika.p329b8182020-04-22 12:25:39 +05301363 if topic == "subscriptions":
1364 engine_topic = main_topic + "_" + topic
1365
tiernoc94c3df2018-02-09 15:38:54 +01001366 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001367 if item in (
1368 "nsd_content",
1369 "package_content",
1370 "artifacts",
1371 "vnfd",
1372 "nsd",
1373 "nst",
1374 "nst_content",
1375 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001376 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001377 path = "$DESCRIPTOR"
1378 elif args:
1379 path = args
tiernob24258a2018-10-04 18:39:49 +02001380 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001381 path = ()
1382 else:
1383 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001384 file, _format = self.engine.get_file(
1385 engine_session,
1386 engine_topic,
1387 _id,
1388 path,
1389 cherrypy.request.headers.get("Accept"),
1390 )
tiernof27c79b2018-03-12 17:08:42 +01001391 outdata = file
1392 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001393 outdata = self.engine.get_item_list(
1394 engine_session, engine_topic, kwargs, api_req=True
1395 )
tiernoc94c3df2018-02-09 15:38:54 +01001396 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301397 if item == "reports":
1398 # TODO check that project_id (_id in this context) has permissions
1399 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301400 filter_q = None
1401 if "vcaStatusRefresh" in kwargs:
1402 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
1403 outdata = self.engine.get_item(engine_session, engine_topic, _id, filter_q, True)
delacruzramo271d2002019-12-02 21:00:37 +01001404
tiernof27c79b2018-03-12 17:08:42 +01001405 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001406 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001407 if topic in (
1408 "ns_descriptors_content",
1409 "vnf_packages_content",
1410 "netslice_templates_content",
1411 ):
tiernof27c79b2018-03-12 17:08:42 +01001412 _id = cherrypy.request.headers.get("Transaction-Id")
1413 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001414 _id, _ = self.engine.new_item(
1415 rollback,
1416 engine_session,
1417 engine_topic,
1418 {},
1419 None,
1420 cherrypy.request.headers,
1421 )
1422 completed = self.engine.upload_content(
1423 engine_session,
1424 engine_topic,
1425 _id,
1426 indata,
1427 kwargs,
1428 cherrypy.request.headers,
1429 )
tiernof27c79b2018-03-12 17:08:42 +01001430 if completed:
tiernob24258a2018-10-04 18:39:49 +02001431 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001432 else:
1433 cherrypy.response.headers["Transaction-Id"] = _id
1434 outdata = {"id": _id}
tiernob24258a2018-10-04 18:39:49 +02001435 elif topic == "ns_instances_content":
1436 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001437 _id, _ = self.engine.new_item(
1438 rollback, engine_session, engine_topic, indata, kwargs
1439 )
tiernob24258a2018-10-04 18:39:49 +02001440 # creates nslcmop
1441 indata["lcmOperationType"] = "instantiate"
1442 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001443 nslcmop_id, _ = self.engine.new_item(
1444 rollback, engine_session, "nslcmops", indata, None
1445 )
tiernob24258a2018-10-04 18:39:49 +02001446 self._set_location_header(main_topic, version, topic, _id)
kuuse078f55e2019-05-16 19:24:21 +02001447 outdata = {"id": _id, "nslcmop_id": nslcmop_id}
tiernob24258a2018-10-04 18:39:49 +02001448 elif topic == "ns_instances" and item:
1449 indata["lcmOperationType"] = item
1450 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001451 _id, _ = self.engine.new_item(
1452 rollback, engine_session, "nslcmops", indata, kwargs
1453 )
1454 self._set_location_header(
1455 main_topic, version, "ns_lcm_op_occs", _id
1456 )
tierno65acb4d2018-04-06 16:42:40 +02001457 outdata = {"id": _id}
1458 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001459 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001460 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001461 _id, _ = self.engine.new_item(
1462 rollback, engine_session, engine_topic, indata, kwargs
1463 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001464 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001465 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001466 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001467 nsilcmop_id, _ = self.engine.new_item(
1468 rollback, engine_session, "nsilcmops", indata, kwargs
1469 )
kuuse078f55e2019-05-16 19:24:21 +02001470 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001471 elif topic == "netslice_instances" and item:
1472 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001473 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001474 _id, _ = self.engine.new_item(
1475 rollback, engine_session, "nsilcmops", indata, kwargs
1476 )
1477 self._set_location_header(
1478 main_topic, version, "nsi_lcm_op_occs", _id
1479 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001480 outdata = {"id": _id}
1481 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001482 elif topic == "vnf_packages" and item == "action":
1483 indata["lcmOperationType"] = item
1484 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001485 _id, _ = self.engine.new_item(
1486 rollback, engine_session, "vnfpkgops", indata, kwargs
1487 )
1488 self._set_location_header(
1489 main_topic, version, "vnfpkg_op_occs", _id
1490 )
delacruzramo271d2002019-12-02 21:00:37 +01001491 outdata = {"id": _id}
1492 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301493 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001494 _id, _ = self.engine.new_item(
1495 rollback, engine_session, engine_topic, indata, kwargs
1496 )
preethika.p329b8182020-04-22 12:25:39 +05301497 self._set_location_header(main_topic, version, topic, _id)
1498 link = {}
1499 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001500 outdata = {
1501 "id": _id,
1502 "filter": indata["filter"],
1503 "callbackUri": indata["CallbackUri"],
1504 "_links": link,
1505 }
preethika.p329b8182020-04-22 12:25:39 +05301506 cherrypy.response.status = HTTPStatus.CREATED.value
tiernof27c79b2018-03-12 17:08:42 +01001507 else:
garciadeblas4568a372021-03-24 09:19:48 +01001508 _id, op_id = self.engine.new_item(
1509 rollback,
1510 engine_session,
1511 engine_topic,
1512 indata,
1513 kwargs,
1514 cherrypy.request.headers,
1515 )
tiernob24258a2018-10-04 18:39:49 +02001516 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001517 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00001518 if op_id:
1519 outdata["op_id"] = op_id
1520 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001521 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02001522
tiernoc94c3df2018-02-09 15:38:54 +01001523 elif method == "DELETE":
1524 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001525 outdata = self.engine.del_item_list(
1526 engine_session, engine_topic, kwargs
1527 )
tierno09c073e2018-04-26 13:36:48 +02001528 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01001529 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00001530 # for NS NSI generate an operation
1531 op_id = None
tierno701018c2019-06-25 11:13:14 +00001532 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02001533 nslcmop_desc = {
1534 "lcmOperationType": "terminate",
1535 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001536 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02001537 }
garciadeblas4568a372021-03-24 09:19:48 +01001538 op_id, _ = self.engine.new_item(
1539 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
1540 )
tierno22577432020-04-08 15:16:57 +00001541 if op_id:
1542 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001543 elif (
1544 topic == "netslice_instances_content"
1545 and not engine_session["force"]
1546 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001547 nsilcmop_desc = {
1548 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02001549 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001550 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02001551 }
garciadeblas4568a372021-03-24 09:19:48 +01001552 op_id, _ = self.engine.new_item(
1553 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
1554 )
tierno22577432020-04-08 15:16:57 +00001555 if op_id:
1556 outdata = {"_id": op_id}
1557 # if there is not any deletion in process, delete
1558 if not op_id:
1559 op_id = self.engine.del_item(engine_session, engine_topic, _id)
1560 if op_id:
1561 outdata = {"op_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001562 cherrypy.response.status = (
1563 HTTPStatus.ACCEPTED.value
1564 if op_id
1565 else HTTPStatus.NO_CONTENT.value
1566 )
tierno09c073e2018-04-26 13:36:48 +02001567
tierno7ae10112018-05-18 14:36:02 +02001568 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00001569 op_id = None
tierno701018c2019-06-25 11:13:14 +00001570 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01001571 raise NbiException(
1572 "Nothing to update. Provide payload and/or query string",
1573 HTTPStatus.BAD_REQUEST,
1574 )
1575 if (
1576 item in ("nsd_content", "package_content", "nst_content")
1577 and method == "PUT"
1578 ):
1579 completed = self.engine.upload_content(
1580 engine_session,
1581 engine_topic,
1582 _id,
1583 indata,
1584 kwargs,
1585 cherrypy.request.headers,
1586 )
tiernof27c79b2018-03-12 17:08:42 +01001587 if not completed:
1588 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01001589 else:
garciadeblas4568a372021-03-24 09:19:48 +01001590 op_id = self.engine.edit_item(
1591 engine_session, engine_topic, _id, indata, kwargs
1592 )
tiernobdebce92019-07-01 15:36:49 +00001593
1594 if op_id:
1595 cherrypy.response.status = HTTPStatus.ACCEPTED.value
1596 outdata = {"op_id": op_id}
1597 else:
1598 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
1599 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01001600 else:
garciadeblas4568a372021-03-24 09:19:48 +01001601 raise NbiException(
1602 "Method {} not allowed".format(method),
1603 HTTPStatus.METHOD_NOT_ALLOWED,
1604 )
tiernoa6bb45d2019-06-14 09:45:39 +00001605
1606 # if Role information changes, it is needed to reload the information of roles
1607 if topic == "roles" and method != "GET":
1608 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01001609
garciadeblas4568a372021-03-24 09:19:48 +01001610 if (
1611 topic == "projects"
1612 and method == "DELETE"
1613 or topic in ["users", "roles"]
1614 and method in ["PUT", "PATCH", "DELETE"]
1615 ):
delacruzramoad682a52019-12-10 16:26:34 +01001616 self.authenticator.remove_token_from_cache()
1617
tierno701018c2019-06-25 11:13:14 +00001618 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02001619 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01001620 if isinstance(
1621 e,
1622 (
1623 NbiException,
1624 EngineException,
1625 DbException,
1626 FsException,
1627 MsgException,
1628 AuthException,
1629 ValidationError,
1630 AuthconnException,
1631 ),
1632 ):
tiernob24258a2018-10-04 18:39:49 +02001633 http_code_value = cherrypy.response.status = e.http_code.value
1634 http_code_name = e.http_code.name
1635 cherrypy.log("Exception {}".format(e))
1636 else:
garciadeblas4568a372021-03-24 09:19:48 +01001637 http_code_value = (
1638 cherrypy.response.status
1639 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01001640 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02001641 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02001642 if hasattr(outdata, "close"): # is an open file
1643 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02001644 error_text = str(e)
1645 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02001646 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02001647 try:
tiernocc103432018-10-19 14:10:35 +02001648 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01001649 self.engine.db.set_one(
1650 rollback_item["topic"],
1651 {"_id": rollback_item["_id"]},
1652 rollback_item["content"],
1653 fail_on_empty=False,
1654 )
preethika.p329b8182020-04-22 12:25:39 +05301655 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01001656 self.engine.db.del_list(
1657 rollback_item["topic"],
1658 rollback_item["filter"],
1659 fail_on_empty=False,
1660 )
tiernocc103432018-10-19 14:10:35 +02001661 else:
garciadeblas4568a372021-03-24 09:19:48 +01001662 self.engine.db.del_one(
1663 rollback_item["topic"],
1664 {"_id": rollback_item["_id"]},
1665 fail_on_empty=False,
1666 )
tierno3ace63c2018-05-03 17:51:43 +02001667 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01001668 rollback_error_text = "Rollback Exception {}: {}".format(
1669 rollback_item, e2
1670 )
tiernob24258a2018-10-04 18:39:49 +02001671 cherrypy.log(rollback_error_text)
1672 error_text += ". " + rollback_error_text
1673 # if isinstance(e, MsgException):
1674 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
1675 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01001676 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02001677 "code": http_code_name,
1678 "status": http_code_value,
1679 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01001680 }
tierno701018c2019-06-25 11:13:14 +00001681 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001682 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00001683 finally:
1684 if token_info:
1685 self._format_login(token_info)
1686 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
1687 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
1688 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01001689 cherrypy.request.login += ";{}={}".format(
1690 logging_id, outdata[logging_id][:36]
1691 )
tiernoc94c3df2018-02-09 15:38:54 +01001692
1693
tiernoc94c3df2018-02-09 15:38:54 +01001694def _start_service():
1695 """
1696 Callback function called when cherrypy.engine starts
1697 Override configuration with env variables
1698 Set database, storage, message configuration
1699 Init database with admin/admin user password
1700 """
tierno932499c2019-01-28 17:28:10 +00001701 global nbi_server
1702 global subscription_thread
tiernoc94c3df2018-02-09 15:38:54 +01001703 cherrypy.log.error("Starting osm_nbi")
1704 # update general cherrypy configuration
1705 update_dict = {}
1706
garciadeblas4568a372021-03-24 09:19:48 +01001707 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01001708 for k, v in environ.items():
1709 if not k.startswith("OSMNBI_"):
1710 continue
tiernoe1281182018-05-22 12:24:36 +02001711 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01001712 if not k2:
1713 continue
1714 try:
1715 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01001716 if k == "OSMNBI_STATIC_DIR":
1717 engine_config["/static"]["tools.staticdir.dir"] = v
1718 engine_config["/static"]["tools.staticdir.on"] = True
1719 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
1720 update_dict["server.socket_port"] = int(v)
1721 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
1722 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02001723 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01001724 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001725 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02001726 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001727 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01001728 engine_config[k1][k2] = int(v)
1729 else:
1730 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001731
tiernoc94c3df2018-02-09 15:38:54 +01001732 except ValueError as e:
1733 cherrypy.log.error("Ignoring environ '{}': " + str(e))
1734 except Exception as e:
1735 cherrypy.log.warn("skipping environ '{}' on exception '{}'".format(k, e))
1736
1737 if update_dict:
1738 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02001739 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001740
1741 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01001742 log_format_simple = (
1743 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
1744 )
1745 log_formatter_simple = logging.Formatter(
1746 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
1747 )
tiernoc94c3df2018-02-09 15:38:54 +01001748 logger_server = logging.getLogger("cherrypy.error")
1749 logger_access = logging.getLogger("cherrypy.access")
1750 logger_cherry = logging.getLogger("cherrypy")
1751 logger_nbi = logging.getLogger("nbi")
1752
tiernof5298be2018-05-16 14:43:57 +02001753 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01001754 file_handler = logging.handlers.RotatingFileHandler(
1755 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
1756 )
tiernoc94c3df2018-02-09 15:38:54 +01001757 file_handler.setFormatter(log_formatter_simple)
1758 logger_cherry.addHandler(file_handler)
1759 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02001760 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01001761 for format_, logger in {
1762 "nbi.server %(filename)s:%(lineno)s": logger_server,
1763 "nbi.access %(filename)s:%(lineno)s": logger_access,
1764 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
1765 }.items():
tiernob24258a2018-10-04 18:39:49 +02001766 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01001767 log_formatter_cherry = logging.Formatter(
1768 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
1769 )
tiernob24258a2018-10-04 18:39:49 +02001770 str_handler = logging.StreamHandler()
1771 str_handler.setFormatter(log_formatter_cherry)
1772 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01001773
tiernof5298be2018-05-16 14:43:57 +02001774 if engine_config["global"].get("log.level"):
1775 logger_cherry.setLevel(engine_config["global"]["log.level"])
1776 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01001777
1778 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01001779 for k1, logname in {
1780 "message": "nbi.msg",
1781 "database": "nbi.db",
1782 "storage": "nbi.fs",
1783 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01001784 engine_config[k1]["logger_name"] = logname
1785 logger_module = logging.getLogger(logname)
1786 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01001787 file_handler = logging.handlers.RotatingFileHandler(
1788 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
1789 )
tiernoc94c3df2018-02-09 15:38:54 +01001790 file_handler.setFormatter(log_formatter_simple)
1791 logger_module.addHandler(file_handler)
1792 if "loglevel" in engine_config[k1]:
1793 logger_module.setLevel(engine_config[k1]["loglevel"])
1794 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01001795 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
1796 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
1797 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
1798 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
1799 target_version=auth_database_version
1800 )
tiernobee508e2019-01-21 11:21:49 +00001801
tierno932499c2019-01-28 17:28:10 +00001802 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01001803 subscription_thread = SubscriptionThread(
1804 config=engine_config, engine=nbi_server.engine
1805 )
tierno932499c2019-01-28 17:28:10 +00001806 subscription_thread.start()
1807 # Do not capture except SubscriptionException
1808
tiernob2e48bd2020-02-04 15:47:18 +00001809 backend = engine_config["authentication"]["backend"]
garciadeblas4568a372021-03-24 09:19:48 +01001810 cherrypy.log.error(
1811 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
1812 nbi_version, nbi_version_date, backend
1813 )
1814 )
tiernoc94c3df2018-02-09 15:38:54 +01001815
1816
1817def _stop_service():
1818 """
1819 Callback function called when cherrypy.engine stops
1820 TODO: Ending database connections.
1821 """
tierno932499c2019-01-28 17:28:10 +00001822 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01001823 if subscription_thread:
1824 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00001825 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01001826 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01001827 cherrypy.log.error("Stopping osm_nbi")
1828
tierno2236d202018-05-16 19:05:16 +02001829
tiernof5298be2018-05-16 14:43:57 +02001830def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00001831 global nbi_server
tiernoc94c3df2018-02-09 15:38:54 +01001832 # conf = {
1833 # '/': {
1834 # #'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
1835 # 'tools.sessions.on': True,
1836 # 'tools.response_headers.on': True,
1837 # # 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
1838 # }
1839 # }
1840 # cherrypy.Server.ssl_module = 'builtin'
1841 # cherrypy.Server.ssl_certificate = "http/cert.pem"
1842 # cherrypy.Server.ssl_private_key = "http/privkey.pem"
1843 # cherrypy.Server.thread_pool = 10
1844 # cherrypy.config.update({'Server.socket_port': config["port"], 'Server.socket_host': config["host"]})
1845
1846 # cherrypy.config.update({'tools.auth_basic.on': True,
1847 # 'tools.auth_basic.realm': 'localhost',
1848 # 'tools.auth_basic.checkpassword': validate_password})
tierno932499c2019-01-28 17:28:10 +00001849 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01001850 cherrypy.engine.subscribe("start", _start_service)
1851 cherrypy.engine.subscribe("stop", _stop_service)
1852 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02001853
1854
1855def usage():
garciadeblas4568a372021-03-24 09:19:48 +01001856 print(
1857 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02001858 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
1859 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01001860 """.format(
1861 sys.argv[0]
1862 )
1863 )
tierno2236d202018-05-16 19:05:16 +02001864 # --log-socket-host HOST: send logs to this host")
1865 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01001866
1867
garciadeblas4568a372021-03-24 09:19:48 +01001868if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02001869 try:
1870 # load parameters and configuration
1871 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
1872 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
1873 config_file = None
1874 for o, a in opts:
1875 if o in ("-h", "--help"):
1876 usage()
1877 sys.exit()
1878 elif o in ("-c", "--config"):
1879 config_file = a
1880 # elif o == "--log-socket-port":
1881 # log_socket_port = a
1882 # elif o == "--log-socket-host":
1883 # log_socket_host = a
1884 # elif o == "--log-file":
1885 # log_file = a
1886 else:
1887 assert False, "Unhandled option"
1888 if config_file:
1889 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01001890 print(
1891 "configuration file '{}' that not exist".format(config_file),
1892 file=sys.stderr,
1893 )
tiernof5298be2018-05-16 14:43:57 +02001894 exit(1)
1895 else:
garciadeblas4568a372021-03-24 09:19:48 +01001896 for config_file in (
1897 __file__[: __file__.rfind(".")] + ".cfg",
1898 "./nbi.cfg",
1899 "/etc/osm/nbi.cfg",
1900 ):
tiernof5298be2018-05-16 14:43:57 +02001901 if path.isfile(config_file):
1902 break
1903 else:
garciadeblas4568a372021-03-24 09:19:48 +01001904 print(
1905 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
1906 file=sys.stderr,
1907 )
tiernof5298be2018-05-16 14:43:57 +02001908 exit(1)
1909 nbi(config_file)
1910 except getopt.GetoptError as e:
1911 print(str(e), file=sys.stderr)
1912 # usage()
1913 exit(1)