1 |
|
#!/usr/bin/python3 |
2 |
|
# -*- coding: utf-8 -*- |
3 |
|
|
4 |
|
# 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 |
|
|
17 |
0 |
import cherrypy |
18 |
0 |
import time |
19 |
0 |
import json |
20 |
0 |
import yaml |
21 |
0 |
import osm_nbi.html_out as html |
22 |
0 |
import logging |
23 |
0 |
import logging.handlers |
24 |
0 |
import getopt |
25 |
0 |
import sys |
26 |
|
|
27 |
0 |
from osm_nbi.authconn import AuthException, AuthconnException |
28 |
0 |
from osm_nbi.auth import Authenticator |
29 |
0 |
from osm_nbi.engine import Engine, EngineException |
30 |
0 |
from osm_nbi.subscriptions import SubscriptionThread |
31 |
0 |
from osm_nbi.validation import ValidationError |
32 |
0 |
from osm_common.dbbase import DbException |
33 |
0 |
from osm_common.fsbase import FsException |
34 |
0 |
from osm_common.msgbase import MsgException |
35 |
0 |
from http import HTTPStatus |
36 |
0 |
from codecs import getreader |
37 |
0 |
from os import environ, path |
38 |
0 |
from osm_nbi import version as nbi_version, version_date as nbi_version_date |
39 |
|
|
40 |
0 |
__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>" |
41 |
|
|
42 |
0 |
__version__ = "0.1.3" # file version, not NBI version |
43 |
0 |
version_date = "Aug 2019" |
44 |
|
|
45 |
0 |
database_version = "1.2" |
46 |
0 |
auth_database_version = "1.0" |
47 |
0 |
nbi_server = None # instance of Server class |
48 |
0 |
subscription_thread = None # instance of SubscriptionThread class |
49 |
|
|
50 |
|
""" |
51 |
|
North Bound Interface (O: OSM specific; 5,X: SOL005 not implemented yet; O5: SOL005 implemented) |
52 |
|
URL: /osm GET POST PUT DELETE PATCH |
53 |
|
/nsd/v1 |
54 |
|
/ns_descriptors_content O O |
55 |
|
/<nsdInfoId> O O O O |
56 |
|
/ns_descriptors O5 O5 |
57 |
|
/<nsdInfoId> O5 O5 5 |
58 |
|
/nsd_content O5 O5 |
59 |
|
/nsd O |
60 |
|
/artifacts[/<artifactPath>] O |
61 |
|
/pnf_descriptors 5 5 |
62 |
|
/<pnfdInfoId> 5 5 5 |
63 |
|
/pnfd_content 5 5 |
64 |
|
/subscriptions 5 5 |
65 |
|
/<subscriptionId> 5 X |
66 |
|
|
67 |
|
/vnfpkgm/v1 |
68 |
|
/vnf_packages_content O O |
69 |
|
/<vnfPkgId> O O |
70 |
|
/vnf_packages O5 O5 |
71 |
|
/<vnfPkgId> O5 O5 5 |
72 |
|
/package_content O5 O5 |
73 |
|
/upload_from_uri X |
74 |
|
/vnfd O5 |
75 |
|
/artifacts[/<artifactPath>] O5 |
76 |
|
/subscriptions X X |
77 |
|
/<subscriptionId> X X |
78 |
|
|
79 |
|
/nslcm/v1 |
80 |
|
/ns_instances_content O O |
81 |
|
/<nsInstanceId> O O |
82 |
|
/ns_instances 5 5 |
83 |
|
/<nsInstanceId> O5 O5 |
84 |
|
instantiate O5 |
85 |
|
terminate O5 |
86 |
|
action O |
87 |
|
scale O5 |
88 |
|
migrate O |
89 |
|
update 05 |
90 |
|
heal O5 |
91 |
|
/ns_lcm_op_occs 5 5 |
92 |
|
/<nsLcmOpOccId> 5 5 5 |
93 |
|
TO BE COMPLETED 5 5 |
94 |
|
/vnf_instances (also vnfrs for compatibility) O |
95 |
|
/<vnfInstanceId> O |
96 |
|
/subscriptions 5 5 |
97 |
|
/<subscriptionId> 5 X |
98 |
|
|
99 |
|
/pdu/v1 |
100 |
|
/pdu_descriptors O O |
101 |
|
/<id> O O O O |
102 |
|
|
103 |
|
/admin/v1 |
104 |
|
/tokens O O |
105 |
|
/<id> O O |
106 |
|
/users O O |
107 |
|
/<id> O O O O |
108 |
|
/projects O O |
109 |
|
/<id> O O |
110 |
|
/vim_accounts (also vims for compatibility) O O |
111 |
|
/<id> O O O |
112 |
|
/wim_accounts O O |
113 |
|
/<id> O O O |
114 |
|
/sdns O O |
115 |
|
/<id> O O O |
116 |
|
/k8sclusters O O |
117 |
|
/<id> O O O |
118 |
|
/k8srepos O O |
119 |
|
/<id> O O |
120 |
|
/osmrepos O O |
121 |
|
/<id> O O |
122 |
|
|
123 |
|
/nst/v1 O O |
124 |
|
/netslice_templates_content O O |
125 |
|
/<nstInfoId> O O O O |
126 |
|
/netslice_templates O O |
127 |
|
/<nstInfoId> O O O |
128 |
|
/nst_content O O |
129 |
|
/nst O |
130 |
|
/artifacts[/<artifactPath>] O |
131 |
|
/subscriptions X X |
132 |
|
/<subscriptionId> X X |
133 |
|
|
134 |
|
/nsilcm/v1 |
135 |
|
/netslice_instances_content O O |
136 |
|
/<SliceInstanceId> O O |
137 |
|
/netslice_instances O O |
138 |
|
/<SliceInstanceId> O O |
139 |
|
instantiate O |
140 |
|
terminate O |
141 |
|
action O |
142 |
|
/nsi_lcm_op_occs O O |
143 |
|
/<nsiLcmOpOccId> O O O |
144 |
|
/subscriptions X X |
145 |
|
/<subscriptionId> X X |
146 |
|
|
147 |
|
query string: |
148 |
|
Follows SOL005 section 4.3.2 It contains extra METHOD to override http method, FORCE to force. |
149 |
|
simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]* |
150 |
|
filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]* |
151 |
|
op := "eq" | "neq" (or "ne") | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont" |
152 |
|
attrName := string |
153 |
|
For filtering inside array, it must select the element of the array, or add ANYINDEX to apply the filtering over any |
154 |
|
item of the array, that is, pass if any item of the array pass the filter. |
155 |
|
It allows both ne and neq for not equal |
156 |
|
TODO: 4.3.3 Attribute selectors |
157 |
|
all_fields, fields=x,y,.., exclude_default, exclude_fields=x,y,... |
158 |
|
(none) … same as “exclude_default” |
159 |
|
all_fields … all attributes. |
160 |
|
fields=<list> … all attributes except all complex attributes with minimum cardinality of zero that are not |
161 |
|
conditionally mandatory, and that are not provided in <list>. |
162 |
|
exclude_fields=<list> … all attributes except those complex attributes with a minimum cardinality of zero that |
163 |
|
are not conditionally mandatory, and that are provided in <list>. |
164 |
|
exclude_default … all attributes except those complex attributes with a minimum cardinality of zero that are not |
165 |
|
conditionally mandatory, and that are part of the "default exclude set" defined in the present specification for |
166 |
|
the particular resource |
167 |
|
exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality |
168 |
|
of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the |
169 |
|
present specification for the particular resource, but that are not part of <list> |
170 |
|
Additionally it admits some administrator values: |
171 |
|
FORCE: To force operations skipping dependency checkings |
172 |
|
ADMIN: To act as an administrator or a different project |
173 |
|
PUBLIC: To get public descriptors or set a descriptor as public |
174 |
|
SET_PROJECT: To make a descriptor available for other project |
175 |
|
|
176 |
|
Header field name Reference Example Descriptions |
177 |
|
Accept IETF RFC 7231 [19] application/json Content-Types that are acceptable for the response. |
178 |
|
This header field shall be present if the response is expected to have a non-empty message body. |
179 |
|
Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the request. |
180 |
|
This header field shall be present if the request has a non-empty message body. |
181 |
|
Authorization IETF RFC 7235 [22] Bearer mF_9.B5f-4.1JqM The authorization token for the request. |
182 |
|
Details are specified in clause 4.5.3. |
183 |
|
Range IETF RFC 7233 [21] 1000-2000 Requested range of bytes from a file |
184 |
|
Header field name Reference Example Descriptions |
185 |
|
Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the response. |
186 |
|
This header field shall be present if the response has a non-empty message body. |
187 |
|
Location IETF RFC 7231 [19] http://www.example.com/vnflcm/v1/vnf_instances/123 Used in redirection, or when a |
188 |
|
new resource has been created. |
189 |
|
This header field shall be present if the response status code is 201 or 3xx. |
190 |
|
In the present document this header field is also used if the response status code is 202 and a new resource was |
191 |
|
created. |
192 |
|
WWW-Authenticate IETF RFC 7235 [22] Bearer realm="example" Challenge if the corresponding HTTP request has not |
193 |
|
provided authorization, or error details if the corresponding HTTP request has provided an invalid authorization |
194 |
|
token. |
195 |
|
Accept-Ranges IETF RFC 7233 [21] bytes Used by the Server to signal whether or not it supports ranges for |
196 |
|
certain resources. |
197 |
|
Content-Range IETF RFC 7233 [21] bytes 21010-47021/ 47022 Signals the byte range that is contained in the |
198 |
|
response, and the total length of the file. |
199 |
|
Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT |
200 |
|
""" |
201 |
|
|
202 |
0 |
valid_query_string = ("ADMIN", "SET_PROJECT", "FORCE", "PUBLIC") |
203 |
|
# ^ Contains possible administrative query string words: |
204 |
|
# ADMIN=True(by default)|Project|Project-list: See all elements, or elements of a project |
205 |
|
# (not owned by my session project). |
206 |
|
# PUBLIC=True(by default)|False: See/hide public elements. Set/Unset a topic to be public |
207 |
|
# FORCE=True(by default)|False: Force edition/deletion operations |
208 |
|
# SET_PROJECT=Project|Project-list: Add/Delete the topic to the projects portfolio |
209 |
|
|
210 |
0 |
valid_url_methods = { |
211 |
|
# contains allowed URL and methods, and the role_permission name |
212 |
|
"admin": { |
213 |
|
"v1": { |
214 |
|
"tokens": { |
215 |
|
"METHODS": ("GET", "POST", "DELETE"), |
216 |
|
"ROLE_PERMISSION": "tokens:", |
217 |
|
"<ID>": {"METHODS": ("GET", "DELETE"), "ROLE_PERMISSION": "tokens:id:"}, |
218 |
|
}, |
219 |
|
"users": { |
220 |
|
"METHODS": ("GET", "POST"), |
221 |
|
"ROLE_PERMISSION": "users:", |
222 |
|
"<ID>": { |
223 |
|
"METHODS": ("GET", "DELETE", "PATCH"), |
224 |
|
"ROLE_PERMISSION": "users:id:", |
225 |
|
}, |
226 |
|
}, |
227 |
|
"projects": { |
228 |
|
"METHODS": ("GET", "POST"), |
229 |
|
"ROLE_PERMISSION": "projects:", |
230 |
|
"<ID>": { |
231 |
|
"METHODS": ("GET", "DELETE", "PATCH"), |
232 |
|
"ROLE_PERMISSION": "projects:id:", |
233 |
|
}, |
234 |
|
}, |
235 |
|
"roles": { |
236 |
|
"METHODS": ("GET", "POST"), |
237 |
|
"ROLE_PERMISSION": "roles:", |
238 |
|
"<ID>": { |
239 |
|
"METHODS": ("GET", "DELETE", "PATCH"), |
240 |
|
"ROLE_PERMISSION": "roles:id:", |
241 |
|
}, |
242 |
|
}, |
243 |
|
"vims": { |
244 |
|
"METHODS": ("GET", "POST"), |
245 |
|
"ROLE_PERMISSION": "vims:", |
246 |
|
"<ID>": { |
247 |
|
"METHODS": ("GET", "DELETE", "PATCH"), |
248 |
|
"ROLE_PERMISSION": "vims:id:", |
249 |
|
}, |
250 |
|
}, |
251 |
|
"vim_accounts": { |
252 |
|
"METHODS": ("GET", "POST"), |
253 |
|
"ROLE_PERMISSION": "vim_accounts:", |
254 |
|
"<ID>": { |
255 |
|
"METHODS": ("GET", "DELETE", "PATCH"), |
256 |
|
"ROLE_PERMISSION": "vim_accounts:id:", |
257 |
|
}, |
258 |
|
}, |
259 |
|
"wim_accounts": { |
260 |
|
"METHODS": ("GET", "POST"), |
261 |
|
"ROLE_PERMISSION": "wim_accounts:", |
262 |
|
"<ID>": { |
263 |
|
"METHODS": ("GET", "DELETE", "PATCH"), |
264 |
|
"ROLE_PERMISSION": "wim_accounts:id:", |
265 |
|
}, |
266 |
|
}, |
267 |
|
"sdns": { |
268 |
|
"METHODS": ("GET", "POST"), |
269 |
|
"ROLE_PERMISSION": "sdn_controllers:", |
270 |
|
"<ID>": { |
271 |
|
"METHODS": ("GET", "DELETE", "PATCH"), |
272 |
|
"ROLE_PERMISSION": "sdn_controllers:id:", |
273 |
|
}, |
274 |
|
}, |
275 |
|
"k8sclusters": { |
276 |
|
"METHODS": ("GET", "POST"), |
277 |
|
"ROLE_PERMISSION": "k8sclusters:", |
278 |
|
"<ID>": { |
279 |
|
"METHODS": ("GET", "DELETE", "PATCH"), |
280 |
|
"ROLE_PERMISSION": "k8sclusters:id:", |
281 |
|
}, |
282 |
|
}, |
283 |
|
"vca": { |
284 |
|
"METHODS": ("GET", "POST"), |
285 |
|
"ROLE_PERMISSION": "vca:", |
286 |
|
"<ID>": { |
287 |
|
"METHODS": ("GET", "DELETE", "PATCH"), |
288 |
|
"ROLE_PERMISSION": "vca:id:", |
289 |
|
}, |
290 |
|
}, |
291 |
|
"k8srepos": { |
292 |
|
"METHODS": ("GET", "POST"), |
293 |
|
"ROLE_PERMISSION": "k8srepos:", |
294 |
|
"<ID>": { |
295 |
|
"METHODS": ("GET", "DELETE"), |
296 |
|
"ROLE_PERMISSION": "k8srepos:id:", |
297 |
|
}, |
298 |
|
}, |
299 |
|
"osmrepos": { |
300 |
|
"METHODS": ("GET", "POST"), |
301 |
|
"ROLE_PERMISSION": "osmrepos:", |
302 |
|
"<ID>": { |
303 |
|
"METHODS": ("GET", "DELETE", "PATCH"), |
304 |
|
"ROLE_PERMISSION": "osmrepos:id:", |
305 |
|
}, |
306 |
|
}, |
307 |
|
"domains": { |
308 |
|
"METHODS": ("GET",), |
309 |
|
"ROLE_PERMISSION": "domains:", |
310 |
|
}, |
311 |
|
} |
312 |
|
}, |
313 |
|
"pdu": { |
314 |
|
"v1": { |
315 |
|
"pdu_descriptors": { |
316 |
|
"METHODS": ("GET", "POST"), |
317 |
|
"ROLE_PERMISSION": "pduds:", |
318 |
|
"<ID>": { |
319 |
|
"METHODS": ("GET", "POST", "DELETE", "PATCH", "PUT"), |
320 |
|
"ROLE_PERMISSION": "pduds:id:", |
321 |
|
}, |
322 |
|
}, |
323 |
|
} |
324 |
|
}, |
325 |
|
"nsd": { |
326 |
|
"v1": { |
327 |
|
"ns_descriptors_content": { |
328 |
|
"METHODS": ("GET", "POST"), |
329 |
|
"ROLE_PERMISSION": "nsds:", |
330 |
|
"<ID>": { |
331 |
|
"METHODS": ("GET", "PUT", "DELETE"), |
332 |
|
"ROLE_PERMISSION": "nsds:id:", |
333 |
|
}, |
334 |
|
}, |
335 |
|
"ns_descriptors": { |
336 |
|
"METHODS": ("GET", "POST"), |
337 |
|
"ROLE_PERMISSION": "nsds:", |
338 |
|
"<ID>": { |
339 |
|
"METHODS": ("GET", "DELETE", "PATCH"), |
340 |
|
"ROLE_PERMISSION": "nsds:id:", |
341 |
|
"nsd_content": { |
342 |
|
"METHODS": ("GET", "PUT"), |
343 |
|
"ROLE_PERMISSION": "nsds:id:content:", |
344 |
|
}, |
345 |
|
"nsd": { |
346 |
|
"METHODS": ("GET",), # descriptor inside package |
347 |
|
"ROLE_PERMISSION": "nsds:id:content:", |
348 |
|
}, |
349 |
|
"artifacts": { |
350 |
|
"METHODS": ("GET",), |
351 |
|
"ROLE_PERMISSION": "nsds:id:nsd_artifact:", |
352 |
|
"*": None, |
353 |
|
}, |
354 |
|
}, |
355 |
|
}, |
356 |
|
"pnf_descriptors": { |
357 |
|
"TODO": ("GET", "POST"), |
358 |
|
"<ID>": { |
359 |
|
"TODO": ("GET", "DELETE", "PATCH"), |
360 |
|
"pnfd_content": {"TODO": ("GET", "PUT")}, |
361 |
|
}, |
362 |
|
}, |
363 |
|
"subscriptions": { |
364 |
|
"TODO": ("GET", "POST"), |
365 |
|
"<ID>": {"TODO": ("GET", "DELETE")}, |
366 |
|
}, |
367 |
|
} |
368 |
|
}, |
369 |
|
"vnfpkgm": { |
370 |
|
"v1": { |
371 |
|
"vnf_packages_content": { |
372 |
|
"METHODS": ("GET", "POST"), |
373 |
|
"ROLE_PERMISSION": "vnfds:", |
374 |
|
"<ID>": { |
375 |
|
"METHODS": ("GET", "PUT", "DELETE"), |
376 |
|
"ROLE_PERMISSION": "vnfds:id:", |
377 |
|
}, |
378 |
|
}, |
379 |
|
"vnf_packages": { |
380 |
|
"METHODS": ("GET", "POST"), |
381 |
|
"ROLE_PERMISSION": "vnfds:", |
382 |
|
"<ID>": { |
383 |
|
"METHODS": ("GET", "DELETE", "PATCH"), # GET: vnfPkgInfo |
384 |
|
"ROLE_PERMISSION": "vnfds:id:", |
385 |
|
"package_content": { |
386 |
|
"METHODS": ("GET", "PUT"), # package |
387 |
|
"ROLE_PERMISSION": "vnfds:id:", |
388 |
|
"upload_from_uri": { |
389 |
|
"METHODS": (), |
390 |
|
"TODO": ("POST",), |
391 |
|
"ROLE_PERMISSION": "vnfds:id:upload:", |
392 |
|
}, |
393 |
|
}, |
394 |
|
"vnfd": { |
395 |
|
"METHODS": ("GET",), # descriptor inside package |
396 |
|
"ROLE_PERMISSION": "vnfds:id:content:", |
397 |
|
}, |
398 |
|
"artifacts": { |
399 |
|
"METHODS": ("GET",), |
400 |
|
"ROLE_PERMISSION": "vnfds:id:vnfd_artifact:", |
401 |
|
"*": None, |
402 |
|
}, |
403 |
|
"action": { |
404 |
|
"METHODS": ("POST",), |
405 |
|
"ROLE_PERMISSION": "vnfds:id:action:", |
406 |
|
}, |
407 |
|
}, |
408 |
|
}, |
409 |
|
"subscriptions": { |
410 |
|
"TODO": ("GET", "POST"), |
411 |
|
"<ID>": {"TODO": ("GET", "DELETE")}, |
412 |
|
}, |
413 |
|
"vnfpkg_op_occs": { |
414 |
|
"METHODS": ("GET",), |
415 |
|
"ROLE_PERMISSION": "vnfds:vnfpkgops:", |
416 |
|
"<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnfds:vnfpkgops:id:"}, |
417 |
|
}, |
418 |
|
} |
419 |
|
}, |
420 |
|
"nslcm": { |
421 |
|
"v1": { |
422 |
|
"ns_instances_content": { |
423 |
|
"METHODS": ("GET", "POST"), |
424 |
|
"ROLE_PERMISSION": "ns_instances:", |
425 |
|
"<ID>": { |
426 |
|
"METHODS": ("GET", "DELETE"), |
427 |
|
"ROLE_PERMISSION": "ns_instances:id:", |
428 |
|
}, |
429 |
|
}, |
430 |
|
"ns_instances": { |
431 |
|
"METHODS": ("GET", "POST"), |
432 |
|
"ROLE_PERMISSION": "ns_instances:", |
433 |
|
"<ID>": { |
434 |
|
"METHODS": ("GET", "DELETE"), |
435 |
|
"ROLE_PERMISSION": "ns_instances:id:", |
436 |
|
"heal": { |
437 |
|
"METHODS": ("POST",), |
438 |
|
"ROLE_PERMISSION": "ns_instances:id:heal:", |
439 |
|
}, |
440 |
|
"scale": { |
441 |
|
"METHODS": ("POST",), |
442 |
|
"ROLE_PERMISSION": "ns_instances:id:scale:", |
443 |
|
}, |
444 |
|
"terminate": { |
445 |
|
"METHODS": ("POST",), |
446 |
|
"ROLE_PERMISSION": "ns_instances:id:terminate:", |
447 |
|
}, |
448 |
|
"instantiate": { |
449 |
|
"METHODS": ("POST",), |
450 |
|
"ROLE_PERMISSION": "ns_instances:id:instantiate:", |
451 |
|
}, |
452 |
|
"migrate": { |
453 |
|
"METHODS": ("POST",), |
454 |
|
"ROLE_PERMISSION": "ns_instances:id:migrate:", |
455 |
|
}, |
456 |
|
"action": { |
457 |
|
"METHODS": ("POST",), |
458 |
|
"ROLE_PERMISSION": "ns_instances:id:action:", |
459 |
|
}, |
460 |
|
"update": { |
461 |
|
"METHODS": ("POST",), |
462 |
|
"ROLE_PERMISSION": "ns_instances:id:update:", |
463 |
|
}, |
464 |
|
"verticalscale": { |
465 |
|
"METHODS": ("POST",), |
466 |
|
"ROLE_PERMISSION": "ns_instances:id:verticalscale:" |
467 |
|
}, |
468 |
|
}, |
469 |
|
}, |
470 |
|
"ns_lcm_op_occs": { |
471 |
|
"METHODS": ("GET",), |
472 |
|
"ROLE_PERMISSION": "ns_instances:opps:", |
473 |
|
"<ID>": { |
474 |
|
"METHODS": ("GET",), |
475 |
|
"ROLE_PERMISSION": "ns_instances:opps:id:", |
476 |
|
}, |
477 |
|
}, |
478 |
|
"vnfrs": { |
479 |
|
"METHODS": ("GET",), |
480 |
|
"ROLE_PERMISSION": "vnf_instances:", |
481 |
|
"<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"}, |
482 |
|
}, |
483 |
|
"vnf_instances": { |
484 |
|
"METHODS": ("GET",), |
485 |
|
"ROLE_PERMISSION": "vnf_instances:", |
486 |
|
"<ID>": {"METHODS": ("GET",), "ROLE_PERMISSION": "vnf_instances:id:"}, |
487 |
|
}, |
488 |
|
"subscriptions": { |
489 |
|
"METHODS": ("GET", "POST"), |
490 |
|
"ROLE_PERMISSION": "ns_subscriptions:", |
491 |
|
"<ID>": { |
492 |
|
"METHODS": ("GET", "DELETE"), |
493 |
|
"ROLE_PERMISSION": "ns_subscriptions:id:", |
494 |
|
}, |
495 |
|
}, |
496 |
|
} |
497 |
|
}, |
498 |
|
"vnflcm": { |
499 |
|
"v1": { |
500 |
|
"vnf_instances": {"METHODS": ("GET", "POST"), |
501 |
|
"ROLE_PERMISSION": "vnflcm_instances:", |
502 |
|
"<ID>": {"METHODS": ("GET", "DELETE"), |
503 |
|
"ROLE_PERMISSION": "vnflcm_instances:id:", |
504 |
|
"scale": {"METHODS": ("POST",), |
505 |
|
"ROLE_PERMISSION": "vnflcm_instances:id:scale:" |
506 |
|
}, |
507 |
|
"terminate": {"METHODS": ("POST",), |
508 |
|
"ROLE_PERMISSION": "vnflcm_instances:id:terminate:" |
509 |
|
}, |
510 |
|
"instantiate": {"METHODS": ("POST",), |
511 |
|
"ROLE_PERMISSION": "vnflcm_instances:id:instantiate:" |
512 |
|
}, |
513 |
|
} |
514 |
|
}, |
515 |
|
"vnf_lcm_op_occs": {"METHODS": ("GET",), |
516 |
|
"ROLE_PERMISSION": "vnf_instances:opps:", |
517 |
|
"<ID>": {"METHODS": ("GET",), |
518 |
|
"ROLE_PERMISSION": "vnf_instances:opps:id:" |
519 |
|
}, |
520 |
|
}, |
521 |
|
"subscriptions": {"METHODS": ("GET", "POST"), |
522 |
|
"ROLE_PERMISSION": "vnflcm_subscriptions:", |
523 |
|
"<ID>": {"METHODS": ("GET", "DELETE"), |
524 |
|
"ROLE_PERMISSION": "vnflcm_subscriptions:id:" |
525 |
|
} |
526 |
|
}, |
527 |
|
} |
528 |
|
}, |
529 |
|
"nst": { |
530 |
|
"v1": { |
531 |
|
"netslice_templates_content": { |
532 |
|
"METHODS": ("GET", "POST"), |
533 |
|
"ROLE_PERMISSION": "slice_templates:", |
534 |
|
"<ID>": { |
535 |
|
"METHODS": ("GET", "PUT", "DELETE"), |
536 |
|
"ROLE_PERMISSION": "slice_templates:id:", |
537 |
|
}, |
538 |
|
}, |
539 |
|
"netslice_templates": { |
540 |
|
"METHODS": ("GET", "POST"), |
541 |
|
"ROLE_PERMISSION": "slice_templates:", |
542 |
|
"<ID>": { |
543 |
|
"METHODS": ("GET", "DELETE"), |
544 |
|
"TODO": ("PATCH",), |
545 |
|
"ROLE_PERMISSION": "slice_templates:id:", |
546 |
|
"nst_content": { |
547 |
|
"METHODS": ("GET", "PUT"), |
548 |
|
"ROLE_PERMISSION": "slice_templates:id:content:", |
549 |
|
}, |
550 |
|
"nst": { |
551 |
|
"METHODS": ("GET",), # descriptor inside package |
552 |
|
"ROLE_PERMISSION": "slice_templates:id:content:", |
553 |
|
}, |
554 |
|
"artifacts": { |
555 |
|
"METHODS": ("GET",), |
556 |
|
"ROLE_PERMISSION": "slice_templates:id:content:", |
557 |
|
"*": None, |
558 |
|
}, |
559 |
|
}, |
560 |
|
}, |
561 |
|
"subscriptions": { |
562 |
|
"TODO": ("GET", "POST"), |
563 |
|
"<ID>": {"TODO": ("GET", "DELETE")}, |
564 |
|
}, |
565 |
|
} |
566 |
|
}, |
567 |
|
"nsilcm": { |
568 |
|
"v1": { |
569 |
|
"netslice_instances_content": { |
570 |
|
"METHODS": ("GET", "POST"), |
571 |
|
"ROLE_PERMISSION": "slice_instances:", |
572 |
|
"<ID>": { |
573 |
|
"METHODS": ("GET", "DELETE"), |
574 |
|
"ROLE_PERMISSION": "slice_instances:id:", |
575 |
|
}, |
576 |
|
}, |
577 |
|
"netslice_instances": { |
578 |
|
"METHODS": ("GET", "POST"), |
579 |
|
"ROLE_PERMISSION": "slice_instances:", |
580 |
|
"<ID>": { |
581 |
|
"METHODS": ("GET", "DELETE"), |
582 |
|
"ROLE_PERMISSION": "slice_instances:id:", |
583 |
|
"terminate": { |
584 |
|
"METHODS": ("POST",), |
585 |
|
"ROLE_PERMISSION": "slice_instances:id:terminate:", |
586 |
|
}, |
587 |
|
"instantiate": { |
588 |
|
"METHODS": ("POST",), |
589 |
|
"ROLE_PERMISSION": "slice_instances:id:instantiate:", |
590 |
|
}, |
591 |
|
"action": { |
592 |
|
"METHODS": ("POST",), |
593 |
|
"ROLE_PERMISSION": "slice_instances:id:action:", |
594 |
|
}, |
595 |
|
}, |
596 |
|
}, |
597 |
|
"nsi_lcm_op_occs": { |
598 |
|
"METHODS": ("GET",), |
599 |
|
"ROLE_PERMISSION": "slice_instances:opps:", |
600 |
|
"<ID>": { |
601 |
|
"METHODS": ("GET",), |
602 |
|
"ROLE_PERMISSION": "slice_instances:opps:id:", |
603 |
|
}, |
604 |
|
}, |
605 |
|
} |
606 |
|
}, |
607 |
|
"nspm": { |
608 |
|
"v1": { |
609 |
|
"pm_jobs": { |
610 |
|
"<ID>": { |
611 |
|
"reports": { |
612 |
|
"<ID>": { |
613 |
|
"METHODS": ("GET",), |
614 |
|
"ROLE_PERMISSION": "reports:id:", |
615 |
|
} |
616 |
|
} |
617 |
|
}, |
618 |
|
}, |
619 |
|
}, |
620 |
|
}, |
621 |
|
"nsfm": { |
622 |
|
"v1": { |
623 |
|
"alarms": {"METHODS": ("GET", "PATCH"), |
624 |
|
"ROLE_PERMISSION": "alarms:", |
625 |
|
"<ID>": {"METHODS": ("GET", "PATCH"), |
626 |
|
"ROLE_PERMISSION": "alarms:id:", |
627 |
|
}, |
628 |
|
} |
629 |
|
}, |
630 |
|
}, |
631 |
|
} |
632 |
|
|
633 |
|
|
634 |
0 |
class NbiException(Exception): |
635 |
0 |
def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED): |
636 |
0 |
Exception.__init__(self, message) |
637 |
0 |
self.http_code = http_code |
638 |
|
|
639 |
|
|
640 |
0 |
class Server(object): |
641 |
0 |
instance = 0 |
642 |
|
# to decode bytes to str |
643 |
0 |
reader = getreader("utf-8") |
644 |
|
|
645 |
0 |
def __init__(self): |
646 |
0 |
self.instance += 1 |
647 |
0 |
self.authenticator = Authenticator(valid_url_methods, valid_query_string) |
648 |
0 |
self.engine = Engine(self.authenticator) |
649 |
|
|
650 |
0 |
def _format_in(self, kwargs): |
651 |
0 |
try: |
652 |
0 |
indata = None |
653 |
0 |
if cherrypy.request.body.length: |
654 |
0 |
error_text = "Invalid input format " |
655 |
|
|
656 |
0 |
if "Content-Type" in cherrypy.request.headers: |
657 |
0 |
if "application/json" in cherrypy.request.headers["Content-Type"]: |
658 |
0 |
error_text = "Invalid json format " |
659 |
0 |
indata = json.load(self.reader(cherrypy.request.body)) |
660 |
0 |
cherrypy.request.headers.pop("Content-File-MD5", None) |
661 |
0 |
elif "application/yaml" in cherrypy.request.headers["Content-Type"]: |
662 |
0 |
error_text = "Invalid yaml format " |
663 |
0 |
indata = yaml.load( |
664 |
|
cherrypy.request.body, Loader=yaml.SafeLoader |
665 |
|
) |
666 |
0 |
cherrypy.request.headers.pop("Content-File-MD5", None) |
667 |
0 |
elif ( |
668 |
|
"application/binary" in cherrypy.request.headers["Content-Type"] |
669 |
|
or "application/gzip" |
670 |
|
in cherrypy.request.headers["Content-Type"] |
671 |
|
or "application/zip" in cherrypy.request.headers["Content-Type"] |
672 |
|
or "text/plain" in cherrypy.request.headers["Content-Type"] |
673 |
|
): |
674 |
0 |
indata = cherrypy.request.body # .read() |
675 |
0 |
elif ( |
676 |
|
"multipart/form-data" |
677 |
|
in cherrypy.request.headers["Content-Type"] |
678 |
|
): |
679 |
0 |
if "descriptor_file" in kwargs: |
680 |
0 |
filecontent = kwargs.pop("descriptor_file") |
681 |
0 |
if not filecontent.file: |
682 |
0 |
raise NbiException( |
683 |
|
"empty file or content", HTTPStatus.BAD_REQUEST |
684 |
|
) |
685 |
0 |
indata = filecontent.file # .read() |
686 |
0 |
if filecontent.content_type.value: |
687 |
0 |
cherrypy.request.headers[ |
688 |
|
"Content-Type" |
689 |
|
] = filecontent.content_type.value |
690 |
|
else: |
691 |
|
# raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable, |
692 |
|
# "Only 'Content-Type' of type 'application/json' or |
693 |
|
# 'application/yaml' for input format are available") |
694 |
0 |
error_text = "Invalid yaml format " |
695 |
0 |
indata = yaml.load( |
696 |
|
cherrypy.request.body, Loader=yaml.SafeLoader |
697 |
|
) |
698 |
0 |
cherrypy.request.headers.pop("Content-File-MD5", None) |
699 |
|
else: |
700 |
0 |
error_text = "Invalid yaml format " |
701 |
0 |
indata = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader) |
702 |
0 |
cherrypy.request.headers.pop("Content-File-MD5", None) |
703 |
0 |
if not indata: |
704 |
0 |
indata = {} |
705 |
|
|
706 |
0 |
format_yaml = False |
707 |
0 |
if cherrypy.request.headers.get("Query-String-Format") == "yaml": |
708 |
0 |
format_yaml = True |
709 |
|
|
710 |
0 |
for k, v in kwargs.items(): |
711 |
0 |
if isinstance(v, str): |
712 |
0 |
if v == "": |
713 |
0 |
kwargs[k] = None |
714 |
0 |
elif format_yaml: |
715 |
0 |
try: |
716 |
0 |
kwargs[k] = yaml.load(v, Loader=yaml.SafeLoader) |
717 |
0 |
except Exception: |
718 |
0 |
pass |
719 |
0 |
elif ( |
720 |
|
k.endswith(".gt") |
721 |
|
or k.endswith(".lt") |
722 |
|
or k.endswith(".gte") |
723 |
|
or k.endswith(".lte") |
724 |
|
): |
725 |
0 |
try: |
726 |
0 |
kwargs[k] = int(v) |
727 |
0 |
except Exception: |
728 |
0 |
try: |
729 |
0 |
kwargs[k] = float(v) |
730 |
0 |
except Exception: |
731 |
0 |
pass |
732 |
0 |
elif v.find(",") > 0: |
733 |
0 |
kwargs[k] = v.split(",") |
734 |
0 |
elif isinstance(v, (list, tuple)): |
735 |
0 |
for index in range(0, len(v)): |
736 |
0 |
if v[index] == "": |
737 |
0 |
v[index] = None |
738 |
0 |
elif format_yaml: |
739 |
0 |
try: |
740 |
0 |
v[index] = yaml.load(v[index], Loader=yaml.SafeLoader) |
741 |
0 |
except Exception: |
742 |
0 |
pass |
743 |
|
|
744 |
0 |
return indata |
745 |
0 |
except (ValueError, yaml.YAMLError) as exc: |
746 |
0 |
raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST) |
747 |
0 |
except KeyError as exc: |
748 |
0 |
raise NbiException( |
749 |
|
"Query string error: " + str(exc), HTTPStatus.BAD_REQUEST |
750 |
|
) |
751 |
0 |
except Exception as exc: |
752 |
0 |
raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST) |
753 |
|
|
754 |
0 |
@staticmethod |
755 |
0 |
def _format_out(data, token_info=None, _format=None): |
756 |
|
""" |
757 |
|
return string of dictionary data according to requested json, yaml, xml. By default json |
758 |
|
:param data: response to be sent. Can be a dict, text or file |
759 |
|
:param token_info: Contains among other username and project |
760 |
|
:param _format: The format to be set as Content-Type if data is a file |
761 |
|
:return: None |
762 |
|
""" |
763 |
0 |
accept = cherrypy.request.headers.get("Accept") |
764 |
0 |
if data is None: |
765 |
0 |
if accept and "text/html" in accept: |
766 |
0 |
return html.format( |
767 |
|
data, cherrypy.request, cherrypy.response, token_info |
768 |
|
) |
769 |
|
# cherrypy.response.status = HTTPStatus.NO_CONTENT.value |
770 |
0 |
return |
771 |
0 |
elif hasattr(data, "read"): # file object |
772 |
0 |
if _format: |
773 |
0 |
cherrypy.response.headers["Content-Type"] = _format |
774 |
0 |
elif "b" in data.mode: # binariy asssumig zip |
775 |
0 |
cherrypy.response.headers["Content-Type"] = "application/zip" |
776 |
|
else: |
777 |
0 |
cherrypy.response.headers["Content-Type"] = "text/plain" |
778 |
|
# TODO check that cherrypy close file. If not implement pending things to close per thread next |
779 |
0 |
return data |
780 |
0 |
if accept: |
781 |
0 |
if "text/html" in accept: |
782 |
0 |
return html.format( |
783 |
|
data, cherrypy.request, cherrypy.response, token_info |
784 |
|
) |
785 |
0 |
elif "application/yaml" in accept or "*/*" in accept: |
786 |
0 |
pass |
787 |
0 |
elif "application/json" in accept or ( |
788 |
|
cherrypy.response.status and cherrypy.response.status >= 300 |
789 |
|
): |
790 |
0 |
cherrypy.response.headers[ |
791 |
|
"Content-Type" |
792 |
|
] = "application/json; charset=utf-8" |
793 |
0 |
a = json.dumps(data, indent=4) + "\n" |
794 |
0 |
return a.encode("utf8") |
795 |
0 |
cherrypy.response.headers["Content-Type"] = "application/yaml" |
796 |
0 |
return yaml.safe_dump( |
797 |
|
data, |
798 |
|
explicit_start=True, |
799 |
|
indent=4, |
800 |
|
default_flow_style=False, |
801 |
|
tags=False, |
802 |
|
encoding="utf-8", |
803 |
|
allow_unicode=True, |
804 |
|
) # , canonical=True, default_style='"' |
805 |
|
|
806 |
0 |
@cherrypy.expose |
807 |
0 |
def index(self, *args, **kwargs): |
808 |
0 |
token_info = None |
809 |
0 |
try: |
810 |
0 |
if cherrypy.request.method == "GET": |
811 |
0 |
token_info = self.authenticator.authorize() |
812 |
0 |
outdata = token_info # Home page |
813 |
|
else: |
814 |
0 |
raise cherrypy.HTTPError( |
815 |
|
HTTPStatus.METHOD_NOT_ALLOWED.value, |
816 |
|
"Method {} not allowed for tokens".format(cherrypy.request.method), |
817 |
|
) |
818 |
|
|
819 |
0 |
return self._format_out(outdata, token_info) |
820 |
|
|
821 |
0 |
except (EngineException, AuthException) as e: |
822 |
|
# cherrypy.log("index Exception {}".format(e)) |
823 |
0 |
cherrypy.response.status = e.http_code.value |
824 |
0 |
return self._format_out("Welcome to OSM!", token_info) |
825 |
|
|
826 |
0 |
@cherrypy.expose |
827 |
0 |
def version(self, *args, **kwargs): |
828 |
|
# TODO consider to remove and provide version using the static version file |
829 |
0 |
try: |
830 |
0 |
if cherrypy.request.method != "GET": |
831 |
0 |
raise NbiException( |
832 |
|
"Only method GET is allowed", HTTPStatus.METHOD_NOT_ALLOWED |
833 |
|
) |
834 |
0 |
elif args or kwargs: |
835 |
0 |
raise NbiException( |
836 |
|
"Invalid URL or query string for version", |
837 |
|
HTTPStatus.METHOD_NOT_ALLOWED, |
838 |
|
) |
839 |
|
# TODO include version of other modules, pick up from some kafka admin message |
840 |
0 |
osm_nbi_version = {"version": nbi_version, "date": nbi_version_date} |
841 |
0 |
return self._format_out(osm_nbi_version) |
842 |
0 |
except NbiException as e: |
843 |
0 |
cherrypy.response.status = e.http_code.value |
844 |
0 |
problem_details = { |
845 |
|
"code": e.http_code.name, |
846 |
|
"status": e.http_code.value, |
847 |
|
"detail": str(e), |
848 |
|
} |
849 |
0 |
return self._format_out(problem_details, None) |
850 |
|
|
851 |
0 |
def domain(self): |
852 |
0 |
try: |
853 |
0 |
domains = { |
854 |
|
"user_domain_name": cherrypy.tree.apps["/osm"] |
855 |
|
.config["authentication"] |
856 |
|
.get("user_domain_name"), |
857 |
|
"project_domain_name": cherrypy.tree.apps["/osm"] |
858 |
|
.config["authentication"] |
859 |
|
.get("project_domain_name"), |
860 |
|
} |
861 |
0 |
return self._format_out(domains) |
862 |
0 |
except NbiException as e: |
863 |
0 |
cherrypy.response.status = e.http_code.value |
864 |
0 |
problem_details = { |
865 |
|
"code": e.http_code.name, |
866 |
|
"status": e.http_code.value, |
867 |
|
"detail": str(e), |
868 |
|
} |
869 |
0 |
return self._format_out(problem_details, None) |
870 |
|
|
871 |
0 |
@staticmethod |
872 |
0 |
def _format_login(token_info): |
873 |
|
""" |
874 |
|
Changes cherrypy.request.login to include username/project_name;session so that cherrypy access log will |
875 |
|
log this information |
876 |
|
:param token_info: Dictionary with token content |
877 |
|
:return: None |
878 |
|
""" |
879 |
0 |
cherrypy.request.login = token_info.get("username", "-") |
880 |
0 |
if token_info.get("project_name"): |
881 |
0 |
cherrypy.request.login += "/" + token_info["project_name"] |
882 |
0 |
if token_info.get("id"): |
883 |
0 |
cherrypy.request.login += ";session=" + token_info["id"][0:12] |
884 |
|
|
885 |
|
# NS Fault Management |
886 |
0 |
@cherrypy.expose |
887 |
0 |
def nsfm(self, version=None, topic=None, uuid=None, project_name=None, ns_id=None, *args, **kwargs): |
888 |
0 |
if topic == 'alarms': |
889 |
0 |
try: |
890 |
0 |
method = cherrypy.request.method |
891 |
0 |
role_permission = self._check_valid_url_method(method, "nsfm", version, topic, None, None, *args) |
892 |
0 |
query_string_operations = self._extract_query_string_operations(kwargs, method) |
893 |
|
|
894 |
0 |
self.authenticator.authorize(role_permission, query_string_operations, None) |
895 |
|
|
896 |
|
# to handle get request |
897 |
0 |
if cherrypy.request.method == 'GET': |
898 |
|
# if request is on basis of uuid |
899 |
0 |
if uuid and uuid != 'None': |
900 |
0 |
try: |
901 |
0 |
alarm = self.engine.db.get_one("alarms", {"uuid": uuid}) |
902 |
0 |
alarm_action = self.engine.db.get_one("alarms_action", {"uuid": uuid}) |
903 |
0 |
alarm.update(alarm_action) |
904 |
0 |
vnf = self.engine.db.get_one("vnfrs", {"nsr-id-ref": alarm["tags"]["ns_id"]}) |
905 |
0 |
alarm["vnf-id"] = vnf["_id"] |
906 |
0 |
return self._format_out(str(alarm)) |
907 |
0 |
except Exception: |
908 |
0 |
return self._format_out("Please provide valid alarm uuid") |
909 |
0 |
elif ns_id and ns_id != 'None': |
910 |
|
# if request is on basis of ns_id |
911 |
0 |
try: |
912 |
0 |
alarms = self.engine.db.get_list("alarms", {"tags.ns_id": ns_id}) |
913 |
0 |
for alarm in alarms: |
914 |
0 |
alarm_action = self.engine.db.get_one("alarms_action", {"uuid": alarm['uuid']}) |
915 |
0 |
alarm.update(alarm_action) |
916 |
0 |
return self._format_out(str(alarms)) |
917 |
0 |
except Exception: |
918 |
0 |
return self._format_out("Please provide valid ns id") |
919 |
|
else: |
920 |
|
# to return only alarm which are related to given project |
921 |
0 |
project = self.engine.db.get_one("projects", {"name": project_name}) |
922 |
0 |
project_id = project.get('_id') |
923 |
0 |
ns_list = self.engine.db.get_list("nsrs", {"_admin.projects_read": project_id}) |
924 |
0 |
ns_ids = [] |
925 |
0 |
for ns in ns_list: |
926 |
0 |
ns_ids.append(ns.get("_id")) |
927 |
0 |
alarms = self.engine.db.get_list("alarms") |
928 |
0 |
alarm_list = [alarm for alarm in alarms if alarm["tags"]["ns_id"] in ns_ids] |
929 |
0 |
for alrm in alarm_list: |
930 |
0 |
action = self.engine.db.get_one("alarms_action", {"uuid": alrm.get("uuid")}) |
931 |
0 |
alrm.update(action) |
932 |
0 |
return self._format_out(str(alarm_list)) |
933 |
|
# to handle patch request for alarm update |
934 |
0 |
elif cherrypy.request.method == 'PATCH': |
935 |
0 |
data = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader) |
936 |
0 |
try: |
937 |
|
# check if uuid is valid |
938 |
0 |
self.engine.db.get_one("alarms", {"uuid": data.get("uuid")}) |
939 |
0 |
except Exception: |
940 |
0 |
return self._format_out("Please provide valid alarm uuid.") |
941 |
0 |
if data.get("is_enable") is not None: |
942 |
0 |
if data.get("is_enable"): |
943 |
0 |
alarm_status = 'ok' |
944 |
|
else: |
945 |
0 |
alarm_status = 'disabled' |
946 |
0 |
self.engine.db.set_one("alarms", {"uuid": data.get("uuid")}, |
947 |
|
{"alarm_status": alarm_status}) |
948 |
|
else: |
949 |
0 |
self.engine.db.set_one("alarms", {"uuid": data.get("uuid")}, |
950 |
|
{"threshold": data.get("threshold")}) |
951 |
0 |
return self._format_out("Alarm updated") |
952 |
0 |
except Exception as e: |
953 |
0 |
cherrypy.response.status = e.http_code.value |
954 |
0 |
if isinstance(e, (NbiException, EngineException, DbException, FsException, MsgException, AuthException, |
955 |
|
ValidationError, AuthconnException)): |
956 |
0 |
http_code_value = cherrypy.response.status = e.http_code.value |
957 |
0 |
http_code_name = e.http_code.name |
958 |
0 |
cherrypy.log("Exception {}".format(e)) |
959 |
|
else: |
960 |
0 |
http_code_value = cherrypy.response.status = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR |
961 |
0 |
cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True) |
962 |
0 |
http_code_name = HTTPStatus.BAD_REQUEST.name |
963 |
0 |
problem_details = { |
964 |
|
"code": http_code_name, |
965 |
|
"status": http_code_value, |
966 |
|
"detail": str(e), |
967 |
|
} |
968 |
0 |
return self._format_out(problem_details) |
969 |
|
|
970 |
0 |
@cherrypy.expose |
971 |
0 |
def token(self, method, token_id=None, kwargs=None): |
972 |
0 |
token_info = None |
973 |
|
# self.engine.load_dbase(cherrypy.request.app.config) |
974 |
0 |
indata = self._format_in(kwargs) |
975 |
0 |
if not isinstance(indata, dict): |
976 |
0 |
raise NbiException( |
977 |
|
"Expected application/yaml or application/json Content-Type", |
978 |
|
HTTPStatus.BAD_REQUEST, |
979 |
|
) |
980 |
|
|
981 |
0 |
if method == "GET": |
982 |
0 |
token_info = self.authenticator.authorize() |
983 |
|
# for logging |
984 |
0 |
self._format_login(token_info) |
985 |
0 |
if token_id: |
986 |
0 |
outdata = self.authenticator.get_token(token_info, token_id) |
987 |
|
else: |
988 |
0 |
outdata = self.authenticator.get_token_list(token_info) |
989 |
0 |
elif method == "POST": |
990 |
0 |
try: |
991 |
0 |
token_info = self.authenticator.authorize() |
992 |
0 |
except Exception: |
993 |
0 |
token_info = None |
994 |
0 |
if kwargs: |
995 |
0 |
indata.update(kwargs) |
996 |
|
# This is needed to log the user when authentication fails |
997 |
0 |
cherrypy.request.login = "{}".format(indata.get("username", "-")) |
998 |
0 |
outdata = token_info = self.authenticator.new_token( |
999 |
|
token_info, indata, cherrypy.request.remote |
1000 |
|
) |
1001 |
0 |
cherrypy.session["Authorization"] = outdata["_id"] |
1002 |
0 |
self._set_location_header("admin", "v1", "tokens", outdata["_id"]) |
1003 |
|
# for logging |
1004 |
0 |
self._format_login(token_info) |
1005 |
|
# password expiry check |
1006 |
0 |
if self.authenticator.check_password_expiry(outdata): |
1007 |
0 |
outdata = {"id": outdata["id"], |
1008 |
|
"message": "change_password", |
1009 |
|
"user_id": outdata["user_id"] |
1010 |
|
} |
1011 |
|
# cherrypy.response.cookie["Authorization"] = outdata["id"] |
1012 |
|
# cherrypy.response.cookie["Authorization"]['expires'] = 3600 |
1013 |
0 |
elif method == "DELETE": |
1014 |
0 |
if not token_id and "id" in kwargs: |
1015 |
0 |
token_id = kwargs["id"] |
1016 |
0 |
elif not token_id: |
1017 |
0 |
token_info = self.authenticator.authorize() |
1018 |
|
# for logging |
1019 |
0 |
self._format_login(token_info) |
1020 |
0 |
token_id = token_info["_id"] |
1021 |
0 |
outdata = self.authenticator.del_token(token_id) |
1022 |
0 |
token_info = None |
1023 |
0 |
cherrypy.session["Authorization"] = "logout" |
1024 |
|
# cherrypy.response.cookie["Authorization"] = token_id |
1025 |
|
# cherrypy.response.cookie["Authorization"]['expires'] = 0 |
1026 |
|
else: |
1027 |
0 |
raise NbiException( |
1028 |
|
"Method {} not allowed for token".format(method), |
1029 |
|
HTTPStatus.METHOD_NOT_ALLOWED, |
1030 |
|
) |
1031 |
0 |
return self._format_out(outdata, token_info) |
1032 |
|
|
1033 |
0 |
@cherrypy.expose |
1034 |
0 |
def test(self, *args, **kwargs): |
1035 |
0 |
if not cherrypy.config.get("server.enable_test") or ( |
1036 |
|
isinstance(cherrypy.config["server.enable_test"], str) |
1037 |
|
and cherrypy.config["server.enable_test"].lower() == "false" |
1038 |
|
): |
1039 |
0 |
cherrypy.response.status = HTTPStatus.METHOD_NOT_ALLOWED.value |
1040 |
0 |
return "test URL is disabled" |
1041 |
0 |
thread_info = None |
1042 |
0 |
if args and args[0] == "help": |
1043 |
0 |
return ( |
1044 |
|
"<html><pre>\ninit\nfile/<name> download file\ndb-clear/table\nfs-clear[/folder]\nlogin\nlogin2\n" |
1045 |
|
"sleep/<time>\nmessage/topic\n</pre></html>" |
1046 |
|
) |
1047 |
|
|
1048 |
0 |
elif args and args[0] == "init": |
1049 |
0 |
try: |
1050 |
|
# self.engine.load_dbase(cherrypy.request.app.config) |
1051 |
0 |
self.engine.create_admin() |
1052 |
0 |
return "Done. User 'admin', password 'admin' created" |
1053 |
0 |
except Exception: |
1054 |
0 |
cherrypy.response.status = HTTPStatus.FORBIDDEN.value |
1055 |
0 |
return self._format_out("Database already initialized") |
1056 |
0 |
elif args and args[0] == "file": |
1057 |
0 |
return cherrypy.lib.static.serve_file( |
1058 |
|
cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1], |
1059 |
|
"text/plain", |
1060 |
|
"attachment", |
1061 |
|
) |
1062 |
0 |
elif args and args[0] == "file2": |
1063 |
0 |
f_path = ( |
1064 |
|
cherrypy.tree.apps["/osm"].config["storage"]["path"] + "/" + args[1] |
1065 |
|
) |
1066 |
0 |
f = open(f_path, "r") |
1067 |
0 |
cherrypy.response.headers["Content-type"] = "text/plain" |
1068 |
0 |
return f |
1069 |
|
|
1070 |
0 |
elif len(args) == 2 and args[0] == "db-clear": |
1071 |
0 |
deleted_info = self.engine.db.del_list(args[1], kwargs) |
1072 |
0 |
return "{} {} deleted\n".format(deleted_info["deleted"], args[1]) |
1073 |
0 |
elif len(args) and args[0] == "fs-clear": |
1074 |
0 |
if len(args) >= 2: |
1075 |
0 |
folders = (args[1],) |
1076 |
|
else: |
1077 |
0 |
folders = self.engine.fs.dir_ls(".") |
1078 |
0 |
for folder in folders: |
1079 |
0 |
self.engine.fs.file_delete(folder) |
1080 |
0 |
return ",".join(folders) + " folders deleted\n" |
1081 |
0 |
elif args and args[0] == "login": |
1082 |
0 |
if not cherrypy.request.headers.get("Authorization"): |
1083 |
0 |
cherrypy.response.headers[ |
1084 |
|
"WWW-Authenticate" |
1085 |
|
] = 'Basic realm="Access to OSM site", charset="UTF-8"' |
1086 |
0 |
cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value |
1087 |
0 |
elif args and args[0] == "login2": |
1088 |
0 |
if not cherrypy.request.headers.get("Authorization"): |
1089 |
0 |
cherrypy.response.headers[ |
1090 |
|
"WWW-Authenticate" |
1091 |
|
] = 'Bearer realm="Access to OSM site"' |
1092 |
0 |
cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value |
1093 |
0 |
elif args and args[0] == "sleep": |
1094 |
0 |
sleep_time = 5 |
1095 |
0 |
try: |
1096 |
0 |
sleep_time = int(args[1]) |
1097 |
0 |
except Exception: |
1098 |
0 |
cherrypy.response.status = HTTPStatus.FORBIDDEN.value |
1099 |
0 |
return self._format_out("Database already initialized") |
1100 |
0 |
thread_info = cherrypy.thread_data |
1101 |
0 |
print(thread_info) |
1102 |
0 |
time.sleep(sleep_time) |
1103 |
|
# thread_info |
1104 |
0 |
elif len(args) >= 2 and args[0] == "message": |
1105 |
0 |
main_topic = args[1] |
1106 |
0 |
return_text = "<html><pre>{} ->\n".format(main_topic) |
1107 |
0 |
try: |
1108 |
0 |
if cherrypy.request.method == "POST": |
1109 |
0 |
to_send = yaml.load(cherrypy.request.body, Loader=yaml.SafeLoader) |
1110 |
0 |
for k, v in to_send.items(): |
1111 |
0 |
self.engine.msg.write(main_topic, k, v) |
1112 |
0 |
return_text += " {}: {}\n".format(k, v) |
1113 |
0 |
elif cherrypy.request.method == "GET": |
1114 |
0 |
for k, v in kwargs.items(): |
1115 |
0 |
v_dict = yaml.load(v, Loader=yaml.SafeLoader) |
1116 |
0 |
self.engine.msg.write(main_topic, k, v_dict) |
1117 |
0 |
return_text += " {}: {}\n".format(k, v_dict) |
1118 |
0 |
except Exception as e: |
1119 |
0 |
return_text += "Error: " + str(e) |
1120 |
0 |
return_text += "</pre></html>\n" |
1121 |
0 |
return return_text |
1122 |
|
|
1123 |
0 |
return_text = ( |
1124 |
|
"<html><pre>\nheaders:\n args: {}\n".format(args) |
1125 |
|
+ " kwargs: {}\n".format(kwargs) |
1126 |
|
+ " headers: {}\n".format(cherrypy.request.headers) |
1127 |
|
+ " path_info: {}\n".format(cherrypy.request.path_info) |
1128 |
|
+ " query_string: {}\n".format(cherrypy.request.query_string) |
1129 |
|
+ " session: {}\n".format(cherrypy.session) |
1130 |
|
+ " cookie: {}\n".format(cherrypy.request.cookie) |
1131 |
|
+ " method: {}\n".format(cherrypy.request.method) |
1132 |
|
+ " session: {}\n".format(cherrypy.session.get("fieldname")) |
1133 |
|
+ " body:\n" |
1134 |
|
) |
1135 |
0 |
return_text += " length: {}\n".format(cherrypy.request.body.length) |
1136 |
0 |
if cherrypy.request.body.length: |
1137 |
0 |
return_text += " content: {}\n".format( |
1138 |
|
str( |
1139 |
|
cherrypy.request.body.read( |
1140 |
|
int(cherrypy.request.headers.get("Content-Length", 0)) |
1141 |
|
) |
1142 |
|
) |
1143 |
|
) |
1144 |
0 |
if thread_info: |
1145 |
0 |
return_text += "thread: {}\n".format(thread_info) |
1146 |
0 |
return_text += "</pre></html>" |
1147 |
0 |
return return_text |
1148 |
|
|
1149 |
0 |
@staticmethod |
1150 |
0 |
def _check_valid_url_method(method, *args): |
1151 |
0 |
if len(args) < 3: |
1152 |
0 |
raise NbiException( |
1153 |
|
"URL must contain at least 'main_topic/version/topic'", |
1154 |
|
HTTPStatus.METHOD_NOT_ALLOWED, |
1155 |
|
) |
1156 |
|
|
1157 |
0 |
reference = valid_url_methods |
1158 |
0 |
for arg in args: |
1159 |
0 |
if arg is None: |
1160 |
0 |
break |
1161 |
0 |
if not isinstance(reference, dict): |
1162 |
0 |
raise NbiException( |
1163 |
|
"URL contains unexpected extra items '{}'".format(arg), |
1164 |
|
HTTPStatus.METHOD_NOT_ALLOWED, |
1165 |
|
) |
1166 |
|
|
1167 |
0 |
if arg in reference: |
1168 |
0 |
reference = reference[arg] |
1169 |
0 |
elif "<ID>" in reference: |
1170 |
0 |
reference = reference["<ID>"] |
1171 |
0 |
elif "*" in reference: |
1172 |
|
# if there is content |
1173 |
0 |
if reference["*"]: |
1174 |
0 |
reference = reference["*"] |
1175 |
0 |
break |
1176 |
|
else: |
1177 |
0 |
raise NbiException( |
1178 |
|
"Unexpected URL item {}".format(arg), HTTPStatus.METHOD_NOT_ALLOWED |
1179 |
|
) |
1180 |
0 |
if "TODO" in reference and method in reference["TODO"]: |
1181 |
0 |
raise NbiException( |
1182 |
|
"Method {} not supported yet for this URL".format(method), |
1183 |
|
HTTPStatus.NOT_IMPLEMENTED, |
1184 |
|
) |
1185 |
0 |
elif "METHODS" in reference and method not in reference["METHODS"]: |
1186 |
0 |
raise NbiException( |
1187 |
|
"Method {} not supported for this URL".format(method), |
1188 |
|
HTTPStatus.METHOD_NOT_ALLOWED, |
1189 |
|
) |
1190 |
0 |
return reference["ROLE_PERMISSION"] + method.lower() |
1191 |
|
|
1192 |
0 |
@staticmethod |
1193 |
0 |
def _set_location_header(main_topic, version, topic, id): |
1194 |
|
""" |
1195 |
|
Insert response header Location with the URL of created item base on URL params |
1196 |
|
:param main_topic: |
1197 |
|
:param version: |
1198 |
|
:param topic: |
1199 |
|
:param id: |
1200 |
|
:return: None |
1201 |
|
""" |
1202 |
|
# Use cherrypy.request.base for absoluted path and make use of request.header HOST just in case behind aNAT |
1203 |
0 |
cherrypy.response.headers["Location"] = "/osm/{}/{}/{}/{}".format( |
1204 |
|
main_topic, version, topic, id |
1205 |
|
) |
1206 |
0 |
return |
1207 |
|
|
1208 |
0 |
@staticmethod |
1209 |
0 |
def _extract_query_string_operations(kwargs, method): |
1210 |
|
""" |
1211 |
|
|
1212 |
|
:param kwargs: |
1213 |
|
:return: |
1214 |
|
""" |
1215 |
0 |
query_string_operations = [] |
1216 |
0 |
if kwargs: |
1217 |
0 |
for qs in ("FORCE", "PUBLIC", "ADMIN", "SET_PROJECT"): |
1218 |
0 |
if qs in kwargs and kwargs[qs].lower() != "false": |
1219 |
0 |
query_string_operations.append(qs.lower() + ":" + method.lower()) |
1220 |
0 |
return query_string_operations |
1221 |
|
|
1222 |
0 |
@staticmethod |
1223 |
0 |
def _manage_admin_query(token_info, kwargs, method, _id): |
1224 |
|
""" |
1225 |
|
Processes the administrator query inputs (if any) of FORCE, ADMIN, PUBLIC, SET_PROJECT |
1226 |
|
Check that users has rights to use them and returs the admin_query |
1227 |
|
:param token_info: token_info rights obtained by token |
1228 |
|
:param kwargs: query string input. |
1229 |
|
:param method: http method: GET, POSST, PUT, ... |
1230 |
|
:param _id: |
1231 |
|
:return: admin_query dictionary with keys: |
1232 |
|
public: True, False or None |
1233 |
|
force: True or False |
1234 |
|
project_id: tuple with projects used for accessing an element |
1235 |
|
set_project: tuple with projects that a created element will belong to |
1236 |
|
method: show, list, delete, write |
1237 |
|
""" |
1238 |
0 |
admin_query = { |
1239 |
|
"force": False, |
1240 |
|
"project_id": (token_info["project_id"],), |
1241 |
|
"username": token_info["username"], |
1242 |
|
"admin": token_info["admin"], |
1243 |
|
"public": None, |
1244 |
|
"allow_show_user_project_role": token_info["allow_show_user_project_role"], |
1245 |
|
} |
1246 |
0 |
if kwargs: |
1247 |
|
# FORCE |
1248 |
0 |
if "FORCE" in kwargs: |
1249 |
0 |
if ( |
1250 |
|
kwargs["FORCE"].lower() != "false" |
1251 |
|
): # if None or True set force to True |
1252 |
0 |
admin_query["force"] = True |
1253 |
0 |
del kwargs["FORCE"] |
1254 |
|
# PUBLIC |
1255 |
0 |
if "PUBLIC" in kwargs: |
1256 |
0 |
if ( |
1257 |
|
kwargs["PUBLIC"].lower() != "false" |
1258 |
|
): # if None or True set public to True |
1259 |
0 |
admin_query["public"] = True |
1260 |
|
else: |
1261 |
0 |
admin_query["public"] = False |
1262 |
0 |
del kwargs["PUBLIC"] |
1263 |
|
# ADMIN |
1264 |
0 |
if "ADMIN" in kwargs: |
1265 |
0 |
behave_as = kwargs.pop("ADMIN") |
1266 |
0 |
if behave_as.lower() != "false": |
1267 |
0 |
if not token_info["admin"]: |
1268 |
0 |
raise NbiException( |
1269 |
|
"Only admin projects can use 'ADMIN' query string", |
1270 |
|
HTTPStatus.UNAUTHORIZED, |
1271 |
|
) |
1272 |
0 |
if ( |
1273 |
|
not behave_as or behave_as.lower() == "true" |
1274 |
|
): # convert True, None to empty list |
1275 |
0 |
admin_query["project_id"] = () |
1276 |
0 |
elif isinstance(behave_as, (list, tuple)): |
1277 |
0 |
admin_query["project_id"] = behave_as |
1278 |
|
else: # isinstance(behave_as, str) |
1279 |
0 |
admin_query["project_id"] = (behave_as,) |
1280 |
0 |
if "SET_PROJECT" in kwargs: |
1281 |
0 |
set_project = kwargs.pop("SET_PROJECT") |
1282 |
0 |
if not set_project: |
1283 |
0 |
admin_query["set_project"] = list(admin_query["project_id"]) |
1284 |
|
else: |
1285 |
0 |
if isinstance(set_project, str): |
1286 |
0 |
set_project = (set_project,) |
1287 |
0 |
if admin_query["project_id"]: |
1288 |
0 |
for p in set_project: |
1289 |
0 |
if p not in admin_query["project_id"]: |
1290 |
0 |
raise NbiException( |
1291 |
|
"Unauthorized for 'SET_PROJECT={p}'. Try with 'ADMIN=True' or " |
1292 |
|
"'ADMIN='{p}'".format(p=p), |
1293 |
|
HTTPStatus.UNAUTHORIZED, |
1294 |
|
) |
1295 |
0 |
admin_query["set_project"] = set_project |
1296 |
|
|
1297 |
|
# PROJECT_READ |
1298 |
|
# if "PROJECT_READ" in kwargs: |
1299 |
|
# admin_query["project"] = kwargs.pop("project") |
1300 |
|
# if admin_query["project"] == token_info["project_id"]: |
1301 |
0 |
if method == "GET": |
1302 |
0 |
if _id: |
1303 |
0 |
admin_query["method"] = "show" |
1304 |
|
else: |
1305 |
0 |
admin_query["method"] = "list" |
1306 |
0 |
elif method == "DELETE": |
1307 |
0 |
admin_query["method"] = "delete" |
1308 |
|
else: |
1309 |
0 |
admin_query["method"] = "write" |
1310 |
0 |
return admin_query |
1311 |
|
|
1312 |
0 |
@cherrypy.expose |
1313 |
0 |
def default( |
1314 |
|
self, |
1315 |
|
main_topic=None, |
1316 |
|
version=None, |
1317 |
|
topic=None, |
1318 |
|
_id=None, |
1319 |
|
item=None, |
1320 |
|
*args, |
1321 |
|
**kwargs |
1322 |
|
): |
1323 |
0 |
token_info = None |
1324 |
0 |
outdata = None |
1325 |
0 |
_format = None |
1326 |
0 |
method = "DONE" |
1327 |
0 |
engine_topic = None |
1328 |
0 |
rollback = [] |
1329 |
0 |
engine_session = None |
1330 |
0 |
try: |
1331 |
0 |
if not main_topic or not version or not topic: |
1332 |
0 |
raise NbiException( |
1333 |
|
"URL must contain at least 'main_topic/version/topic'", |
1334 |
|
HTTPStatus.METHOD_NOT_ALLOWED, |
1335 |
|
) |
1336 |
0 |
if main_topic not in ( |
1337 |
|
"admin", |
1338 |
|
"vnfpkgm", |
1339 |
|
"nsd", |
1340 |
|
"nslcm", |
1341 |
|
"pdu", |
1342 |
|
"nst", |
1343 |
|
"nsilcm", |
1344 |
|
"nspm", |
1345 |
|
"vnflcm", |
1346 |
|
): |
1347 |
0 |
raise NbiException( |
1348 |
|
"URL main_topic '{}' not supported".format(main_topic), |
1349 |
|
HTTPStatus.METHOD_NOT_ALLOWED, |
1350 |
|
) |
1351 |
0 |
if version != "v1": |
1352 |
0 |
raise NbiException( |
1353 |
|
"URL version '{}' not supported".format(version), |
1354 |
|
HTTPStatus.METHOD_NOT_ALLOWED, |
1355 |
|
) |
1356 |
|
|
1357 |
0 |
if ( |
1358 |
|
kwargs |
1359 |
|
and "METHOD" in kwargs |
1360 |
|
and kwargs["METHOD"] in ("PUT", "POST", "DELETE", "GET", "PATCH") |
1361 |
|
): |
1362 |
0 |
method = kwargs.pop("METHOD") |
1363 |
|
else: |
1364 |
0 |
method = cherrypy.request.method |
1365 |
|
|
1366 |
0 |
role_permission = self._check_valid_url_method( |
1367 |
|
method, main_topic, version, topic, _id, item, *args |
1368 |
|
) |
1369 |
0 |
query_string_operations = self._extract_query_string_operations( |
1370 |
|
kwargs, method |
1371 |
|
) |
1372 |
0 |
if main_topic == "admin" and topic == "tokens": |
1373 |
0 |
return self.token(method, _id, kwargs) |
1374 |
0 |
token_info = self.authenticator.authorize( |
1375 |
|
role_permission, query_string_operations, _id |
1376 |
|
) |
1377 |
0 |
if main_topic == "admin" and topic == "domains": |
1378 |
0 |
return self.domain() |
1379 |
0 |
engine_session = self._manage_admin_query(token_info, kwargs, method, _id) |
1380 |
0 |
indata = self._format_in(kwargs) |
1381 |
0 |
engine_topic = topic |
1382 |
|
|
1383 |
0 |
if item and topic != "pm_jobs": |
1384 |
0 |
engine_topic = item |
1385 |
|
|
1386 |
0 |
if main_topic == "nsd": |
1387 |
0 |
engine_topic = "nsds" |
1388 |
0 |
elif main_topic == "vnfpkgm": |
1389 |
0 |
engine_topic = "vnfds" |
1390 |
0 |
if topic == "vnfpkg_op_occs": |
1391 |
0 |
engine_topic = "vnfpkgops" |
1392 |
0 |
if topic == "vnf_packages" and item == "action": |
1393 |
0 |
engine_topic = "vnfpkgops" |
1394 |
0 |
elif main_topic == "nslcm": |
1395 |
0 |
engine_topic = "nsrs" |
1396 |
0 |
if topic == "ns_lcm_op_occs": |
1397 |
0 |
engine_topic = "nslcmops" |
1398 |
0 |
if topic == "vnfrs" or topic == "vnf_instances": |
1399 |
0 |
engine_topic = "vnfrs" |
1400 |
0 |
elif main_topic == "vnflcm": |
1401 |
0 |
if topic == "vnf_lcm_op_occs": |
1402 |
0 |
engine_topic = "vnflcmops" |
1403 |
0 |
elif main_topic == "nst": |
1404 |
0 |
engine_topic = "nsts" |
1405 |
0 |
elif main_topic == "nsilcm": |
1406 |
0 |
engine_topic = "nsis" |
1407 |
0 |
if topic == "nsi_lcm_op_occs": |
1408 |
0 |
engine_topic = "nsilcmops" |
1409 |
0 |
elif main_topic == "pdu": |
1410 |
0 |
engine_topic = "pdus" |
1411 |
0 |
if ( |
1412 |
|
engine_topic == "vims" |
1413 |
|
): # TODO this is for backward compatibility, it will be removed in the future |
1414 |
0 |
engine_topic = "vim_accounts" |
1415 |
|
|
1416 |
0 |
if topic == "subscriptions": |
1417 |
0 |
engine_topic = main_topic + "_" + topic |
1418 |
|
|
1419 |
0 |
if method == "GET": |
1420 |
0 |
if item in ( |
1421 |
|
"nsd_content", |
1422 |
|
"package_content", |
1423 |
|
"artifacts", |
1424 |
|
"vnfd", |
1425 |
|
"nsd", |
1426 |
|
"nst", |
1427 |
|
"nst_content", |
1428 |
|
): |
1429 |
0 |
if item in ("vnfd", "nsd", "nst"): |
1430 |
0 |
path = "$DESCRIPTOR" |
1431 |
0 |
elif args: |
1432 |
0 |
path = args |
1433 |
0 |
elif item == "artifacts": |
1434 |
0 |
path = () |
1435 |
|
else: |
1436 |
0 |
path = None |
1437 |
0 |
file, _format = self.engine.get_file( |
1438 |
|
engine_session, |
1439 |
|
engine_topic, |
1440 |
|
_id, |
1441 |
|
path, |
1442 |
|
cherrypy.request.headers.get("Accept"), |
1443 |
|
) |
1444 |
0 |
outdata = file |
1445 |
0 |
elif not _id: |
1446 |
0 |
outdata = self.engine.get_item_list( |
1447 |
|
engine_session, engine_topic, kwargs, api_req=True |
1448 |
|
) |
1449 |
|
else: |
1450 |
0 |
if item == "reports": |
1451 |
|
# TODO check that project_id (_id in this context) has permissions |
1452 |
0 |
_id = args[0] |
1453 |
0 |
filter_q = None |
1454 |
0 |
if "vcaStatusRefresh" in kwargs: |
1455 |
0 |
filter_q = {"vcaStatusRefresh": kwargs["vcaStatusRefresh"]} |
1456 |
0 |
outdata = self.engine.get_item(engine_session, engine_topic, _id, filter_q, True) |
1457 |
|
|
1458 |
0 |
elif method == "POST": |
1459 |
0 |
cherrypy.response.status = HTTPStatus.CREATED.value |
1460 |
0 |
if topic in ( |
1461 |
|
"ns_descriptors_content", |
1462 |
|
"vnf_packages_content", |
1463 |
|
"netslice_templates_content", |
1464 |
|
): |
1465 |
0 |
_id = cherrypy.request.headers.get("Transaction-Id") |
1466 |
0 |
if not _id: |
1467 |
0 |
_id, _ = self.engine.new_item( |
1468 |
|
rollback, |
1469 |
|
engine_session, |
1470 |
|
engine_topic, |
1471 |
|
{}, |
1472 |
|
None, |
1473 |
|
cherrypy.request.headers, |
1474 |
|
) |
1475 |
0 |
completed = self.engine.upload_content( |
1476 |
|
engine_session, |
1477 |
|
engine_topic, |
1478 |
|
_id, |
1479 |
|
indata, |
1480 |
|
kwargs, |
1481 |
|
cherrypy.request.headers, |
1482 |
|
) |
1483 |
0 |
if completed: |
1484 |
0 |
self._set_location_header(main_topic, version, topic, _id) |
1485 |
|
else: |
1486 |
0 |
cherrypy.response.headers["Transaction-Id"] = _id |
1487 |
0 |
outdata = {"id": _id} |
1488 |
0 |
elif topic == "ns_instances_content": |
1489 |
|
# creates NSR |
1490 |
0 |
_id, _ = self.engine.new_item( |
1491 |
|
rollback, engine_session, engine_topic, indata, kwargs |
1492 |
|
) |
1493 |
|
# creates nslcmop |
1494 |
0 |
indata["lcmOperationType"] = "instantiate" |
1495 |
0 |
indata["nsInstanceId"] = _id |
1496 |
0 |
nslcmop_id, _ = self.engine.new_item( |
1497 |
|
rollback, engine_session, "nslcmops", indata, None |
1498 |
|
) |
1499 |
0 |
self._set_location_header(main_topic, version, topic, _id) |
1500 |
0 |
outdata = {"id": _id, "nslcmop_id": nslcmop_id} |
1501 |
0 |
elif topic == "ns_instances" and item: |
1502 |
0 |
indata["lcmOperationType"] = item |
1503 |
0 |
indata["nsInstanceId"] = _id |
1504 |
0 |
_id, _ = self.engine.new_item( |
1505 |
|
rollback, engine_session, "nslcmops", indata, kwargs |
1506 |
|
) |
1507 |
0 |
self._set_location_header( |
1508 |
|
main_topic, version, "ns_lcm_op_occs", _id |
1509 |
|
) |
1510 |
0 |
outdata = {"id": _id} |
1511 |
0 |
cherrypy.response.status = HTTPStatus.ACCEPTED.value |
1512 |
0 |
elif topic == "netslice_instances_content": |
1513 |
|
# creates NetSlice_Instance_record (NSIR) |
1514 |
0 |
_id, _ = self.engine.new_item( |
1515 |
|
rollback, engine_session, engine_topic, indata, kwargs |
1516 |
|
) |
1517 |
0 |
self._set_location_header(main_topic, version, topic, _id) |
1518 |
0 |
indata["lcmOperationType"] = "instantiate" |
1519 |
0 |
indata["netsliceInstanceId"] = _id |
1520 |
0 |
nsilcmop_id, _ = self.engine.new_item( |
1521 |
|
rollback, engine_session, "nsilcmops", indata, kwargs |
1522 |
|
) |
1523 |
0 |
outdata = {"id": _id, "nsilcmop_id": nsilcmop_id} |
1524 |
0 |
elif topic == "netslice_instances" and item: |
1525 |
0 |
indata["lcmOperationType"] = item |
1526 |
0 |
indata["netsliceInstanceId"] = _id |
1527 |
0 |
_id, _ = self.engine.new_item( |
1528 |
|
rollback, engine_session, "nsilcmops", indata, kwargs |
1529 |
|
) |
1530 |
0 |
self._set_location_header( |
1531 |
|
main_topic, version, "nsi_lcm_op_occs", _id |
1532 |
|
) |
1533 |
0 |
outdata = {"id": _id} |
1534 |
0 |
cherrypy.response.status = HTTPStatus.ACCEPTED.value |
1535 |
0 |
elif topic == "vnf_packages" and item == "action": |
1536 |
0 |
indata["lcmOperationType"] = item |
1537 |
0 |
indata["vnfPkgId"] = _id |
1538 |
0 |
_id, _ = self.engine.new_item( |
1539 |
|
rollback, engine_session, "vnfpkgops", indata, kwargs |
1540 |
|
) |
1541 |
0 |
self._set_location_header( |
1542 |
|
main_topic, version, "vnfpkg_op_occs", _id |
1543 |
|
) |
1544 |
0 |
outdata = {"id": _id} |
1545 |
0 |
cherrypy.response.status = HTTPStatus.ACCEPTED.value |
1546 |
0 |
elif topic == "subscriptions": |
1547 |
0 |
_id, _ = self.engine.new_item( |
1548 |
|
rollback, engine_session, engine_topic, indata, kwargs |
1549 |
|
) |
1550 |
0 |
self._set_location_header(main_topic, version, topic, _id) |
1551 |
0 |
link = {} |
1552 |
0 |
link["self"] = cherrypy.response.headers["Location"] |
1553 |
0 |
outdata = { |
1554 |
|
"id": _id, |
1555 |
|
"filter": indata["filter"], |
1556 |
|
"callbackUri": indata["CallbackUri"], |
1557 |
|
"_links": link, |
1558 |
|
} |
1559 |
0 |
cherrypy.response.status = HTTPStatus.CREATED.value |
1560 |
0 |
elif topic == "vnf_instances" and item: |
1561 |
0 |
indata["lcmOperationType"] = item |
1562 |
0 |
indata["vnfInstanceId"] = _id |
1563 |
0 |
_id, _ = self.engine.new_item(rollback, engine_session, "vnflcmops", indata, kwargs) |
1564 |
0 |
self._set_location_header(main_topic, version, "vnf_lcm_op_occs", _id) |
1565 |
0 |
outdata = {"id": _id} |
1566 |
0 |
cherrypy.response.status = HTTPStatus.ACCEPTED.value |
1567 |
|
else: |
1568 |
0 |
_id, op_id = self.engine.new_item( |
1569 |
|
rollback, |
1570 |
|
engine_session, |
1571 |
|
engine_topic, |
1572 |
|
indata, |
1573 |
|
kwargs, |
1574 |
|
cherrypy.request.headers, |
1575 |
|
) |
1576 |
0 |
self._set_location_header(main_topic, version, topic, _id) |
1577 |
0 |
outdata = {"id": _id} |
1578 |
0 |
if op_id: |
1579 |
0 |
outdata["op_id"] = op_id |
1580 |
0 |
cherrypy.response.status = HTTPStatus.ACCEPTED.value |
1581 |
|
# TODO form NsdInfo when topic in ("ns_descriptors", "vnf_packages") |
1582 |
|
|
1583 |
0 |
elif method == "DELETE": |
1584 |
0 |
if not _id: |
1585 |
0 |
outdata = self.engine.del_item_list( |
1586 |
|
engine_session, engine_topic, kwargs |
1587 |
|
) |
1588 |
0 |
cherrypy.response.status = HTTPStatus.OK.value |
1589 |
|
else: # len(args) > 1 |
1590 |
|
# for NS NSI generate an operation |
1591 |
0 |
op_id = None |
1592 |
0 |
if topic == "ns_instances_content" and not engine_session["force"]: |
1593 |
0 |
nslcmop_desc = { |
1594 |
|
"lcmOperationType": "terminate", |
1595 |
|
"nsInstanceId": _id, |
1596 |
|
"autoremove": True, |
1597 |
|
} |
1598 |
0 |
op_id, _ = self.engine.new_item( |
1599 |
|
rollback, engine_session, "nslcmops", nslcmop_desc, kwargs |
1600 |
|
) |
1601 |
0 |
if op_id: |
1602 |
0 |
outdata = {"_id": op_id} |
1603 |
0 |
elif ( |
1604 |
|
topic == "netslice_instances_content" |
1605 |
|
and not engine_session["force"] |
1606 |
|
): |
1607 |
0 |
nsilcmop_desc = { |
1608 |
|
"lcmOperationType": "terminate", |
1609 |
|
"netsliceInstanceId": _id, |
1610 |
|
"autoremove": True, |
1611 |
|
} |
1612 |
0 |
op_id, _ = self.engine.new_item( |
1613 |
|
rollback, engine_session, "nsilcmops", nsilcmop_desc, None |
1614 |
|
) |
1615 |
0 |
if op_id: |
1616 |
0 |
outdata = {"_id": op_id} |
1617 |
|
# if there is not any deletion in process, delete |
1618 |
0 |
if not op_id: |
1619 |
0 |
op_id = self.engine.del_item(engine_session, engine_topic, _id) |
1620 |
0 |
if op_id: |
1621 |
0 |
outdata = {"op_id": op_id} |
1622 |
0 |
cherrypy.response.status = ( |
1623 |
|
HTTPStatus.ACCEPTED.value |
1624 |
|
if op_id |
1625 |
|
else HTTPStatus.NO_CONTENT.value |
1626 |
|
) |
1627 |
|
|
1628 |
0 |
elif method in ("PUT", "PATCH"): |
1629 |
0 |
op_id = None |
1630 |
0 |
if not indata and not kwargs and not engine_session.get("set_project"): |
1631 |
0 |
raise NbiException( |
1632 |
|
"Nothing to update. Provide payload and/or query string", |
1633 |
|
HTTPStatus.BAD_REQUEST, |
1634 |
|
) |
1635 |
0 |
if ( |
1636 |
|
item in ("nsd_content", "package_content", "nst_content") |
1637 |
|
and method == "PUT" |
1638 |
|
): |
1639 |
0 |
completed = self.engine.upload_content( |
1640 |
|
engine_session, |
1641 |
|
engine_topic, |
1642 |
|
_id, |
1643 |
|
indata, |
1644 |
|
kwargs, |
1645 |
|
cherrypy.request.headers, |
1646 |
|
) |
1647 |
0 |
if not completed: |
1648 |
0 |
cherrypy.response.headers["Transaction-Id"] = id |
1649 |
|
else: |
1650 |
0 |
op_id = self.engine.edit_item( |
1651 |
|
engine_session, engine_topic, _id, indata, kwargs |
1652 |
|
) |
1653 |
|
|
1654 |
0 |
if op_id: |
1655 |
0 |
cherrypy.response.status = HTTPStatus.ACCEPTED.value |
1656 |
0 |
outdata = {"op_id": op_id} |
1657 |
|
else: |
1658 |
0 |
cherrypy.response.status = HTTPStatus.NO_CONTENT.value |
1659 |
0 |
outdata = None |
1660 |
|
else: |
1661 |
0 |
raise NbiException( |
1662 |
|
"Method {} not allowed".format(method), |
1663 |
|
HTTPStatus.METHOD_NOT_ALLOWED, |
1664 |
|
) |
1665 |
|
|
1666 |
|
# if Role information changes, it is needed to reload the information of roles |
1667 |
0 |
if topic == "roles" and method != "GET": |
1668 |
0 |
self.authenticator.load_operation_to_allowed_roles() |
1669 |
|
|
1670 |
0 |
if ( |
1671 |
|
topic == "projects" |
1672 |
|
and method == "DELETE" |
1673 |
|
or topic in ["users", "roles"] |
1674 |
|
and method in ["PUT", "PATCH", "DELETE"] |
1675 |
|
): |
1676 |
0 |
self.authenticator.remove_token_from_cache() |
1677 |
|
|
1678 |
0 |
return self._format_out(outdata, token_info, _format) |
1679 |
0 |
except Exception as e: |
1680 |
0 |
if isinstance( |
1681 |
|
e, |
1682 |
|
( |
1683 |
|
NbiException, |
1684 |
|
EngineException, |
1685 |
|
DbException, |
1686 |
|
FsException, |
1687 |
|
MsgException, |
1688 |
|
AuthException, |
1689 |
|
ValidationError, |
1690 |
|
AuthconnException, |
1691 |
|
), |
1692 |
|
): |
1693 |
0 |
http_code_value = cherrypy.response.status = e.http_code.value |
1694 |
0 |
http_code_name = e.http_code.name |
1695 |
0 |
cherrypy.log("Exception {}".format(e)) |
1696 |
|
else: |
1697 |
0 |
http_code_value = ( |
1698 |
|
cherrypy.response.status |
1699 |
|
) = HTTPStatus.BAD_REQUEST.value # INTERNAL_SERVER_ERROR |
1700 |
0 |
cherrypy.log("CRITICAL: Exception {}".format(e), traceback=True) |
1701 |
0 |
http_code_name = HTTPStatus.BAD_REQUEST.name |
1702 |
0 |
if hasattr(outdata, "close"): # is an open file |
1703 |
0 |
outdata.close() |
1704 |
0 |
error_text = str(e) |
1705 |
0 |
rollback.reverse() |
1706 |
0 |
for rollback_item in rollback: |
1707 |
0 |
try: |
1708 |
0 |
if rollback_item.get("operation") == "set": |
1709 |
0 |
self.engine.db.set_one( |
1710 |
|
rollback_item["topic"], |
1711 |
|
{"_id": rollback_item["_id"]}, |
1712 |
|
rollback_item["content"], |
1713 |
|
fail_on_empty=False, |
1714 |
|
) |
1715 |
0 |
elif rollback_item.get("operation") == "del_list": |
1716 |
0 |
self.engine.db.del_list( |
1717 |
|
rollback_item["topic"], |
1718 |
|
rollback_item["filter"], |
1719 |
|
fail_on_empty=False, |
1720 |
|
) |
1721 |
|
else: |
1722 |
0 |
self.engine.db.del_one( |
1723 |
|
rollback_item["topic"], |
1724 |
|
{"_id": rollback_item["_id"]}, |
1725 |
|
fail_on_empty=False, |
1726 |
|
) |
1727 |
0 |
except Exception as e2: |
1728 |
0 |
rollback_error_text = "Rollback Exception {}: {}".format( |
1729 |
|
rollback_item, e2 |
1730 |
|
) |
1731 |
0 |
cherrypy.log(rollback_error_text) |
1732 |
0 |
error_text += ". " + rollback_error_text |
1733 |
|
# if isinstance(e, MsgException): |
1734 |
|
# error_text = "{} has been '{}' but other modules cannot be informed because an error on bus".format( |
1735 |
|
# engine_topic[:-1], method, error_text) |
1736 |
0 |
problem_details = { |
1737 |
|
"code": http_code_name, |
1738 |
|
"status": http_code_value, |
1739 |
|
"detail": error_text, |
1740 |
|
} |
1741 |
0 |
return self._format_out(problem_details, token_info) |
1742 |
|
# raise cherrypy.HTTPError(e.http_code.value, str(e)) |
1743 |
|
finally: |
1744 |
0 |
if token_info: |
1745 |
0 |
self._format_login(token_info) |
1746 |
0 |
if method in ("PUT", "PATCH", "POST") and isinstance(outdata, dict): |
1747 |
0 |
for logging_id in ("id", "op_id", "nsilcmop_id", "nslcmop_id"): |
1748 |
0 |
if outdata.get(logging_id): |
1749 |
0 |
cherrypy.request.login += ";{}={}".format( |
1750 |
|
logging_id, outdata[logging_id][:36] |
1751 |
|
) |
1752 |
|
|
1753 |
|
|
1754 |
0 |
def _start_service(): |
1755 |
|
""" |
1756 |
|
Callback function called when cherrypy.engine starts |
1757 |
|
Override configuration with env variables |
1758 |
|
Set database, storage, message configuration |
1759 |
|
Init database with admin/admin user password |
1760 |
|
""" |
1761 |
|
global nbi_server |
1762 |
|
global subscription_thread |
1763 |
0 |
cherrypy.log.error("Starting osm_nbi") |
1764 |
|
# update general cherrypy configuration |
1765 |
0 |
update_dict = {} |
1766 |
|
|
1767 |
0 |
engine_config = cherrypy.tree.apps["/osm"].config |
1768 |
0 |
for k, v in environ.items(): |
1769 |
0 |
if not k.startswith("OSMNBI_"): |
1770 |
0 |
continue |
1771 |
0 |
k1, _, k2 = k[7:].lower().partition("_") |
1772 |
0 |
if not k2: |
1773 |
0 |
continue |
1774 |
0 |
try: |
1775 |
|
# update static configuration |
1776 |
0 |
if k == "OSMNBI_STATIC_DIR": |
1777 |
0 |
engine_config["/static"]["tools.staticdir.dir"] = v |
1778 |
0 |
engine_config["/static"]["tools.staticdir.on"] = True |
1779 |
0 |
elif k == "OSMNBI_SOCKET_PORT" or k == "OSMNBI_SERVER_PORT": |
1780 |
0 |
update_dict["server.socket_port"] = int(v) |
1781 |
0 |
elif k == "OSMNBI_SOCKET_HOST" or k == "OSMNBI_SERVER_HOST": |
1782 |
0 |
update_dict["server.socket_host"] = v |
1783 |
0 |
elif k1 in ("server", "test", "auth", "log"): |
1784 |
0 |
update_dict[k1 + "." + k2] = v |
1785 |
0 |
elif k1 in ("message", "database", "storage", "authentication"): |
1786 |
|
# k2 = k2.replace('_', '.') |
1787 |
0 |
if k2 in ("port", "db_port"): |
1788 |
0 |
engine_config[k1][k2] = int(v) |
1789 |
|
else: |
1790 |
0 |
engine_config[k1][k2] = v |
1791 |
|
|
1792 |
0 |
except ValueError as e: |
1793 |
0 |
cherrypy.log.error("Ignoring environ '{}': " + str(e)) |
1794 |
0 |
except Exception as e: |
1795 |
0 |
cherrypy.log.warn("skipping environ '{}' on exception '{}'".format(k, e)) |
1796 |
|
|
1797 |
0 |
if update_dict: |
1798 |
0 |
cherrypy.config.update(update_dict) |
1799 |
0 |
engine_config["global"].update(update_dict) |
1800 |
|
|
1801 |
|
# logging cherrypy |
1802 |
0 |
log_format_simple = ( |
1803 |
|
"%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s" |
1804 |
|
) |
1805 |
0 |
log_formatter_simple = logging.Formatter( |
1806 |
|
log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S" |
1807 |
|
) |
1808 |
0 |
logger_server = logging.getLogger("cherrypy.error") |
1809 |
0 |
logger_access = logging.getLogger("cherrypy.access") |
1810 |
0 |
logger_cherry = logging.getLogger("cherrypy") |
1811 |
0 |
logger_nbi = logging.getLogger("nbi") |
1812 |
|
|
1813 |
0 |
if "log.file" in engine_config["global"]: |
1814 |
0 |
file_handler = logging.handlers.RotatingFileHandler( |
1815 |
|
engine_config["global"]["log.file"], maxBytes=100e6, backupCount=9, delay=0 |
1816 |
|
) |
1817 |
0 |
file_handler.setFormatter(log_formatter_simple) |
1818 |
0 |
logger_cherry.addHandler(file_handler) |
1819 |
0 |
logger_nbi.addHandler(file_handler) |
1820 |
|
# log always to standard output |
1821 |
0 |
for format_, logger in { |
1822 |
|
"nbi.server %(filename)s:%(lineno)s": logger_server, |
1823 |
|
"nbi.access %(filename)s:%(lineno)s": logger_access, |
1824 |
|
"%(name)s %(filename)s:%(lineno)s": logger_nbi, |
1825 |
|
}.items(): |
1826 |
0 |
log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_) |
1827 |
0 |
log_formatter_cherry = logging.Formatter( |
1828 |
|
log_format_cherry, datefmt="%Y-%m-%dT%H:%M:%S" |
1829 |
|
) |
1830 |
0 |
str_handler = logging.StreamHandler() |
1831 |
0 |
str_handler.setFormatter(log_formatter_cherry) |
1832 |
0 |
logger.addHandler(str_handler) |
1833 |
|
|
1834 |
0 |
if engine_config["global"].get("log.level"): |
1835 |
0 |
logger_cherry.setLevel(engine_config["global"]["log.level"]) |
1836 |
0 |
logger_nbi.setLevel(engine_config["global"]["log.level"]) |
1837 |
|
|
1838 |
|
# logging other modules |
1839 |
0 |
for k1, logname in { |
1840 |
|
"message": "nbi.msg", |
1841 |
|
"database": "nbi.db", |
1842 |
|
"storage": "nbi.fs", |
1843 |
|
}.items(): |
1844 |
0 |
engine_config[k1]["logger_name"] = logname |
1845 |
0 |
logger_module = logging.getLogger(logname) |
1846 |
0 |
if "logfile" in engine_config[k1]: |
1847 |
0 |
file_handler = logging.handlers.RotatingFileHandler( |
1848 |
|
engine_config[k1]["logfile"], maxBytes=100e6, backupCount=9, delay=0 |
1849 |
|
) |
1850 |
0 |
file_handler.setFormatter(log_formatter_simple) |
1851 |
0 |
logger_module.addHandler(file_handler) |
1852 |
0 |
if "loglevel" in engine_config[k1]: |
1853 |
0 |
logger_module.setLevel(engine_config[k1]["loglevel"]) |
1854 |
|
# TODO add more entries, e.g.: storage |
1855 |
0 |
cherrypy.tree.apps["/osm"].root.engine.start(engine_config) |
1856 |
0 |
cherrypy.tree.apps["/osm"].root.authenticator.start(engine_config) |
1857 |
0 |
cherrypy.tree.apps["/osm"].root.engine.init_db(target_version=database_version) |
1858 |
0 |
cherrypy.tree.apps["/osm"].root.authenticator.init_db( |
1859 |
|
target_version=auth_database_version |
1860 |
|
) |
1861 |
|
|
1862 |
|
# start subscriptions thread: |
1863 |
0 |
subscription_thread = SubscriptionThread( |
1864 |
|
config=engine_config, engine=nbi_server.engine |
1865 |
|
) |
1866 |
0 |
subscription_thread.start() |
1867 |
|
# Do not capture except SubscriptionException |
1868 |
|
|
1869 |
0 |
backend = engine_config["authentication"]["backend"] |
1870 |
0 |
cherrypy.log.error( |
1871 |
|
"Starting OSM NBI Version '{} {}' with '{}' authentication backend".format( |
1872 |
|
nbi_version, nbi_version_date, backend |
1873 |
|
) |
1874 |
|
) |
1875 |
|
|
1876 |
|
|
1877 |
0 |
def _stop_service(): |
1878 |
|
""" |
1879 |
|
Callback function called when cherrypy.engine stops |
1880 |
|
TODO: Ending database connections. |
1881 |
|
""" |
1882 |
|
global subscription_thread |
1883 |
0 |
if subscription_thread: |
1884 |
0 |
subscription_thread.terminate() |
1885 |
0 |
subscription_thread = None |
1886 |
0 |
cherrypy.tree.apps["/osm"].root.engine.stop() |
1887 |
0 |
cherrypy.log.error("Stopping osm_nbi") |
1888 |
|
|
1889 |
|
|
1890 |
0 |
def nbi(config_file): |
1891 |
|
global nbi_server |
1892 |
|
# conf = { |
1893 |
|
# '/': { |
1894 |
|
# #'request.dispatch': cherrypy.dispatch.MethodDispatcher(), |
1895 |
|
# 'tools.sessions.on': True, |
1896 |
|
# 'tools.response_headers.on': True, |
1897 |
|
# # 'tools.response_headers.headers': [('Content-Type', 'text/plain')], |
1898 |
|
# } |
1899 |
|
# } |
1900 |
|
# cherrypy.Server.ssl_module = 'builtin' |
1901 |
|
# cherrypy.Server.ssl_certificate = "http/cert.pem" |
1902 |
|
# cherrypy.Server.ssl_private_key = "http/privkey.pem" |
1903 |
|
# cherrypy.Server.thread_pool = 10 |
1904 |
|
# cherrypy.config.update({'Server.socket_port': config["port"], 'Server.socket_host': config["host"]}) |
1905 |
|
|
1906 |
|
# cherrypy.config.update({'tools.auth_basic.on': True, |
1907 |
|
# 'tools.auth_basic.realm': 'localhost', |
1908 |
|
# 'tools.auth_basic.checkpassword': validate_password}) |
1909 |
0 |
nbi_server = Server() |
1910 |
0 |
cherrypy.engine.subscribe("start", _start_service) |
1911 |
0 |
cherrypy.engine.subscribe("stop", _stop_service) |
1912 |
0 |
cherrypy.quickstart(nbi_server, "/osm", config_file) |
1913 |
|
|
1914 |
|
|
1915 |
0 |
def usage(): |
1916 |
0 |
print( |
1917 |
|
"""Usage: {} [options] |
1918 |
|
-c|--config [configuration_file]: loads the configuration file (default: ./nbi.cfg) |
1919 |
|
-h|--help: shows this help |
1920 |
|
""".format( |
1921 |
|
sys.argv[0] |
1922 |
|
) |
1923 |
|
) |
1924 |
|
# --log-socket-host HOST: send logs to this host") |
1925 |
|
# --log-socket-port PORT: send logs using this port (default: 9022)") |
1926 |
|
|
1927 |
|
|
1928 |
0 |
if __name__ == "__main__": |
1929 |
0 |
try: |
1930 |
|
# load parameters and configuration |
1931 |
0 |
opts, args = getopt.getopt(sys.argv[1:], "hvc:", ["config=", "help"]) |
1932 |
|
# TODO add "log-socket-host=", "log-socket-port=", "log-file=" |
1933 |
0 |
config_file = None |
1934 |
0 |
for o, a in opts: |
1935 |
0 |
if o in ("-h", "--help"): |
1936 |
0 |
usage() |
1937 |
0 |
sys.exit() |
1938 |
0 |
elif o in ("-c", "--config"): |
1939 |
0 |
config_file = a |
1940 |
|
# elif o == "--log-socket-port": |
1941 |
|
# log_socket_port = a |
1942 |
|
# elif o == "--log-socket-host": |
1943 |
|
# log_socket_host = a |
1944 |
|
# elif o == "--log-file": |
1945 |
|
# log_file = a |
1946 |
|
else: |
1947 |
0 |
assert False, "Unhandled option" |
1948 |
0 |
if config_file: |
1949 |
0 |
if not path.isfile(config_file): |
1950 |
0 |
print( |
1951 |
|
"configuration file '{}' that not exist".format(config_file), |
1952 |
|
file=sys.stderr, |
1953 |
|
) |
1954 |
0 |
exit(1) |
1955 |
|
else: |
1956 |
0 |
for config_file in ( |
1957 |
|
__file__[: __file__.rfind(".")] + ".cfg", |
1958 |
|
"./nbi.cfg", |
1959 |
|
"/etc/osm/nbi.cfg", |
1960 |
|
): |
1961 |
0 |
if path.isfile(config_file): |
1962 |
0 |
break |
1963 |
|
else: |
1964 |
0 |
print( |
1965 |
|
"No configuration file 'nbi.cfg' found neither at local folder nor at /etc/osm/", |
1966 |
|
file=sys.stderr, |
1967 |
|
) |
1968 |
0 |
exit(1) |
1969 |
0 |
nbi(config_file) |
1970 |
0 |
except getopt.GetoptError as e: |
1971 |
0 |
print(str(e), file=sys.stderr) |
1972 |
|
# usage() |
1973 |
0 |
exit(1) |