2 # -*- coding: utf-8 -*-
5 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
6 # This file is part of openmano
9 # Licensed under the Apache License, Version 2.0 (the "License"); you may
10 # not use this file except in compliance with the License. You may obtain
11 # a copy of the License at
13 # http://www.apache.org/licenses/LICENSE-2.0
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18 # License for the specific language governing permissions and limitations
21 # For those usages not covered by the Apache License, Version 2.0 please
22 # contact with: nfvlabs@tid.es
27 Main program that implements a reference NFVO (Network Functions Virtualisation Orchestrator).
28 It interfaces with an NFV VIM through its API and offers a northbound interface, based on REST (openmano API),
29 where NFV services are offered including the creation and deletion of VNF templates, VNF instances,
30 network service templates and network service instances.
32 It loads the configuration file and launches the http_server thread that will listen requests using openmano API.
40 from jsonschema import validate as js_v, exceptions as js_e
42 import logging.handlers as log_handlers
44 from osm_ro import httpserver, nfvo, nfvo_db
45 from osm_ro.openmano_schemas import config_schema
46 from osm_ro.db_base import db_base_Exception
49 __author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes"
50 __date__ = "$26-aug-2014 11:09:29$"
51 __version__ = "0.5.46-r556"
52 version_date = "Dec 2017"
53 database_version = 27 # expected database schema version
60 class LoadConfigurationException(Exception):
63 def load_configuration(configuration_file):
64 default_tokens = {'http_port':9090,
65 'http_host':'localhost',
66 'http_console_proxy': True,
67 'http_console_host': None,
69 'log_socket_port': 9022,
70 'auto_push_VNF_to_VIMs': True,
71 'db_host': 'localhost',
72 'db_ovim_host': 'localhost'
75 #Check config file exists
76 with open(configuration_file, 'r') as f:
78 #Parse configuration file
79 config = yaml.load(config_str)
80 #Validate configuration file with the config_schema
81 js_v(config, config_schema)
83 #Add default values tokens
84 for k,v in default_tokens.items():
89 except yaml.YAMLError as e:
91 if hasattr(e, 'problem_mark'):
93 error_pos = " at line:{} column:{}".format(mark.line+1, mark.column+1)
94 raise LoadConfigurationException("Bad YAML format at configuration file '{file}'{pos}: {message}".format(
95 file=configuration_file, pos=error_pos, message=e))
96 except js_e.ValidationError as e:
99 error_pos=" at '" + ":".join(map(str, e.path))+"'"
100 raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(
101 file=configuration_file, pos=error_pos, message=e))
102 except Exception as e:
103 raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(
104 file=configuration_file, message=e))
107 def console_port_iterator():
108 '''this iterator deals with the http_console_ports
109 returning the ports one by one
112 while index < len(global_config["http_console_ports"]):
113 port = global_config["http_console_ports"][index]
114 #print("ports -> ", port)
115 if type(port) is int:
117 else: #this is dictionary with from to keys
119 #print("ports -> ", port, port2)
120 while port2 <= port["to"]:
121 #print("ports -> ", port, port2)
128 print("Usage: ", sys.argv[0], "[options]")
129 print( " -v|--version: prints current version")
130 print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)")
131 print( " -h|--help: shows this help")
132 print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)")
133 print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)")
134 #print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file")
135 print( " --log-socket-host HOST: send logs to this host")
136 print( " --log-socket-port PORT: send logs using this port (default: 9022)")
137 print( " --log-file FILE: send logs to this file")
141 def set_logging_file(log_file):
143 file_handler = logging.handlers.RotatingFileHandler(log_file, maxBytes=100e6, backupCount=9, delay=0)
144 file_handler.setFormatter(log_formatter_simple)
145 logger.addHandler(file_handler)
146 # logger.debug("moving logs to '%s'", global_config["log_file"])
147 # remove initial stream handler
148 logging.root.removeHandler(logging.root.handlers[0])
149 print ("logging on '{}'".format(log_file))
151 raise LoadConfigurationException(
152 "Cannot open logging file '{}': {}. Check folder exist and permissions".format(log_file, e))
155 if __name__=="__main__":
156 # Configure logging step 1
157 hostname = socket.gethostname()
158 # streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
159 # "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
160 log_formatter_complete = logging.Formatter('%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s '
161 'severity:%(levelname)s logger:%(name)s log:%(message)s'.format(
163 datefmt='%Y-%m-%dT%H:%M:%S')
164 log_format_simple = "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
165 log_formatter_simple = logging.Formatter(log_format_simple, datefmt='%Y-%m-%dT%H:%M:%S')
166 logging.basicConfig(format=log_format_simple, level= logging.DEBUG)
167 logger = logging.getLogger('openmano')
168 logger.setLevel(logging.DEBUG)
169 socket_handler = None
170 # Read parameters and configuration file
173 #load parameters and configuration
174 opts, args = getopt.getopt(sys.argv[1:], "hvc:V:p:P:", ["config=", "help", "version", "port=", "vnf-repository=", "adminport=", "log-socket-host=", "log-socket-port=", "log-file="])
177 config_file = 'osm_ro/openmanod.cfg'
178 vnf_repository = None
180 log_socket_host = None
181 log_socket_port = None
184 if o in ("-v", "--version"):
185 print ("openmanod version " + __version__ + ' ' + version_date)
186 print ("(c) Copyright Telefonica")
188 elif o in ("-h", "--help"):
191 elif o in ("-V", "--vnf-repository"):
193 elif o in ("-c", "--config"):
195 elif o in ("-p", "--port"):
197 elif o in ("-P", "--adminport"):
199 elif o == "--log-socket-port":
201 elif o == "--log-socket-host":
203 elif o == "--log-file":
206 assert False, "Unhandled option"
208 set_logging_file(log_file)
209 global_config = load_configuration(config_file)
210 global_config["version"] = __version__
211 global_config["version_date"] = version_date
213 # Override parameters obtained by command line
215 global_config['http_port'] = port
217 global_config['http_admin_port'] = port_admin
219 global_config['log_socket_host'] = log_socket_host
221 global_config['log_socket_port'] = log_socket_port
222 # if vnf_repository is not None:
223 # global_config['vnf_repository'] = vnf_repository
225 # if not 'vnf_repository' in global_config:
226 # logger.error( os.getcwd() )
227 # global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
228 # #print global_config
229 # if not os.path.exists(global_config['vnf_repository']):
230 # logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
232 # os.makedirs(global_config['vnf_repository'])
233 # except Exception as e:
234 # logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
237 global_config["console_port_iterator"] = console_port_iterator
238 global_config["console_thread"]={}
239 global_config["console_ports"]={}
240 if not global_config["http_console_host"]:
241 global_config["http_console_host"] = global_config["http_host"]
242 if global_config["http_host"]=="0.0.0.0":
243 global_config["http_console_host"] = socket.gethostname()
245 # Configure logging STEP 2
246 if "log_host" in global_config:
247 socket_handler= log_handlers.SocketHandler(global_config["log_socket_host"], global_config["log_socket_port"])
248 socket_handler.setFormatter(log_formatter_complete)
249 if global_config.get("log_socket_level") and global_config["log_socket_level"] != global_config["log_level"]:
250 socket_handler.setLevel(global_config["log_socket_level"])
251 logger.addHandler(socket_handler)
253 # logger.addHandler(log_handlers.SysLogHandler())
255 global_config['log_file'] = log_file
256 elif global_config.get('log_file'):
257 set_logging_file(global_config['log_file'])
259 # logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
260 logger.setLevel(getattr(logging, global_config['log_level']))
261 logger.critical("Starting openmano server version: '%s %s' command: '%s'",
262 __version__, version_date, " ".join(sys.argv))
264 for log_module in ("nfvo", "http", "vim", "db", "console", "ovim"):
265 log_level_module = "log_level_" + log_module
266 log_file_module = "log_file_" + log_module
267 logger_module = logging.getLogger('openmano.' + log_module)
268 if log_level_module in global_config:
269 logger_module.setLevel(global_config[log_level_module])
270 if log_file_module in global_config:
272 file_handler = logging.handlers.RotatingFileHandler(global_config[log_file_module],
273 maxBytes=100e6, backupCount=9, delay=0)
274 file_handler.setFormatter(log_formatter_simple)
275 logger_module.addHandler(file_handler)
277 raise LoadConfigurationException(
278 "Cannot open logging file '{}': {}. Check folder exist and permissions".format(
279 global_config[log_file_module], str(e)) )
280 global_config["logger_"+log_module] = logger_module
281 #httpserver.logger = global_config["logger_http"]
282 #nfvo.logger = global_config["logger_nfvo"]
284 # Initialize DB connection
285 mydb = nfvo_db.nfvo_db();
286 mydb.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name'])
287 db_path = osm_ro.__path__[0] + "/database_utils"
288 if not os.path.exists(db_path + "/migrate_mano_db.sh"):
289 db_path = osm_ro.__path__[0] + "/../database_utils"
291 r = mydb.get_db_version()
292 if r[0] != database_version:
293 logger.critical("DATABASE wrong version '{current}'. Try to upgrade/downgrade to version '{target}'"
294 " with '{db_path}/migrate_mano_db.sh {target}'".format(
295 current=r[0], target=database_version, db_path=db_path))
297 except db_base_Exception as e:
298 logger.critical("DATABASE is not valid. If you think it is corrupted, you can init it with"
299 " '{db_path}/init_mano_db.sh' script".format(db_path=db_path))
302 nfvo.global_config=global_config
303 nfvo.start_service(mydb)
305 httpthread = httpserver.httpserver(mydb, False, global_config['http_host'], global_config['http_port'])
308 if 'http_admin_port' in global_config:
309 httpthreadadmin = httpserver.httpserver(mydb, True, global_config['http_host'], global_config['http_admin_port'])
310 httpthreadadmin.start()
312 logger.info('Waiting for http clients')
313 print('Waiting for http clients')
314 print('openmanod ready')
315 print('====================')
319 #TODO: Interactive console must be implemented here instead of join or sleep
322 #if 'http_admin_port' in global_config:
323 # httpthreadadmin.join()
327 except KeyboardInterrupt as e:
331 except getopt.GetoptError as e:
332 logger.critical(str(e)) # will print something like "option -a not recognized"
335 except LoadConfigurationException as e:
336 logger.critical(str(e))
338 except db_base_Exception as e:
339 logger.critical(str(e))
341 except nfvo.NfvoException as e:
342 logger.critical(str(e), exc_info=True)