blob: 67cf58b72c570dbca08fa185313e89f30d0e5bd8 [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)
956
957 # cherrypy.response.cookie["Authorization"] = outdata["id"]
958 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
959 elif method == "DELETE":
960 if not token_id and "id" in kwargs:
961 token_id = kwargs["id"]
962 elif not token_id:
963 token_info = self.authenticator.authorize()
964 # for logging
965 self._format_login(token_info)
966 token_id = token_info["_id"]
967 outdata = self.authenticator.del_token(token_id)
968 token_info = None
garciadeblas4568a372021-03-24 09:19:48 +0100969 cherrypy.session["Authorization"] = "logout"
tiernoa5035702019-07-29 08:54:42 +0000970 # cherrypy.response.cookie["Authorization"] = token_id
971 # cherrypy.response.cookie["Authorization"]['expires'] = 0
972 else:
garciadeblas4568a372021-03-24 09:19:48 +0100973 raise NbiException(
974 "Method {} not allowed for token".format(method),
975 HTTPStatus.METHOD_NOT_ALLOWED,
976 )
tiernoa5035702019-07-29 08:54:42 +0000977 return self._format_out(outdata, token_info)
tiernoc94c3df2018-02-09 15:38:54 +0100978
979 @cherrypy.expose
980 def test(self, *args, **kwargs):
garciadeblas4568a372021-03-24 09:19:48 +0100981 if not cherrypy.config.get("server.enable_test") or (
982 isinstance(cherrypy.config["server.enable_test"], str)
983 and cherrypy.config["server.enable_test"].lower() == "false"
984 ):
tierno4836bac2020-01-15 14:41:48 +0000985 cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value
986 return "test URL is disabled"
tiernoc94c3df2018-02-09 15:38:54 +0100987 thread_info = None
tiernof27c79b2018-03-12 17:08:42 +0100988 if args and args[0] == "help":
garciadeblas4568a372021-03-24 09:19:48 +0100989 return (
990 "<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n"
991 "sleep/<time>\nmessage/topic\n</pre></html>"
992 )
tiernof27c79b2018-03-12 17:08:42 +0100993
994 elif args and args[0] == "init":
tiernoc94c3df2018-02-09 15:38:54 +0100995 try:
996 # self.engine.load_dbase(cherrypy.request.app.config)
997 self.engine.create_admin()
998 return "Done. User 'admin', password 'admin' created"
999 except Exception:
1000 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1001 return self._format_out("Database already initialized")
tiernof27c79b2018-03-12 17:08:42 +01001002 elif args and args[0] == "file":
garciadeblas4568a372021-03-24 09:19:48 +01001003 return cherrypy.lib.static.serve_file(
1004 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1],
1005 "text/plain",
1006 "attachment",
1007 )
tiernof27c79b2018-03-12 17:08:42 +01001008 elif args and args[0] == "file2":
garciadeblas4568a372021-03-24 09:19:48 +01001009 f_path = (
1010 cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1]
1011 )
tiernof27c79b2018-03-12 17:08:42 +01001012 f = open(f_path, "r")
1013 cherrypy.response.headers["Content-type"] = "text/plain"
tiernof27c79b2018-03-12 17:08:42 +01001014 return f
tierno55945e72018-04-06 16:40:27 +02001015
tiernof27c79b2018-03-12 17:08:42 +01001016 elif len(args) == 2 and args[0] == "db-clear":
tiernof717cbe2018-12-03 16:35:42 +00001017 deleted_info = self.engine.db.del_list(args[1], kwargs)
1018 return "{} {} deleted\n".format(deleted_info["deleted"], args[1])
1019 elif len(args) and args[0] == "fs-clear":
1020 if len(args) >= 2:
1021 folders = (args[1],)
1022 else:
1023 folders = self.engine.fs.dir_ls(".")
1024 for folder in folders:
1025 self.engine.fs.file_delete(folder)
1026 return ",".join(folders) + " folders deleted\n"
tiernoc94c3df2018-02-09 15:38:54 +01001027 elif args and args[0] == "login":
1028 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001029 cherrypy.response.headers[
1030 "WWW-Authenticate"
1031 ] = 'Basic realm="Access to OSM site", charset="UTF-8"'
tiernoc94c3df2018-02-09 15:38:54 +01001032 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1033 elif args and args[0] == "login2":
1034 if not cherrypy.request.headers.get("Authorization"):
garciadeblas4568a372021-03-24 09:19:48 +01001035 cherrypy.response.headers[
1036 "WWW-Authenticate"
1037 ] = 'Bearer realm="Access to OSM site"'
tiernoc94c3df2018-02-09 15:38:54 +01001038 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
1039 elif args and args[0] == "sleep":
1040 sleep_time = 5
1041 try:
1042 sleep_time = int(args[1])
1043 except Exception:
1044 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
1045 return self._format_out("Database already initialized")
1046 thread_info = cherrypy.thread_data
1047 print(thread_info)
1048 time.sleep(sleep_time)
1049 # thread_info
1050 elif len(args) >= 2 and args[0] == "message":
tiernob24258a2018-10-04 18:39:49 +02001051 main_topic = args[1]
1052 return_text = "<html><pre>{} ->\n".format(main_topic)
tiernoc94c3df2018-02-09 15:38:54 +01001053 try:
garciadeblas4568a372021-03-24 09:19:48 +01001054 if cherrypy.request.method == "POST":
delacruzramob19cadc2019-10-08 10:18:02 +02001055 to_send = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader)
tierno55945e72018-04-06 16:40:27 +02001056 for k, v in to_send.items():
tiernob24258a2018-10-04 18:39:49 +02001057 self.engine.msg.write(main_topic, k, v)
tierno55945e72018-04-06 16:40:27 +02001058 return_text += " {}: {}\n".format(k, v)
garciadeblas4568a372021-03-24 09:19:48 +01001059 elif cherrypy.request.method == "GET":
tierno55945e72018-04-06 16:40:27 +02001060 for k, v in kwargs.items():
tiernof1509b22020-05-12 14:32:37 +00001061 v_dict = yaml.load(v, Loader=yaml.SafeLoader)
1062 self.engine.msg.write(main_topic, k, v_dict)
1063 return_text += " {}: {}\n".format(k, v_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001064 except Exception as e:
tierno55945e72018-04-06 16:40:27 +02001065 return_text += "Error: " + str(e)
1066 return_text += "</pre></html>\n"
1067 return return_text
tiernoc94c3df2018-02-09 15:38:54 +01001068
1069 return_text = (
garciadeblas4568a372021-03-24 09:19:48 +01001070 "<html><pre>\nheaders:\n args: {}\n".format(args)
1071 + " kwargs: {}\n".format(kwargs)
1072 + " headers: {}\n".format(cherrypy.request.headers)
1073 + " path_info: {}\n".format(cherrypy.request.path_info)
1074 + " query_string: {}\n".format(cherrypy.request.query_string)
1075 + " session: {}\n".format(cherrypy.session)
1076 + " cookie: {}\n".format(cherrypy.request.cookie)
1077 + " method: {}\n".format(cherrypy.request.method)
1078 + " session: {}\n".format(cherrypy.session.get("fieldname"))
1079 + " body:\n"
1080 )
tiernoc94c3df2018-02-09 15:38:54 +01001081 return_text += " length: {}\n".format(cherrypy.request.body.length)
1082 if cherrypy.request.body.length:
1083 return_text += " content: {}\n".format(
garciadeblas4568a372021-03-24 09:19:48 +01001084 str(
1085 cherrypy.request.body.read(
1086 int(cherrypy.request.headers.get("Content-Length", 0))
1087 )
1088 )
1089 )
tiernoc94c3df2018-02-09 15:38:54 +01001090 if thread_info:
1091 return_text += "thread: {}\n".format(thread_info)
1092 return_text += "</pre></html>"
1093 return return_text
1094
tierno701018c2019-06-25 11:13:14 +00001095 @staticmethod
1096 def _check_valid_url_method(method, *args):
tiernof27c79b2018-03-12 17:08:42 +01001097 if len(args) < 3:
garciadeblas4568a372021-03-24 09:19:48 +01001098 raise NbiException(
1099 "URL must contain at least 'main_topic/version/topic'",
1100 HTTPStatus.METHOD_NOT_ALLOWED,
1101 )
tiernof27c79b2018-03-12 17:08:42 +01001102
tierno701018c2019-06-25 11:13:14 +00001103 reference = valid_url_methods
tiernof27c79b2018-03-12 17:08:42 +01001104 for arg in args:
1105 if arg is None:
1106 break
1107 if not isinstance(reference, dict):
garciadeblas4568a372021-03-24 09:19:48 +01001108 raise NbiException(
1109 "URL contains unexpected extra items '{}'".format(arg),
1110 HTTPStatus.METHOD_NOT_ALLOWED,
1111 )
tiernof27c79b2018-03-12 17:08:42 +01001112
1113 if arg in reference:
1114 reference = reference[arg]
1115 elif "<ID>" in reference:
1116 reference = reference["<ID>"]
1117 elif "*" in reference:
tierno74b53582020-06-18 10:52:37 +00001118 # if there is content
1119 if reference["*"]:
1120 reference = reference["*"]
tiernof27c79b2018-03-12 17:08:42 +01001121 break
1122 else:
garciadeblas4568a372021-03-24 09:19:48 +01001123 raise NbiException(
1124 "Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED
1125 )
tiernof27c79b2018-03-12 17:08:42 +01001126 if "TODO" in reference and method in reference["TODO"]:
garciadeblas4568a372021-03-24 09:19:48 +01001127 raise NbiException(
1128 "Method {} not supported yet for this URL".format(method),
1129 HTTPStatus.NOT_IMPLEMENTED,
1130 )
tierno2236d202018-05-16 19:05:16 +02001131 elif "METHODS" in reference and method not in reference["METHODS"]:
garciadeblas4568a372021-03-24 09:19:48 +01001132 raise NbiException(
1133 "Method {} not supported for this URL".format(method),
1134 HTTPStatus.METHOD_NOT_ALLOWED,
1135 )
tierno701018c2019-06-25 11:13:14 +00001136 return reference["ROLE_PERMISSION"] + method.lower()
tiernof27c79b2018-03-12 17:08:42 +01001137
1138 @staticmethod
tiernob24258a2018-10-04 18:39:49 +02001139 def _set_location_header(main_topic, version, topic, id):
tiernof27c79b2018-03-12 17:08:42 +01001140 """
1141 Insert response header Location with the URL of created item base on URL params
tiernob24258a2018-10-04 18:39:49 +02001142 :param main_topic:
tiernof27c79b2018-03-12 17:08:42 +01001143 :param version:
tiernob24258a2018-10-04 18:39:49 +02001144 :param topic:
tiernof27c79b2018-03-12 17:08:42 +01001145 :param id:
1146 :return: None
1147 """
1148 # 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 +01001149 cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format(
1150 main_topic, version, topic, id
1151 )
tiernof27c79b2018-03-12 17:08:42 +01001152 return
1153
tierno65ca36d2019-02-12 19:27:52 +01001154 @staticmethod
tierno701018c2019-06-25 11:13:14 +00001155 def _extract_query_string_operations(kwargs, method):
1156 """
1157
1158 :param kwargs:
1159 :return:
1160 """
1161 query_string_operations = []
1162 if kwargs:
1163 for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"):
1164 if qs in kwargs and kwargs[qs].lower() != "false":
1165 query_string_operations.append(qs.lower() + ":" + method.lower())
1166 return query_string_operations
1167
1168 @staticmethod
1169 def _manage_admin_query(token_info, kwargs, method, _id):
tierno65ca36d2019-02-12 19:27:52 +01001170 """
1171 Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT
1172 Check that users has rights to use them and returs the admin_query
tierno701018c2019-06-25 11:13:14 +00001173 :param token_info: token_info rights obtained by token
tierno65ca36d2019-02-12 19:27:52 +01001174 :param kwargs: query string input.
1175 :param method: http method: GET, POSST, PUT, ...
1176 :param _id:
1177 :return: admin_query dictionary with keys:
1178 public: True, False or None
1179 force: True or False
1180 project_id: tuple with projects used for accessing an element
1181 set_project: tuple with projects that a created element will belong to
1182 method: show, list, delete, write
1183 """
garciadeblas4568a372021-03-24 09:19:48 +01001184 admin_query = {
1185 "force": False,
1186 "project_id": (token_info["project_id"],),
1187 "username": token_info["username"],
1188 "admin": token_info["admin"],
1189 "public": None,
1190 "allow_show_user_project_role": token_info["allow_show_user_project_role"],
1191 }
tierno65ca36d2019-02-12 19:27:52 +01001192 if kwargs:
1193 # FORCE
1194 if "FORCE" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001195 if (
1196 kwargs["FORCE"].lower() != "false"
1197 ): # if None or True set force to True
tierno65ca36d2019-02-12 19:27:52 +01001198 admin_query["force"] = True
1199 del kwargs["FORCE"]
1200 # PUBLIC
1201 if "PUBLIC" in kwargs:
garciadeblas4568a372021-03-24 09:19:48 +01001202 if (
1203 kwargs["PUBLIC"].lower() != "false"
1204 ): # if None or True set public to True
tierno65ca36d2019-02-12 19:27:52 +01001205 admin_query["public"] = True
1206 else:
1207 admin_query["public"] = False
1208 del kwargs["PUBLIC"]
1209 # ADMIN
1210 if "ADMIN" in kwargs:
1211 behave_as = kwargs.pop("ADMIN")
1212 if behave_as.lower() != "false":
tierno701018c2019-06-25 11:13:14 +00001213 if not token_info["admin"]:
garciadeblas4568a372021-03-24 09:19:48 +01001214 raise NbiException(
1215 "Only admin projects can use 'ADMIN' query string",
1216 HTTPStatus.UNAUTHORIZED,
1217 )
1218 if (
1219 not behave_as or behave_as.lower() == "true"
1220 ): # convert True, None to empty list
tierno65ca36d2019-02-12 19:27:52 +01001221 admin_query["project_id"] = ()
1222 elif isinstance(behave_as, (list, tuple)):
1223 admin_query["project_id"] = behave_as
garciadeblas4568a372021-03-24 09:19:48 +01001224 else: # isinstance(behave_as, str)
1225 admin_query["project_id"] = (behave_as,)
tierno65ca36d2019-02-12 19:27:52 +01001226 if "SET_PROJECT" in kwargs:
1227 set_project = kwargs.pop("SET_PROJECT")
1228 if not set_project:
1229 admin_query["set_project"] = list(admin_query["project_id"])
1230 else:
1231 if isinstance(set_project, str):
garciadeblas4568a372021-03-24 09:19:48 +01001232 set_project = (set_project,)
tierno65ca36d2019-02-12 19:27:52 +01001233 if admin_query["project_id"]:
1234 for p in set_project:
1235 if p not in admin_query["project_id"]:
garciadeblas4568a372021-03-24 09:19:48 +01001236 raise NbiException(
1237 "Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or "
1238 "'ADMIN='{p}'".format(p=p),
1239 HTTPStatus.UNAUTHORIZED,
1240 )
tierno65ca36d2019-02-12 19:27:52 +01001241 admin_query["set_project"] = set_project
1242
1243 # PROJECT_READ
1244 # if "PROJECT_READ" in kwargs:
1245 # admin_query["project"] = kwargs.pop("project")
tierno701018c2019-06-25 11:13:14 +00001246 # if admin_query["project"] == token_info["project_id"]:
tierno65ca36d2019-02-12 19:27:52 +01001247 if method == "GET":
1248 if _id:
1249 admin_query["method"] = "show"
1250 else:
1251 admin_query["method"] = "list"
1252 elif method == "DELETE":
1253 admin_query["method"] = "delete"
1254 else:
1255 admin_query["method"] = "write"
1256 return admin_query
1257
tiernoc94c3df2018-02-09 15:38:54 +01001258 @cherrypy.expose
garciadeblas4568a372021-03-24 09:19:48 +01001259 def default(
1260 self,
1261 main_topic=None,
1262 version=None,
1263 topic=None,
1264 _id=None,
1265 item=None,
1266 *args,
1267 **kwargs
1268 ):
tierno701018c2019-06-25 11:13:14 +00001269 token_info = None
tiernof27c79b2018-03-12 17:08:42 +01001270 outdata = None
1271 _format = None
tierno0f98af52018-03-19 10:28:22 +01001272 method = "DONE"
tiernob24258a2018-10-04 18:39:49 +02001273 engine_topic = None
tiernodb9dc582018-06-20 17:27:29 +02001274 rollback = []
tierno701018c2019-06-25 11:13:14 +00001275 engine_session = None
tiernoc94c3df2018-02-09 15:38:54 +01001276 try:
tiernob24258a2018-10-04 18:39:49 +02001277 if not main_topic or not version or not topic:
garciadeblas4568a372021-03-24 09:19:48 +01001278 raise NbiException(
1279 "URL must contain at least 'main_topic/version/topic'",
1280 HTTPStatus.METHOD_NOT_ALLOWED,
1281 )
1282 if main_topic not in (
1283 "admin",
1284 "vnfpkgm",
1285 "nsd",
1286 "nslcm",
1287 "pdu",
1288 "nst",
1289 "nsilcm",
1290 "nspm",
1291 ):
1292 raise NbiException(
1293 "URL main_topic '{}' not supported".format(main_topic),
1294 HTTPStatus.METHOD_NOT_ALLOWED,
1295 )
1296 if version != "v1":
1297 raise NbiException(
1298 "URL version '{}' not supported".format(version),
1299 HTTPStatus.METHOD_NOT_ALLOWED,
1300 )
tiernoc94c3df2018-02-09 15:38:54 +01001301
garciadeblas4568a372021-03-24 09:19:48 +01001302 if (
1303 kwargs
1304 and "METHOD" in kwargs
1305 and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH")
1306 ):
tiernof27c79b2018-03-12 17:08:42 +01001307 method = kwargs.pop("METHOD")
1308 else:
1309 method = cherrypy.request.method
tierno65ca36d2019-02-12 19:27:52 +01001310
garciadeblas4568a372021-03-24 09:19:48 +01001311 role_permission = self._check_valid_url_method(
1312 method, main_topic, version, topic, _id, item, *args
1313 )
1314 query_string_operations = self._extract_query_string_operations(
1315 kwargs, method
1316 )
tiernob24258a2018-10-04 18:39:49 +02001317 if main_topic == "admin" and topic == "tokens":
tiernof27c79b2018-03-12 17:08:42 +01001318 return self.token(method, _id, kwargs)
garciadeblas4568a372021-03-24 09:19:48 +01001319 token_info = self.authenticator.authorize(
1320 role_permission, query_string_operations, _id
1321 )
tierno12eac3c2020-03-19 23:22:08 +00001322 if main_topic == "admin" and topic == "domains":
1323 return self.domain()
tierno701018c2019-06-25 11:13:14 +00001324 engine_session = self._manage_admin_query(token_info, kwargs, method, _id)
tiernof27c79b2018-03-12 17:08:42 +01001325 indata = self._format_in(kwargs)
tiernob24258a2018-10-04 18:39:49 +02001326 engine_topic = topic
preethika.p329b8182020-04-22 12:25:39 +05301327
vijay.r35ef2f72019-04-30 17:55:49 +05301328 if item and topic != "pm_jobs":
tiernob24258a2018-10-04 18:39:49 +02001329 engine_topic = item
tiernoc94c3df2018-02-09 15:38:54 +01001330
tiernob24258a2018-10-04 18:39:49 +02001331 if main_topic == "nsd":
1332 engine_topic = "nsds"
1333 elif main_topic == "vnfpkgm":
1334 engine_topic = "vnfds"
delacruzramo271d2002019-12-02 21:00:37 +01001335 if topic == "vnfpkg_op_occs":
1336 engine_topic = "vnfpkgops"
1337 if topic == "vnf_packages" and item == "action":
1338 engine_topic = "vnfpkgops"
tiernob24258a2018-10-04 18:39:49 +02001339 elif main_topic == "nslcm":
1340 engine_topic = "nsrs"
1341 if topic == "ns_lcm_op_occs":
1342 engine_topic = "nslcmops"
1343 if topic == "vnfrs" or topic == "vnf_instances":
1344 engine_topic = "vnfrs"
garciadeblas9750c5a2018-10-15 16:20:35 +02001345 elif main_topic == "nst":
1346 engine_topic = "nsts"
1347 elif main_topic == "nsilcm":
1348 engine_topic = "nsis"
1349 if topic == "nsi_lcm_op_occs":
delacruzramoc061f562019-04-05 11:00:02 +02001350 engine_topic = "nsilcmops"
tiernob24258a2018-10-04 18:39:49 +02001351 elif main_topic == "pdu":
1352 engine_topic = "pdus"
garciadeblas4568a372021-03-24 09:19:48 +01001353 if (
1354 engine_topic == "vims"
1355 ): # TODO this is for backward compatibility, it will be removed in the future
tiernob24258a2018-10-04 18:39:49 +02001356 engine_topic = "vim_accounts"
tiernoc94c3df2018-02-09 15:38:54 +01001357
preethika.p329b8182020-04-22 12:25:39 +05301358 if topic == "subscriptions":
1359 engine_topic = main_topic + "_" + topic
1360
tiernoc94c3df2018-02-09 15:38:54 +01001361 if method == "GET":
garciadeblas4568a372021-03-24 09:19:48 +01001362 if item in (
1363 "nsd_content",
1364 "package_content",
1365 "artifacts",
1366 "vnfd",
1367 "nsd",
1368 "nst",
1369 "nst_content",
1370 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001371 if item in ("vnfd", "nsd", "nst"):
tiernof27c79b2018-03-12 17:08:42 +01001372 path = "$DESCRIPTOR"
1373 elif args:
1374 path = args
tiernob24258a2018-10-04 18:39:49 +02001375 elif item == "artifacts":
tiernof27c79b2018-03-12 17:08:42 +01001376 path = ()
1377 else:
1378 path = None
garciadeblas4568a372021-03-24 09:19:48 +01001379 file, _format = self.engine.get_file(
1380 engine_session,
1381 engine_topic,
1382 _id,
1383 path,
1384 cherrypy.request.headers.get("Accept"),
1385 )
tiernof27c79b2018-03-12 17:08:42 +01001386 outdata = file
1387 elif not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001388 outdata = self.engine.get_item_list(
1389 engine_session, engine_topic, kwargs, api_req=True
1390 )
tiernoc94c3df2018-02-09 15:38:54 +01001391 else:
vijay.r35ef2f72019-04-30 17:55:49 +05301392 if item == "reports":
1393 # TODO check that project_id (_id in this context) has permissions
1394 _id = args[0]
K Sai Kiran57589552021-01-27 21:38:34 +05301395 filter_q = None
1396 if "vcaStatusRefresh" in kwargs:
1397 filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]}
1398 outdata = self.engine.get_item(engine_session, engine_topic, _id, filter_q, True)
delacruzramo271d2002019-12-02 21:00:37 +01001399
tiernof27c79b2018-03-12 17:08:42 +01001400 elif method == "POST":
tiernobdebce92019-07-01 15:36:49 +00001401 cherrypy.response.status = HTTPStatus.CREATED.value
garciadeblas4568a372021-03-24 09:19:48 +01001402 if topic in (
1403 "ns_descriptors_content",
1404 "vnf_packages_content",
1405 "netslice_templates_content",
1406 ):
tiernof27c79b2018-03-12 17:08:42 +01001407 _id = cherrypy.request.headers.get("Transaction-Id")
1408 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001409 _id, _ = self.engine.new_item(
1410 rollback,
1411 engine_session,
1412 engine_topic,
1413 {},
1414 None,
1415 cherrypy.request.headers,
1416 )
1417 completed = self.engine.upload_content(
1418 engine_session,
1419 engine_topic,
1420 _id,
1421 indata,
1422 kwargs,
1423 cherrypy.request.headers,
1424 )
tiernof27c79b2018-03-12 17:08:42 +01001425 if completed:
tiernob24258a2018-10-04 18:39:49 +02001426 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001427 else:
1428 cherrypy.response.headers["Transaction-Id"] = _id
1429 outdata = {"id": _id}
tiernob24258a2018-10-04 18:39:49 +02001430 elif topic == "ns_instances_content":
1431 # creates NSR
garciadeblas4568a372021-03-24 09:19:48 +01001432 _id, _ = self.engine.new_item(
1433 rollback, engine_session, engine_topic, indata, kwargs
1434 )
tiernob24258a2018-10-04 18:39:49 +02001435 # creates nslcmop
1436 indata["lcmOperationType"] = "instantiate"
1437 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001438 nslcmop_id, _ = self.engine.new_item(
1439 rollback, engine_session, "nslcmops", indata, None
1440 )
tiernob24258a2018-10-04 18:39:49 +02001441 self._set_location_header(main_topic, version, topic, _id)
kuuse078f55e2019-05-16 19:24:21 +02001442 outdata = {"id": _id, "nslcmop_id": nslcmop_id}
tiernob24258a2018-10-04 18:39:49 +02001443 elif topic == "ns_instances" and item:
1444 indata["lcmOperationType"] = item
1445 indata["nsInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001446 _id, _ = self.engine.new_item(
1447 rollback, engine_session, "nslcmops", indata, kwargs
1448 )
1449 self._set_location_header(
1450 main_topic, version, "ns_lcm_op_occs", _id
1451 )
tierno65acb4d2018-04-06 16:42:40 +02001452 outdata = {"id": _id}
1453 cherrypy.response.status = HTTPStatus.ACCEPTED.value
garciadeblas9750c5a2018-10-15 16:20:35 +02001454 elif topic == "netslice_instances_content":
Felipe Vicens07f31722018-10-29 15:16:44 +01001455 # creates NetSlice_Instance_record (NSIR)
garciadeblas4568a372021-03-24 09:19:48 +01001456 _id, _ = self.engine.new_item(
1457 rollback, engine_session, engine_topic, indata, kwargs
1458 )
Felipe Vicens07f31722018-10-29 15:16:44 +01001459 self._set_location_header(main_topic, version, topic, _id)
garciadeblas9750c5a2018-10-15 16:20:35 +02001460 indata["lcmOperationType"] = "instantiate"
Felipe Vicens126af572019-06-05 19:13:04 +02001461 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001462 nsilcmop_id, _ = self.engine.new_item(
1463 rollback, engine_session, "nsilcmops", indata, kwargs
1464 )
kuuse078f55e2019-05-16 19:24:21 +02001465 outdata = {"id": _id, "nsilcmop_id": nsilcmop_id}
garciadeblas9750c5a2018-10-15 16:20:35 +02001466 elif topic == "netslice_instances" and item:
1467 indata["lcmOperationType"] = item
Felipe Vicens126af572019-06-05 19:13:04 +02001468 indata["netsliceInstanceId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001469 _id, _ = self.engine.new_item(
1470 rollback, engine_session, "nsilcmops", indata, kwargs
1471 )
1472 self._set_location_header(
1473 main_topic, version, "nsi_lcm_op_occs", _id
1474 )
garciadeblas9750c5a2018-10-15 16:20:35 +02001475 outdata = {"id": _id}
1476 cherrypy.response.status = HTTPStatus.ACCEPTED.value
delacruzramo271d2002019-12-02 21:00:37 +01001477 elif topic == "vnf_packages" and item == "action":
1478 indata["lcmOperationType"] = item
1479 indata["vnfPkgId"] = _id
garciadeblas4568a372021-03-24 09:19:48 +01001480 _id, _ = self.engine.new_item(
1481 rollback, engine_session, "vnfpkgops", indata, kwargs
1482 )
1483 self._set_location_header(
1484 main_topic, version, "vnfpkg_op_occs", _id
1485 )
delacruzramo271d2002019-12-02 21:00:37 +01001486 outdata = {"id": _id}
1487 cherrypy.response.status = HTTPStatus.ACCEPTED.value
preethika.p329b8182020-04-22 12:25:39 +05301488 elif topic == "subscriptions":
garciadeblas4568a372021-03-24 09:19:48 +01001489 _id, _ = self.engine.new_item(
1490 rollback, engine_session, engine_topic, indata, kwargs
1491 )
preethika.p329b8182020-04-22 12:25:39 +05301492 self._set_location_header(main_topic, version, topic, _id)
1493 link = {}
1494 link["self"] = cherrypy.response.headers["Location"]
garciadeblas4568a372021-03-24 09:19:48 +01001495 outdata = {
1496 "id": _id,
1497 "filter": indata["filter"],
1498 "callbackUri": indata["CallbackUri"],
1499 "_links": link,
1500 }
preethika.p329b8182020-04-22 12:25:39 +05301501 cherrypy.response.status = HTTPStatus.CREATED.value
tiernof27c79b2018-03-12 17:08:42 +01001502 else:
garciadeblas4568a372021-03-24 09:19:48 +01001503 _id, op_id = self.engine.new_item(
1504 rollback,
1505 engine_session,
1506 engine_topic,
1507 indata,
1508 kwargs,
1509 cherrypy.request.headers,
1510 )
tiernob24258a2018-10-04 18:39:49 +02001511 self._set_location_header(main_topic, version, topic, _id)
tiernof27c79b2018-03-12 17:08:42 +01001512 outdata = {"id": _id}
tiernobdebce92019-07-01 15:36:49 +00001513 if op_id:
1514 outdata["op_id"] = op_id
1515 cherrypy.response.status = HTTPStatus.ACCEPTED.value
tiernob24258a2018-10-04 18:39:49 +02001516 # TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages")
tierno09c073e2018-04-26 13:36:48 +02001517
tiernoc94c3df2018-02-09 15:38:54 +01001518 elif method == "DELETE":
1519 if not _id:
garciadeblas4568a372021-03-24 09:19:48 +01001520 outdata = self.engine.del_item_list(
1521 engine_session, engine_topic, kwargs
1522 )
tierno09c073e2018-04-26 13:36:48 +02001523 cherrypy.response.status = HTTPStatus.OK.value
tiernoc94c3df2018-02-09 15:38:54 +01001524 else: # len(args) > 1
tierno22577432020-04-08 15:16:57 +00001525 # for NS NSI generate an operation
1526 op_id = None
tierno701018c2019-06-25 11:13:14 +00001527 if topic == "ns_instances_content" and not engine_session["force"]:
tiernob24258a2018-10-04 18:39:49 +02001528 nslcmop_desc = {
1529 "lcmOperationType": "terminate",
1530 "nsInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001531 "autoremove": True,
tiernob24258a2018-10-04 18:39:49 +02001532 }
garciadeblas4568a372021-03-24 09:19:48 +01001533 op_id, _ = self.engine.new_item(
1534 rollback, engine_session, "nslcmops", nslcmop_desc, kwargs
1535 )
tierno22577432020-04-08 15:16:57 +00001536 if op_id:
1537 outdata = {"_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001538 elif (
1539 topic == "netslice_instances_content"
1540 and not engine_session["force"]
1541 ):
garciadeblas9750c5a2018-10-15 16:20:35 +02001542 nsilcmop_desc = {
1543 "lcmOperationType": "terminate",
Felipe Vicens126af572019-06-05 19:13:04 +02001544 "netsliceInstanceId": _id,
garciadeblas4568a372021-03-24 09:19:48 +01001545 "autoremove": True,
garciadeblas9750c5a2018-10-15 16:20:35 +02001546 }
garciadeblas4568a372021-03-24 09:19:48 +01001547 op_id, _ = self.engine.new_item(
1548 rollback, engine_session, "nsilcmops", nsilcmop_desc, None
1549 )
tierno22577432020-04-08 15:16:57 +00001550 if op_id:
1551 outdata = {"_id": op_id}
1552 # if there is not any deletion in process, delete
1553 if not op_id:
1554 op_id = self.engine.del_item(engine_session, engine_topic, _id)
1555 if op_id:
1556 outdata = {"op_id": op_id}
garciadeblas4568a372021-03-24 09:19:48 +01001557 cherrypy.response.status = (
1558 HTTPStatus.ACCEPTED.value
1559 if op_id
1560 else HTTPStatus.NO_CONTENT.value
1561 )
tierno09c073e2018-04-26 13:36:48 +02001562
tierno7ae10112018-05-18 14:36:02 +02001563 elif method in ("PUT", "PATCH"):
tiernobdebce92019-07-01 15:36:49 +00001564 op_id = None
tierno701018c2019-06-25 11:13:14 +00001565 if not indata and not kwargs and not engine_session.get("set_project"):
garciadeblas4568a372021-03-24 09:19:48 +01001566 raise NbiException(
1567 "Nothing to update. Provide payload and/or query string",
1568 HTTPStatus.BAD_REQUEST,
1569 )
1570 if (
1571 item in ("nsd_content", "package_content", "nst_content")
1572 and method == "PUT"
1573 ):
1574 completed = self.engine.upload_content(
1575 engine_session,
1576 engine_topic,
1577 _id,
1578 indata,
1579 kwargs,
1580 cherrypy.request.headers,
1581 )
tiernof27c79b2018-03-12 17:08:42 +01001582 if not completed:
1583 cherrypy.response.headers["Transaction-Id"] = id
tiernof27c79b2018-03-12 17:08:42 +01001584 else:
garciadeblas4568a372021-03-24 09:19:48 +01001585 op_id = self.engine.edit_item(
1586 engine_session, engine_topic, _id, indata, kwargs
1587 )
tiernobdebce92019-07-01 15:36:49 +00001588
1589 if op_id:
1590 cherrypy.response.status = HTTPStatus.ACCEPTED.value
1591 outdata = {"op_id": op_id}
1592 else:
1593 cherrypy.response.status = HTTPStatus.NO_CONTENT.value
1594 outdata = None
tiernoc94c3df2018-02-09 15:38:54 +01001595 else:
garciadeblas4568a372021-03-24 09:19:48 +01001596 raise NbiException(
1597 "Method {} not allowed".format(method),
1598 HTTPStatus.METHOD_NOT_ALLOWED,
1599 )
tiernoa6bb45d2019-06-14 09:45:39 +00001600
1601 # if Role information changes, it is needed to reload the information of roles
1602 if topic == "roles" and method != "GET":
1603 self.authenticator.load_operation_to_allowed_roles()
delacruzramoad682a52019-12-10 16:26:34 +01001604
garciadeblas4568a372021-03-24 09:19:48 +01001605 if (
1606 topic == "projects"
1607 and method == "DELETE"
1608 or topic in ["users", "roles"]
1609 and method in ["PUT", "PATCH", "DELETE"]
1610 ):
delacruzramoad682a52019-12-10 16:26:34 +01001611 self.authenticator.remove_token_from_cache()
1612
tierno701018c2019-06-25 11:13:14 +00001613 return self._format_out(outdata, token_info, _format)
tiernob24258a2018-10-04 18:39:49 +02001614 except Exception as e:
garciadeblas4568a372021-03-24 09:19:48 +01001615 if isinstance(
1616 e,
1617 (
1618 NbiException,
1619 EngineException,
1620 DbException,
1621 FsException,
1622 MsgException,
1623 AuthException,
1624 ValidationError,
1625 AuthconnException,
1626 ),
1627 ):
tiernob24258a2018-10-04 18:39:49 +02001628 http_code_value = cherrypy.response.status = e.http_code.value
1629 http_code_name = e.http_code.name
1630 cherrypy.log("Exception {}".format(e))
1631 else:
garciadeblas4568a372021-03-24 09:19:48 +01001632 http_code_value = (
1633 cherrypy.response.status
1634 ) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR
Felipe Vicense36ab852018-11-23 14:12:09 +01001635 cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True)
tiernob24258a2018-10-04 18:39:49 +02001636 http_code_name = HTTPStatus.BAD_REQUEST.name
tierno3ace63c2018-05-03 17:51:43 +02001637 if hasattr(outdata, "close"): # is an open file
1638 outdata.close()
tiernob24258a2018-10-04 18:39:49 +02001639 error_text = str(e)
1640 rollback.reverse()
tiernodb9dc582018-06-20 17:27:29 +02001641 for rollback_item in rollback:
tierno3ace63c2018-05-03 17:51:43 +02001642 try:
tiernocc103432018-10-19 14:10:35 +02001643 if rollback_item.get("operation") == "set":
garciadeblas4568a372021-03-24 09:19:48 +01001644 self.engine.db.set_one(
1645 rollback_item["topic"],
1646 {"_id": rollback_item["_id"]},
1647 rollback_item["content"],
1648 fail_on_empty=False,
1649 )
preethika.p329b8182020-04-22 12:25:39 +05301650 elif rollback_item.get("operation") == "del_list":
garciadeblas4568a372021-03-24 09:19:48 +01001651 self.engine.db.del_list(
1652 rollback_item["topic"],
1653 rollback_item["filter"],
1654 fail_on_empty=False,
1655 )
tiernocc103432018-10-19 14:10:35 +02001656 else:
garciadeblas4568a372021-03-24 09:19:48 +01001657 self.engine.db.del_one(
1658 rollback_item["topic"],
1659 {"_id": rollback_item["_id"]},
1660 fail_on_empty=False,
1661 )
tierno3ace63c2018-05-03 17:51:43 +02001662 except Exception as e2:
garciadeblas4568a372021-03-24 09:19:48 +01001663 rollback_error_text = "Rollback Exception {}: {}".format(
1664 rollback_item, e2
1665 )
tiernob24258a2018-10-04 18:39:49 +02001666 cherrypy.log(rollback_error_text)
1667 error_text += ". " + rollback_error_text
1668 # if isinstance(e, MsgException):
1669 # error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format(
1670 # engine_topic[:-1], method, error_text)
tiernoc94c3df2018-02-09 15:38:54 +01001671 problem_details = {
tiernob24258a2018-10-04 18:39:49 +02001672 "code": http_code_name,
1673 "status": http_code_value,
1674 "detail": error_text,
tiernoc94c3df2018-02-09 15:38:54 +01001675 }
tierno701018c2019-06-25 11:13:14 +00001676 return self._format_out(problem_details, token_info)
tiernoc94c3df2018-02-09 15:38:54 +01001677 # raise cherrypy.HTTPError(e.http_code.value, str(e))
tiernoa5035702019-07-29 08:54:42 +00001678 finally:
1679 if token_info:
1680 self._format_login(token_info)
1681 if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict):
1682 for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"):
1683 if outdata.get(logging_id):
garciadeblas4568a372021-03-24 09:19:48 +01001684 cherrypy.request.login += ";{}={}".format(
1685 logging_id, outdata[logging_id][:36]
1686 )
tiernoc94c3df2018-02-09 15:38:54 +01001687
1688
tiernoc94c3df2018-02-09 15:38:54 +01001689def _start_service():
1690 """
1691 Callback function called when cherrypy.engine starts
1692 Override configuration with env variables
1693 Set database, storage, message configuration
1694 Init database with admin/admin user password
1695 """
tierno932499c2019-01-28 17:28:10 +00001696 global nbi_server
1697 global subscription_thread
tiernoc94c3df2018-02-09 15:38:54 +01001698 cherrypy.log.error("Starting osm_nbi")
1699 # update general cherrypy configuration
1700 update_dict = {}
1701
garciadeblas4568a372021-03-24 09:19:48 +01001702 engine_config = cherrypy.tree.apps["/osm"].config
tiernoc94c3df2018-02-09 15:38:54 +01001703 for k, v in environ.items():
1704 if not k.startswith("OSMNBI_"):
1705 continue
tiernoe1281182018-05-22 12:24:36 +02001706 k1, _, k2 = k[7:].lower().partition("_")
tiernoc94c3df2018-02-09 15:38:54 +01001707 if not k2:
1708 continue
1709 try:
1710 # update static configuration
garciadeblas4568a372021-03-24 09:19:48 +01001711 if k == "OSMNBI_STATIC_DIR":
1712 engine_config["/static"]["tools.staticdir.dir"] = v
1713 engine_config["/static"]["tools.staticdir.on"] = True
1714 elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT":
1715 update_dict["server.socket_port"] = int(v)
1716 elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST":
1717 update_dict["server.socket_host"] = v
tiernof5298be2018-05-16 14:43:57 +02001718 elif k1 in ("server", "test", "auth", "log"):
garciadeblas4568a372021-03-24 09:19:48 +01001719 update_dict[k1 + "." + k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001720 elif k1 in ("message", "database", "storage", "authentication"):
tiernof5298be2018-05-16 14:43:57 +02001721 # k2 = k2.replace('_', '.')
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001722 if k2 in ("port", "db_port"):
tiernoc94c3df2018-02-09 15:38:54 +01001723 engine_config[k1][k2] = int(v)
1724 else:
1725 engine_config[k1][k2] = v
Eduardo Sousa819d34c2018-07-31 01:20:02 +01001726
tiernoc94c3df2018-02-09 15:38:54 +01001727 except ValueError as e:
1728 cherrypy.log.error("Ignoring environ '{}': " + str(e))
1729 except Exception as e:
1730 cherrypy.log.warn("skipping environ '{}' on exception '{}'".format(k, e))
1731
1732 if update_dict:
1733 cherrypy.config.update(update_dict)
tiernof5298be2018-05-16 14:43:57 +02001734 engine_config["global"].update(update_dict)
tiernoc94c3df2018-02-09 15:38:54 +01001735
1736 # logging cherrypy
garciadeblas4568a372021-03-24 09:19:48 +01001737 log_format_simple = (
1738 "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
1739 )
1740 log_formatter_simple = logging.Formatter(
1741 log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
1742 )
tiernoc94c3df2018-02-09 15:38:54 +01001743 logger_server = logging.getLogger("cherrypy.error")
1744 logger_access = logging.getLogger("cherrypy.access")
1745 logger_cherry = logging.getLogger("cherrypy")
1746 logger_nbi = logging.getLogger("nbi")
1747
tiernof5298be2018-05-16 14:43:57 +02001748 if "log.file" in engine_config["global"]:
garciadeblas4568a372021-03-24 09:19:48 +01001749 file_handler = logging.handlers.RotatingFileHandler(
1750 engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0
1751 )
tiernoc94c3df2018-02-09 15:38:54 +01001752 file_handler.setFormatter(log_formatter_simple)
1753 logger_cherry.addHandler(file_handler)
1754 logger_nbi.addHandler(file_handler)
tiernob24258a2018-10-04 18:39:49 +02001755 # log always to standard output
garciadeblas4568a372021-03-24 09:19:48 +01001756 for format_, logger in {
1757 "nbi.server %(filename)s:%(lineno)s": logger_server,
1758 "nbi.access %(filename)s:%(lineno)s": logger_access,
1759 "%(name)s %(filename)s:%(lineno)s": logger_nbi,
1760 }.items():
tiernob24258a2018-10-04 18:39:49 +02001761 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
garciadeblas4568a372021-03-24 09:19:48 +01001762 log_formatter_cherry = logging.Formatter(
1763 log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S"
1764 )
tiernob24258a2018-10-04 18:39:49 +02001765 str_handler = logging.StreamHandler()
1766 str_handler.setFormatter(log_formatter_cherry)
1767 logger.addHandler(str_handler)
tiernoc94c3df2018-02-09 15:38:54 +01001768
tiernof5298be2018-05-16 14:43:57 +02001769 if engine_config["global"].get("log.level"):
1770 logger_cherry.setLevel(engine_config["global"]["log.level"])
1771 logger_nbi.setLevel(engine_config["global"]["log.level"])
tiernoc94c3df2018-02-09 15:38:54 +01001772
1773 # logging other modules
garciadeblas4568a372021-03-24 09:19:48 +01001774 for k1, logname in {
1775 "message": "nbi.msg",
1776 "database": "nbi.db",
1777 "storage": "nbi.fs",
1778 }.items():
tiernoc94c3df2018-02-09 15:38:54 +01001779 engine_config[k1]["logger_name"] = logname
1780 logger_module = logging.getLogger(logname)
1781 if "logfile" in engine_config[k1]:
garciadeblas4568a372021-03-24 09:19:48 +01001782 file_handler = logging.handlers.RotatingFileHandler(
1783 engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0
1784 )
tiernoc94c3df2018-02-09 15:38:54 +01001785 file_handler.setFormatter(log_formatter_simple)
1786 logger_module.addHandler(file_handler)
1787 if "loglevel" in engine_config[k1]:
1788 logger_module.setLevel(engine_config[k1]["loglevel"])
1789 # TODO add more entries, e.g.: storage
garciadeblas4568a372021-03-24 09:19:48 +01001790 cherrypy.tree.apps["/osm"].root.engine.start(engine_config)
1791 cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config)
1792 cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version)
1793 cherrypy.tree.apps["/osm"].root.authenticator.init_db(
1794 target_version=auth_database_version
1795 )
tiernobee508e2019-01-21 11:21:49 +00001796
tierno932499c2019-01-28 17:28:10 +00001797 # start subscriptions thread:
garciadeblas4568a372021-03-24 09:19:48 +01001798 subscription_thread = SubscriptionThread(
1799 config=engine_config, engine=nbi_server.engine
1800 )
tierno932499c2019-01-28 17:28:10 +00001801 subscription_thread.start()
1802 # Do not capture except SubscriptionException
1803
tiernob2e48bd2020-02-04 15:47:18 +00001804 backend = engine_config["authentication"]["backend"]
garciadeblas4568a372021-03-24 09:19:48 +01001805 cherrypy.log.error(
1806 "Starting OSM NBI Version '{} {}' with '{}' authentication backend".format(
1807 nbi_version, nbi_version_date, backend
1808 )
1809 )
tiernoc94c3df2018-02-09 15:38:54 +01001810
1811
1812def _stop_service():
1813 """
1814 Callback function called when cherrypy.engine stops
1815 TODO: Ending database connections.
1816 """
tierno932499c2019-01-28 17:28:10 +00001817 global subscription_thread
tierno65ca36d2019-02-12 19:27:52 +01001818 if subscription_thread:
1819 subscription_thread.terminate()
tierno932499c2019-01-28 17:28:10 +00001820 subscription_thread = None
garciadeblas4568a372021-03-24 09:19:48 +01001821 cherrypy.tree.apps["/osm"].root.engine.stop()
tiernoc94c3df2018-02-09 15:38:54 +01001822 cherrypy.log.error("Stopping osm_nbi")
1823
tierno2236d202018-05-16 19:05:16 +02001824
tiernof5298be2018-05-16 14:43:57 +02001825def nbi(config_file):
tierno932499c2019-01-28 17:28:10 +00001826 global nbi_server
tiernoc94c3df2018-02-09 15:38:54 +01001827 # conf = {
1828 # '/': {
1829 # #'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
1830 # 'tools.sessions.on': True,
1831 # 'tools.response_headers.on': True,
1832 # # 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
1833 # }
1834 # }
1835 # cherrypy.Server.ssl_module = 'builtin'
1836 # cherrypy.Server.ssl_certificate = "http/cert.pem"
1837 # cherrypy.Server.ssl_private_key = "http/privkey.pem"
1838 # cherrypy.Server.thread_pool = 10
1839 # cherrypy.config.update({'Server.socket_port': config["port"], 'Server.socket_host': config["host"]})
1840
1841 # cherrypy.config.update({'tools.auth_basic.on': True,
1842 # 'tools.auth_basic.realm': 'localhost',
1843 # 'tools.auth_basic.checkpassword': validate_password})
tierno932499c2019-01-28 17:28:10 +00001844 nbi_server = Server()
garciadeblas4568a372021-03-24 09:19:48 +01001845 cherrypy.engine.subscribe("start", _start_service)
1846 cherrypy.engine.subscribe("stop", _stop_service)
1847 cherrypy.quickstart(nbi_server, "/osm", config_file)
tiernof5298be2018-05-16 14:43:57 +02001848
1849
1850def usage():
garciadeblas4568a372021-03-24 09:19:48 +01001851 print(
1852 """Usage: {} [options]
tiernof5298be2018-05-16 14:43:57 +02001853 -c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg)
1854 -h|--help: shows this help
garciadeblas4568a372021-03-24 09:19:48 +01001855 """.format(
1856 sys.argv[0]
1857 )
1858 )
tierno2236d202018-05-16 19:05:16 +02001859 # --log-socket-host HOST: send logs to this host")
1860 # --log-socket-port PORT: send logs using this port (default: 9022)")
tiernoc94c3df2018-02-09 15:38:54 +01001861
1862
garciadeblas4568a372021-03-24 09:19:48 +01001863if __name__ == "__main__":
tiernof5298be2018-05-16 14:43:57 +02001864 try:
1865 # load parameters and configuration
1866 opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"])
1867 # TODO add "log-socket-host=", "log-socket-port=", "log-file="
1868 config_file = None
1869 for o, a in opts:
1870 if o in ("-h", "--help"):
1871 usage()
1872 sys.exit()
1873 elif o in ("-c", "--config"):
1874 config_file = a
1875 # elif o == "--log-socket-port":
1876 # log_socket_port = a
1877 # elif o == "--log-socket-host":
1878 # log_socket_host = a
1879 # elif o == "--log-file":
1880 # log_file = a
1881 else:
1882 assert False, "Unhandled option"
1883 if config_file:
1884 if not path.isfile(config_file):
garciadeblas4568a372021-03-24 09:19:48 +01001885 print(
1886 "configuration file '{}' that not exist".format(config_file),
1887 file=sys.stderr,
1888 )
tiernof5298be2018-05-16 14:43:57 +02001889 exit(1)
1890 else:
garciadeblas4568a372021-03-24 09:19:48 +01001891 for config_file in (
1892 __file__[: __file__.rfind(".")] + ".cfg",
1893 "./nbi.cfg",
1894 "/etc/osm/nbi.cfg",
1895 ):
tiernof5298be2018-05-16 14:43:57 +02001896 if path.isfile(config_file):
1897 break
1898 else:
garciadeblas4568a372021-03-24 09:19:48 +01001899 print(
1900 "No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/",
1901 file=sys.stderr,
1902 )
tiernof5298be2018-05-16 14:43:57 +02001903 exit(1)
1904 nbi(config_file)
1905 except getopt.GetoptError as e:
1906 print(str(e), file=sys.stderr)
1907 # usage()
1908 exit(1)