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.15-r524"
52 version_date = "Jun 2017"
53 database_version = 21 #expected database schema version
58 class LoadConfigurationException(Exception):
61 def load_configuration(configuration_file):
62 default_tokens = {'http_port':9090,
63 'http_host':'localhost',
64 'http_console_proxy': True,
65 'http_console_host': None,
67 'log_socket_port': 9022,
68 'auto_push_VNF_to_VIMs': True,
69 'db_host': 'localhost',
70 'db_ovim_host': 'localhost'
73 #Check config file exists
74 with open(configuration_file, 'r') as f:
76 #Parse configuration file
77 config = yaml.load(config_str)
78 #Validate configuration file with the config_schema
79 js_v(config, config_schema)
81 #Add default values tokens
82 for k,v in default_tokens.items():
87 except yaml.YAMLError as e:
89 if hasattr(e, 'problem_mark'):
91 error_pos = " at line:{} column:{}".format(mark.line+1, mark.column+1)
92 raise LoadConfigurationException("Bad YAML format at configuration file '{file}'{pos}: {message}".format(
93 file=configuration_file, pos=error_pos, message=e))
94 except js_e.ValidationError as e:
97 error_pos=" at '" + ":".join(map(str, e.path))+"'"
98 raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(
99 file=configuration_file, pos=error_pos, message=e))
100 except Exception as e:
101 raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(
102 file=configuration_file, message=e))
105 def console_port_iterator():
106 '''this iterator deals with the http_console_ports
107 returning the ports one by one
110 while index < len(global_config["http_console_ports"]):
111 port = global_config["http_console_ports"][index]
112 #print("ports -> ", port)
113 if type(port) is int:
115 else: #this is dictionary with from to keys
117 #print("ports -> ", port, port2)
118 while port2 <= port["to"]:
119 #print("ports -> ", port, port2)
126 print("Usage: ", sys.argv[0], "[options]")
127 print( " -v|--version: prints current version")
128 print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)")
129 print( " -h|--help: shows this help")
130 print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)")
131 print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)")
132 #print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file")
133 print( " --log-socket-host HOST: send logs to this host")
134 print( " --log-socket-port PORT: send logs using this port (default: 9022)")
135 print( " --log-file FILE: send logs to this file")
139 def set_logging_file(log_file):
141 file_handler = logging.handlers.RotatingFileHandler(log_file, maxBytes=100e6, backupCount=9, delay=0)
142 file_handler.setFormatter(log_formatter_simple)
143 logger.addHandler(file_handler)
144 # logger.debug("moving logs to '%s'", global_config["log_file"])
145 # remove initial stream handler
146 logging.root.removeHandler(logging.root.handlers[0])
147 print ("logging on '{}'".format(log_file))
149 raise LoadConfigurationException(
150 "Cannot open logging file '{}': {}. Check folder exist and permissions".format(log_file, e))
153 if __name__=="__main__":
154 # Configure logging step 1
155 hostname = socket.gethostname()
156 # streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
157 # "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
158 log_formatter_complete = logging.Formatter('%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s '
159 'severity:%(levelname)s logger:%(name)s log:%(message)s'.format(
161 datefmt='%Y-%m-%dT%H:%M:%S')
162 log_format_simple = "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
163 log_formatter_simple = logging.Formatter(log_format_simple, datefmt='%Y-%m-%dT%H:%M:%S')
164 logging.basicConfig(format=log_format_simple, level= logging.DEBUG)
165 logger = logging.getLogger('openmano')
166 logger.setLevel(logging.DEBUG)
167 socket_handler = None
168 # Read parameters and configuration file
171 #load parameters and configuration
172 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="])
175 config_file = 'osm_ro/openmanod.cfg'
176 vnf_repository = None
178 log_socket_host = None
179 log_socket_port = None
182 if o in ("-v", "--version"):
183 print ("openmanod version " + __version__ + ' ' + version_date)
184 print ("(c) Copyright Telefonica")
186 elif o in ("-h", "--help"):
189 elif o in ("-V", "--vnf-repository"):
191 elif o in ("-c", "--config"):
193 elif o in ("-p", "--port"):
195 elif o in ("-P", "--adminport"):
197 elif o == "--log-socket-port":
199 elif o == "--log-socket-host":
201 elif o == "--log-file":
204 assert False, "Unhandled option"
206 set_logging_file(log_file)
207 global_config = load_configuration(config_file)
208 global_config["version"] = __version__
209 global_config["version_date"] = version_date
211 # Override parameters obtained by command line
213 global_config['http_port'] = port
215 global_config['http_admin_port'] = port_admin
217 global_config['log_socket_host'] = log_socket_host
219 global_config['log_socket_port'] = log_socket_port
220 # if vnf_repository is not None:
221 # global_config['vnf_repository'] = vnf_repository
223 # if not 'vnf_repository' in global_config:
224 # logger.error( os.getcwd() )
225 # global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
226 # #print global_config
227 # if not os.path.exists(global_config['vnf_repository']):
228 # logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
230 # os.makedirs(global_config['vnf_repository'])
231 # except Exception as e:
232 # logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
235 global_config["console_port_iterator"] = console_port_iterator
236 global_config["console_thread"]={}
237 global_config["console_ports"]={}
238 if not global_config["http_console_host"]:
239 global_config["http_console_host"] = global_config["http_host"]
240 if global_config["http_host"]=="0.0.0.0":
241 global_config["http_console_host"] = socket.gethostname()
243 # Configure logging STEP 2
244 if "log_host" in global_config:
245 socket_handler= log_handlers.SocketHandler(global_config["log_socket_host"], global_config["log_socket_port"])
246 socket_handler.setFormatter(log_formatter_complete)
247 if global_config.get("log_socket_level") and global_config["log_socket_level"] != global_config["log_level"]:
248 socket_handler.setLevel(global_config["log_socket_level"])
249 logger.addHandler(socket_handler)
251 # logger.addHandler(log_handlers.SysLogHandler())
253 global_config['log_file'] = log_file
254 elif global_config.get('log_file'):
255 set_logging_file(global_config['log_file'])
257 # logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
258 logger.setLevel(getattr(logging, global_config['log_level']))
259 logger.critical("Starting openmano server version: '%s %s' command: '%s'",
260 __version__, version_date, " ".join(sys.argv))
262 for log_module in ("nfvo", "http", "vim", "db", "console", "ovim"):
263 log_level_module = "log_level_" + log_module
264 log_file_module = "log_file_" + log_module
265 logger_module = logging.getLogger('openmano.' + log_module)
266 if log_level_module in global_config:
267 logger_module.setLevel(global_config[log_level_module])
268 if log_file_module in global_config:
270 file_handler = logging.handlers.RotatingFileHandler(global_config[log_file_module],
271 maxBytes=100e6, backupCount=9, delay=0)
272 file_handler.setFormatter(log_formatter_simple)
273 logger_module.addHandler(file_handler)
275 raise LoadConfigurationException(
276 "Cannot open logging file '{}': {}. Check folder exist and permissions".format(
277 global_config[log_file_module], str(e)) )
278 global_config["logger_"+log_module] = logger_module
279 #httpserver.logger = global_config["logger_http"]
280 #nfvo.logger = global_config["logger_nfvo"]
282 # Initialize DB connection
283 mydb = nfvo_db.nfvo_db();
284 mydb.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name'])
285 db_path = osm_ro.__path__[0] + "/database_utils"
286 if not os.path.exists(db_path + "/migrate_mano_db.sh"):
287 db_path = osm_ro.__path__[0] + "/../database_utils"
289 r = mydb.get_db_version()
290 if r[0] != database_version:
291 logger.critical("DATABASE wrong version '{current}'. Try to upgrade/downgrade to version '{target}'"
292 " with '{db_path}/migrate_mano_db.sh {target}'".format(
293 current=r[0], target=database_version, db_path=db_path))
295 except db_base_Exception as e:
296 logger.critical("DATABASE is not valid. If you think it is corrupted, you can init it with"
297 " '{db_path}/init_mano_db.sh' script".format(db_path=db_path))
300 nfvo.global_config=global_config
301 nfvo.start_service(mydb)
303 httpthread = httpserver.httpserver(mydb, False, global_config['http_host'], global_config['http_port'])
306 if 'http_admin_port' in global_config:
307 httpthreadadmin = httpserver.httpserver(mydb, True, global_config['http_host'], global_config['http_admin_port'])
308 httpthreadadmin.start()
310 logger.info('Waiting for http clients')
311 print('Waiting for http clients')
312 print('openmanod ready')
313 print('====================')
317 #TODO: Interactive console must be implemented here instead of join or sleep
320 #if 'http_admin_port' in global_config:
321 # httpthreadadmin.join()
325 except KeyboardInterrupt as e:
329 except getopt.GetoptError as e:
330 logger.critical(str(e)) # will print something like "option -a not recognized"
333 except LoadConfigurationException as e:
334 logger.critical(str(e))
336 except db_base_Exception as e:
337 logger.critical(str(e))
339 except nfvo.NfvoException as e:
340 logger.critical(str(e), exc_info=True)