9cdb409b2bd2b158e5b12f4857401101314b93ab
[osm/NBI.git] / osm_nbi / nbi.py
1 #!/usr/bin/python3
2 # -*- coding: utf-8 -*-
3
4 import cherrypy
5 import time
6 import json
7 import yaml
8 import html_out as html
9 import logging
10 from engine import Engine, EngineException
11 from dbbase import DbException
12 from base64 import standard_b64decode
13 from os import getenv
14 from http import HTTPStatus
15 from http.client import responses as http_responses
16 from codecs import getreader
17 from os import environ
18
19 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
20 __version__ = "0.1"
21 version_date = "Feb 2018"
22
23 """
24 North Bound Interface (O: OSM; S: SOL5
25 URL: /osm GET POST PUT DELETE PATCH
26 /nsd/v1
27 /ns_descriptors O5 O5
28 /<nsdInfoId> O5 O5 5
29 /nsd_content O5 O5
30 /pnf_descriptors 5 5
31 /<pnfdInfoId> 5 5 5
32 /pnfd_content 5 5
33 /subcriptions 5 5
34 /<subcriptionId> 5 X
35
36 /vnfpkgm/v1
37 /vnf_packages O5 O5
38 /<vnfPkgId> O5 O5 5
39 /vnfd O5 O
40 /package_content O5 O5
41 /upload_from_uri X
42 /artifacts/<artifactPatch X
43 /subcriptions X X
44 /<subcriptionId> X X
45
46 /nslcm/v1
47 /ns_instances O5 O5
48 /<nsInstanceId> O5 O5
49 TO BE COMPLETED
50 /ns_lcm_op_occs 5 5
51 /<nsLcmOpOccId> 5 5 5
52 TO BE COMPLETED 5 5
53 /subcriptions 5 5
54 /<subcriptionId> 5 X
55
56 query string.
57 <attrName>[.<attrName>...]*[.<op>]=<value>[,<value>...]&...
58 op: "eq"(or empty to one or the values) | "neq" (to any of the values) | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
59 all_fields, fields=x,y,.., exclude_default, exclude_fields=x,y,...
60 (none) … same as “exclude_default”
61 all_fields … all attributes.
62 fields=<list> … all attributes except all complex attributes with minimum cardinality of zero that are not conditionally mandatory, and that are not provided in <list>.
63 exclude_fields=<list> … all attributes except those complex attributes with a minimum cardinality of zero that are not conditionally mandatory, and that are provided in <list>.
64 exclude_default … all attributes except those complex attributes with a minimum cardinality of zero that are not conditionally mandatory, and that are part of the "default exclude set" defined in the present specification for the particular resource
65 exclude_default and include=<list> … all attributes except those complex attributes with a minimum cardinality of zero that are not conditionally mandatory and that are part of the "default exclude set" defined in the present specification for the particular resource, but that are not part of <list>
66 Header field name Reference Example Descriptions
67 Accept IETF RFC 7231 [19] application/json Content-Types that are acceptable for the response.
68 This header field shall be present if the response is expected to have a non-empty message body.
69 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the request.
70 This header field shall be present if the request has a non-empty message body.
71 Authorization IETF RFC 7235 [22] Bearer mF_9.B5f-4.1JqM The authorization token for the request. Details are specified in clause 4.5.3.
72 Range IETF RFC 7233 [21] 1000-2000 Requested range of bytes from a file
73 Header field name Reference Example Descriptions
74 Content-Type IETF RFC 7231 [19] application/json The MIME type of the body of the response.
75 This header field shall be present if the response has a non-empty message body.
76 Location IETF RFC 7231 [19] http://www.example.com/vnflcm/v1/vnf_instances/123 Used in redirection, or when a new resource has been created.
77 This header field shall be present if the response status code is 201 or 3xx.
78 In the present document this header field is also used if the response status code is 202 and a new resource was created.
79 WWW-Authenticate IETF RFC 7235 [22] Bearer realm="example" Challenge if the corresponding HTTP request has not provided authorization, or error details if the corresponding HTTP request has provided an invalid authorization token.
80 Accept-Ranges IETF RFC 7233 [21] bytes Used by the Server to signal whether or not it supports ranges for certain resources.
81 Content-Range IETF RFC 7233 [21] bytes 21010-47021/ 47022 Signals the byte range that is contained in the response, and the total length of the file.
82 Retry-After IETF RFC 7231 [19] Fri, 31 Dec 1999 23:59:59 GMT
83
84 or
85
86 120 Used to indicate how long the user agent ought to wait before making a follow-up request.
87 It can be used with 503 responses.
88 The value of this field can be an HTTP-date or a number of seconds to delay after the response is received.
89
90 #TODO http header for partial uploads: Content-Range: "bytes 0-1199/15000". Id is returned first time and send in following chunks
91 """
92
93
94 class NbiException(Exception):
95
96 def __init__(self, message, http_code=HTTPStatus.METHOD_NOT_ALLOWED):
97 Exception.__init__(self, message)
98 self.http_code = http_code
99
100
101 class Server(object):
102 instance = 0
103 # to decode bytes to str
104 reader = getreader("utf-8")
105
106 def __init__(self):
107 self.instance += 1
108 self.engine = Engine()
109
110 def _authorization(self):
111 token = None
112 user_passwd64 = None
113 try:
114 # 1. Get token Authorization bearer
115 auth = cherrypy.request.headers.get("Authorization")
116 if auth:
117 auth_list = auth.split(" ")
118 if auth_list[0].lower() == "bearer":
119 token = auth_list[-1]
120 elif auth_list[0].lower() == "basic":
121 user_passwd64 = auth_list[-1]
122 if not token:
123 if cherrypy.session.get("Authorization"):
124 # 2. Try using session before request a new token. If not, basic authentication will generate
125 token = cherrypy.session.get("Authorization")
126 if token == "logout":
127 token = None # force Unauthorized response to insert user pasword again
128 elif user_passwd64 and cherrypy.request.config.get("auth.allow_basic_authentication"):
129 # 3. Get new token from user password
130 user = None
131 passwd = None
132 try:
133 user_passwd = standard_b64decode(user_passwd64).decode()
134 user, _, passwd = user_passwd.partition(":")
135 except:
136 pass
137 outdata = self.engine.new_token(None, {"username": user, "password": passwd})
138 token = outdata["id"]
139 cherrypy.session['Authorization'] = token
140 # 4. Get token from cookie
141 # if not token:
142 # auth_cookie = cherrypy.request.cookie.get("Authorization")
143 # if auth_cookie:
144 # token = auth_cookie.value
145 return self.engine.authorize(token)
146 except EngineException as e:
147 if cherrypy.session.get('Authorization'):
148 del cherrypy.session['Authorization']
149 cherrypy.response.headers["WWW-Authenticate"] = 'Bearer realm="{}"'.format(e)
150 raise
151
152 def _format_in(self, kwargs):
153 try:
154 indata = None
155 if cherrypy.request.body.length:
156 error_text = "Invalid input format "
157
158 if "Content-Type" in cherrypy.request.headers:
159 if "application/json" in cherrypy.request.headers["Content-Type"]:
160 error_text = "Invalid json format "
161 indata = json.load(self.reader(cherrypy.request.body))
162 elif "application/yaml" in cherrypy.request.headers["Content-Type"]:
163 error_text = "Invalid yaml format "
164 indata = yaml.load(cherrypy.request.body)
165 elif "application/binary" in cherrypy.request.headers["Content-Type"] or \
166 "application/gzip" in cherrypy.request.headers["Content-Type"] or \
167 "application/zip" in cherrypy.request.headers["Content-Type"]:
168 indata = cherrypy.request.body.read()
169 elif "multipart/form-data" in cherrypy.request.headers["Content-Type"]:
170 if "descriptor_file" in kwargs:
171 filecontent = kwargs.pop("descriptor_file")
172 if not filecontent.file:
173 raise NbiException("empty file or content", HTTPStatus.BAD_REQUEST)
174 indata = filecontent.file.read()
175 if filecontent.content_type.value:
176 cherrypy.request.headers["Content-Type"] = filecontent.content_type.value
177 else:
178 # raise cherrypy.HTTPError(HTTPStatus.Not_Acceptable,
179 # "Only 'Content-Type' of type 'application/json' or
180 # 'application/yaml' for input format are available")
181 error_text = "Invalid yaml format "
182 indata = yaml.load(cherrypy.request.body)
183 else:
184 error_text = "Invalid yaml format "
185 indata = yaml.load(cherrypy.request.body)
186 if not indata:
187 indata = {}
188
189 if "METHOD" in kwargs:
190 method = kwargs.pop("METHOD")
191 else:
192 method = cherrypy.request.method
193 format_yaml = False
194 if cherrypy.request.headers.get("Query-String-Format") == "yaml":
195 format_yaml = True
196
197 for k, v in kwargs.items():
198 if isinstance(v, str):
199 if v == "":
200 kwargs[k] = None
201 elif format_yaml:
202 try:
203 kwargs[k] = yaml.load(v)
204 except:
205 pass
206 elif k.endswith(".gt") or k.endswith(".lt") or k.endswith(".gte") or k.endswith(".lte"):
207 try:
208 kwargs[k] = int(v)
209 except:
210 try:
211 kwargs[k] = float(v)
212 except:
213 pass
214 elif v.find(",") > 0:
215 kwargs[k] = v.split(",")
216 elif isinstance(v, (list, tuple)):
217 for index in range(0, len(v)):
218 if v[index] == "":
219 v[index] = None
220 elif format_yaml:
221 try:
222 v[index] = yaml.load(v[index])
223 except:
224 pass
225
226 return indata, method
227 except (ValueError, yaml.YAMLError) as exc:
228 raise NbiException(error_text + str(exc), HTTPStatus.BAD_REQUEST)
229 except KeyError as exc:
230 raise NbiException("Query string error: " + str(exc), HTTPStatus.BAD_REQUEST)
231
232 @staticmethod
233 def _format_out(data, session=None):
234 """
235 return string of dictionary data according to requested json, yaml, xml. By default json
236 :param data: response to be sent. Can be a dict or text
237 :param session:
238 :return: None
239 """
240 if "Accept" in cherrypy.request.headers:
241 accept = cherrypy.request.headers["Accept"]
242 if "application/json" in accept:
243 cherrypy.response.headers["Content-Type"] = 'application/json; charset=utf-8'
244 a = json.dumps(data, indent=4) + "\n"
245 return a.encode("utf8")
246 elif "text/html" in accept:
247 return html.format(data, cherrypy.request, cherrypy.response, session)
248
249 elif "application/yaml" in accept or "*/*" in accept:
250 pass
251 else:
252 raise cherrypy.HTTPError(HTTPStatus.NOT_ACCEPTABLE.value,
253 "Only 'Accept' of type 'application/json' or 'application/yaml' "
254 "for output format are available")
255 cherrypy.response.headers["Content-Type"] = 'application/yaml'
256 return yaml.safe_dump(data, explicit_start=True, indent=4, default_flow_style=False, tags=False,
257 encoding='utf-8', allow_unicode=True) # , canonical=True, default_style='"'
258
259 @cherrypy.expose
260 def index(self, *args, **kwargs):
261 session = None
262 try:
263 if cherrypy.request.method == "GET":
264 session = self._authorization()
265 outdata = "Index page"
266 else:
267 raise cherrypy.HTTPError(HTTPStatus.METHOD_NOT_ALLOWED.value,
268 "Method {} not allowed for tokens".format(cherrypy.request.method))
269
270 return self._format_out(outdata, session)
271
272 except EngineException as e:
273 cherrypy.log("index Exception {}".format(e))
274 cherrypy.response.status = e.http_code.value
275 return self._format_out("Welcome to OSM!", session)
276
277 @cherrypy.expose
278 def token(self, *args, **kwargs):
279 if not args:
280 raise NbiException("URL must contain at least 'item/version'", HTTPStatus.METHOD_NOT_ALLOWED)
281 version = args[0]
282 if version != 'v1':
283 raise NbiException("URL version '{}' not supported".format(version), HTTPStatus.METHOD_NOT_ALLOWED)
284 session = None
285 # self.engine.load_dbase(cherrypy.request.app.config)
286 try:
287 indata, method = self._format_in(kwargs)
288 if method == "GET":
289 session = self._authorization()
290 if len(args) >= 2:
291 outdata = self.engine.get_token(session, args[1])
292 else:
293 outdata = self.engine.get_token_list(session)
294 elif method == "POST":
295 try:
296 session = self._authorization()
297 except:
298 session = None
299 if kwargs:
300 indata.update(kwargs)
301 outdata = self.engine.new_token(session, indata, cherrypy.request.remote)
302 session = outdata
303 cherrypy.session['Authorization'] = outdata["_id"]
304 # cherrypy.response.cookie["Authorization"] = outdata["id"]
305 # cherrypy.response.cookie["Authorization"]['expires'] = 3600
306 elif method == "DELETE":
307 if len(args) >= 2 and "logout" not in args:
308 token_id = args[1]
309 elif "id" in kwargs:
310 token_id = kwargs["id"]
311 else:
312 session = self._authorization()
313 token_id = session["_id"]
314 outdata = self.engine.del_token(token_id)
315 session = None
316 cherrypy.session['Authorization'] = "logout"
317 # cherrypy.response.cookie["Authorization"] = token_id
318 # cherrypy.response.cookie["Authorization"]['expires'] = 0
319 else:
320 raise NbiException("Method {} not allowed for token".format(method), HTTPStatus.METHOD_NOT_ALLOWED)
321 return self._format_out(outdata, session)
322 except (NbiException, EngineException, DbException) as e:
323 cherrypy.log("tokens Exception {}".format(e))
324 cherrypy.response.status = e.http_code.value
325 problem_details = {
326 "code": e.http_code.name,
327 "status": e.http_code.value,
328 "detail": str(e),
329 }
330 return self._format_out(problem_details, session)
331
332 @cherrypy.expose
333 def test(self, *args, **kwargs):
334 thread_info = None
335 if args and args[0] == "init":
336 try:
337 # self.engine.load_dbase(cherrypy.request.app.config)
338 self.engine.create_admin()
339 return "Done. User 'admin', password 'admin' created"
340 except Exception:
341 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
342 return self._format_out("Database already initialized")
343 elif args and args[0] == "prune":
344 return self.engine.prune()
345 elif args and args[0] == "login":
346 if not cherrypy.request.headers.get("Authorization"):
347 cherrypy.response.headers["WWW-Authenticate"] = 'Basic realm="Access to OSM site", charset="UTF-8"'
348 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
349 elif args and args[0] == "login2":
350 if not cherrypy.request.headers.get("Authorization"):
351 cherrypy.response.headers["WWW-Authenticate"] = 'Bearer realm="Access to OSM site"'
352 cherrypy.response.status = HTTPStatus.UNAUTHORIZED.value
353 elif args and args[0] == "sleep":
354 sleep_time = 5
355 try:
356 sleep_time = int(args[1])
357 except Exception:
358 cherrypy.response.status = HTTPStatus.FORBIDDEN.value
359 return self._format_out("Database already initialized")
360 thread_info = cherrypy.thread_data
361 print(thread_info)
362 time.sleep(sleep_time)
363 # thread_info
364 elif len(args) >= 2 and args[0] == "message":
365 topic = args[1]
366 try:
367 for k, v in kwargs.items():
368 self.engine.msg.write(topic, k, yaml.load(v))
369 return "ok"
370 except Exception as e:
371 return "Error: " + format(e)
372
373 return_text = (
374 "<html><pre>\nheaders:\n args: {}\n".format(args) +
375 " kwargs: {}\n".format(kwargs) +
376 " headers: {}\n".format(cherrypy.request.headers) +
377 " path_info: {}\n".format(cherrypy.request.path_info) +
378 " query_string: {}\n".format(cherrypy.request.query_string) +
379 " session: {}\n".format(cherrypy.session) +
380 " cookie: {}\n".format(cherrypy.request.cookie) +
381 " method: {}\n".format(cherrypy.request.method) +
382 " session: {}\n".format(cherrypy.session.get('fieldname')) +
383 " body:\n")
384 return_text += " length: {}\n".format(cherrypy.request.body.length)
385 if cherrypy.request.body.length:
386 return_text += " content: {}\n".format(
387 str(cherrypy.request.body.read(int(cherrypy.request.headers.get('Content-Length', 0)))))
388 if thread_info:
389 return_text += "thread: {}\n".format(thread_info)
390 return_text += "</pre></html>"
391 return return_text
392
393 @cherrypy.expose
394 def default(self, *args, **kwargs):
395 session = None
396 try:
397 if not args or len(args) < 2:
398 raise NbiException("URL must contain at least 'item/version'", HTTPStatus.METHOD_NOT_ALLOWED)
399 item = args[0]
400 version = args[1]
401 if item not in ("token", "user", "project", "vnfpkgm", "nsd", "nslcm"):
402 raise NbiException("URL item '{}' not supported".format(item), HTTPStatus.METHOD_NOT_ALLOWED)
403 if version != 'v1':
404 raise NbiException("URL version '{}' not supported".format(version), HTTPStatus.METHOD_NOT_ALLOWED)
405
406 # self.engine.load_dbase(cherrypy.request.app.config)
407 session = self._authorization()
408 indata, method = self._format_in(kwargs)
409 _id = None
410
411 if item == "nsd":
412 item = "nsds"
413 if len(args) < 3 or args[2] != "ns_descriptors":
414 raise NbiException("only ns_descriptors is allowed", HTTPStatus.METHOD_NOT_ALLOWED)
415 if len(args) > 3:
416 _id = args[3]
417 if len(args) > 4 and args[4] != "nsd_content":
418 raise NbiException("only nsd_content is allowed", HTTPStatus.METHOD_NOT_ALLOWED)
419 elif item == "vnfpkgm":
420 item = "vnfds"
421 if len(args) < 3 or args[2] != "vnf_packages":
422 raise NbiException("only vnf_packages is allowed", HTTPStatus.METHOD_NOT_ALLOWED)
423 if len(args) > 3:
424 _id = args[3]
425 if len(args) > 4 and args[4] not in ("vnfd", "package_content"):
426 raise NbiException("only vnfd or package_content are allowed", HTTPStatus.METHOD_NOT_ALLOWED)
427 elif item == "nslcm":
428 item = "nsrs"
429 if len(args) < 3 or args[2] != "ns_instances":
430 raise NbiException("only ns_instances is allowed", HTTPStatus.METHOD_NOT_ALLOWED)
431 if len(args) > 3:
432 _id = args[3]
433 if len(args) > 4:
434 raise NbiException("This feature is not implemented", HTTPStatus.METHOD_NOT_ALLOWED)
435 else:
436 if len(args) >= 3:
437 _id = args[2]
438 item += "s"
439
440 if method == "GET":
441 if not _id:
442 outdata = self.engine.get_item_list(session, item, kwargs)
443 else: # len(args) > 1
444 outdata = self.engine.get_item(session, item, _id)
445 elif method == "POST":
446 id, completed = self.engine.new_item(session, item, indata, kwargs, cherrypy.request.headers)
447 if not completed:
448 cherrypy.response.headers["Transaction-Id"] = id
449 cherrypy.response.status = HTTPStatus.CREATED.value
450 else:
451 cherrypy.response.headers["Location"] = cherrypy.request.base + "/osm/" + "/".join(args[0:3]) + "/" + id
452 outdata = {"id": id}
453 elif method == "DELETE":
454 if not _id:
455 outdata = self.engine.del_item_list(session, item, kwargs)
456 else: # len(args) > 1
457 outdata = self.engine.del_item(session, item, _id)
458 elif method == "PUT":
459 if not _id:
460 raise NbiException("Missing '/<id>' at the URL to identify item to be updated",
461 HTTPStatus.METHOD_NOT_ALLOWED)
462 elif not indata and not kwargs:
463 raise NbiException("Nothing to update. Provide payload and/or query string",
464 HTTPStatus.BAD_REQUEST)
465 outdata = {"id": self.engine.edit_item(session, item, args[1], indata, kwargs)}
466 else:
467 raise NbiException("Method {} not allowed".format(method), HTTPStatus.METHOD_NOT_ALLOWED)
468 return self._format_out(outdata, session)
469 except (NbiException, EngineException, DbException) as e:
470 cherrypy.log("Exception {}".format(e))
471 cherrypy.response.status = e.http_code.value
472 problem_details = {
473 "code": e.http_code.name,
474 "status": e.http_code.value,
475 "detail": str(e),
476 }
477 return self._format_out(problem_details, session)
478 # raise cherrypy.HTTPError(e.http_code.value, str(e))
479
480
481 # def validate_password(realm, username, password):
482 # cherrypy.log("realm "+ str(realm))
483 # if username == "admin" and password == "admin":
484 # return True
485 # return False
486
487
488 def _start_service():
489 """
490 Callback function called when cherrypy.engine starts
491 Override configuration with env variables
492 Set database, storage, message configuration
493 Init database with admin/admin user password
494 """
495 cherrypy.log.error("Starting osm_nbi")
496 # update general cherrypy configuration
497 update_dict = {}
498
499 engine_config = cherrypy.tree.apps['/osm'].config
500 for k, v in environ.items():
501 if not k.startswith("OSMNBI_"):
502 continue
503 k1, _, k2 = k[7:].lower().partition("_")
504 if not k2:
505 continue
506 try:
507 # update static configuration
508 if k == 'OSMNBI_STATIC_DIR':
509 engine_config["/static"]['tools.staticdir.dir'] = v
510 engine_config["/static"]['tools.staticdir.on'] = True
511 elif k == 'OSMNBI_SOCKET_PORT' or k == 'OSMNBI_SERVER_PORT':
512 update_dict['server.socket_port'] = int(v)
513 elif k == 'OSMNBI_SOCKET_HOST' or k == 'OSMNBI_SERVER_HOST':
514 update_dict['server.socket_host'] = v
515 elif k1 == "server":
516 update_dict['server' + k2] = v
517 # TODO add more entries
518 elif k1 in ("message", "database", "storage"):
519 if k2 == "port":
520 engine_config[k1][k2] = int(v)
521 else:
522 engine_config[k1][k2] = v
523 except ValueError as e:
524 cherrypy.log.error("Ignoring environ '{}': " + str(e))
525 except Exception as e:
526 cherrypy.log.warn("skipping environ '{}' on exception '{}'".format(k, e))
527
528 if update_dict:
529 cherrypy.config.update(update_dict)
530
531 # logging cherrypy
532 log_format_simple = "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
533 log_formatter_simple = logging.Formatter(log_format_simple, datefmt='%Y-%m-%dT%H:%M:%S')
534 logger_server = logging.getLogger("cherrypy.error")
535 logger_access = logging.getLogger("cherrypy.access")
536 logger_cherry = logging.getLogger("cherrypy")
537 logger_nbi = logging.getLogger("nbi")
538
539 if "logfile" in engine_config["global"]:
540 file_handler = logging.handlers.RotatingFileHandler(engine_config["global"]["logfile"],
541 maxBytes=100e6, backupCount=9, delay=0)
542 file_handler.setFormatter(log_formatter_simple)
543 logger_cherry.addHandler(file_handler)
544 logger_nbi.addHandler(file_handler)
545 else:
546 for format_, logger in {"nbi.server": logger_server,
547 "nbi.access": logger_access,
548 "%(name)s %(filename)s:%(lineno)s": logger_nbi
549 }.items():
550 log_format_cherry = "%(asctime)s %(levelname)s {} %(message)s".format(format_)
551 log_formatter_cherry = logging.Formatter(log_format_cherry, datefmt='%Y-%m-%dT%H:%M:%S')
552 str_handler = logging.StreamHandler()
553 str_handler.setFormatter(log_formatter_cherry)
554 logger.addHandler(str_handler)
555
556 if engine_config["global"].get("loglevel"):
557 logger_cherry.setLevel(engine_config["global"]["loglevel"])
558 logger_nbi.setLevel(engine_config["global"]["loglevel"])
559
560 # logging other modules
561 for k1, logname in {"message": "nbi.msg", "database": "nbi.db", "storage": "nbi.fs"}.items():
562 engine_config[k1]["logger_name"] = logname
563 logger_module = logging.getLogger(logname)
564 if "logfile" in engine_config[k1]:
565 file_handler = logging.handlers.RotatingFileHandler(engine_config[k1]["logfile"],
566 maxBytes=100e6, backupCount=9, delay=0)
567 file_handler.setFormatter(log_formatter_simple)
568 logger_module.addHandler(file_handler)
569 if "loglevel" in engine_config[k1]:
570 logger_module.setLevel(engine_config[k1]["loglevel"])
571 # TODO add more entries, e.g.: storage
572 cherrypy.tree.apps['/osm'].root.engine.start(engine_config)
573 try:
574 cherrypy.tree.apps['/osm'].root.engine.create_admin()
575 except EngineException:
576 pass
577 # getenv('OSMOPENMANO_TENANT', None)
578
579
580 def _stop_service():
581 """
582 Callback function called when cherrypy.engine stops
583 TODO: Ending database connections.
584 """
585 cherrypy.tree.apps['/osm'].root.engine.stop()
586 cherrypy.log.error("Stopping osm_nbi")
587
588 def nbi():
589 # conf = {
590 # '/': {
591 # #'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
592 # 'tools.sessions.on': True,
593 # 'tools.response_headers.on': True,
594 # # 'tools.response_headers.headers': [('Content-Type', 'text/plain')],
595 # }
596 # }
597 # cherrypy.Server.ssl_module = 'builtin'
598 # cherrypy.Server.ssl_certificate = "http/cert.pem"
599 # cherrypy.Server.ssl_private_key = "http/privkey.pem"
600 # cherrypy.Server.thread_pool = 10
601 # cherrypy.config.update({'Server.socket_port': config["port"], 'Server.socket_host': config["host"]})
602
603 # cherrypy.config.update({'tools.auth_basic.on': True,
604 # 'tools.auth_basic.realm': 'localhost',
605 # 'tools.auth_basic.checkpassword': validate_password})
606 cherrypy.engine.subscribe('start', _start_service)
607 cherrypy.engine.subscribe('stop', _stop_service)
608 cherrypy.quickstart(Server(), '/osm', "nbi.cfg")
609
610
611 if __name__ == '__main__':
612 nbi()