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.21-r531"
52 version_date = "Aug 2017"
53 database_version = 24 # expected database schema version
59 class LoadConfigurationException(Exception):
62 def load_configuration(configuration_file):
63 default_tokens = {'http_port':9090,
64 'http_host':'localhost',
65 'http_console_proxy': True,
66 'http_console_host': None,
68 'log_socket_port': 9022,
69 'auto_push_VNF_to_VIMs': True,
70 'db_host': 'localhost',
71 'db_ovim_host': 'localhost'
74 #Check config file exists
75 with open(configuration_file, 'r') as f:
77 #Parse configuration file
78 config = yaml.load(config_str)
79 #Validate configuration file with the config_schema
80 js_v(config, config_schema)
82 #Add default values tokens
83 for k,v in default_tokens.items():
88 except yaml.YAMLError as e:
90 if hasattr(e, 'problem_mark'):
92 error_pos = " at line:{} column:{}".format(mark.line+1, mark.column+1)
93 raise LoadConfigurationException("Bad YAML format at configuration file '{file}'{pos}: {message}".format(
94 file=configuration_file, pos=error_pos, message=e))
95 except js_e.ValidationError as e:
98 error_pos=" at '" + ":".join(map(str, e.path))+"'"
99 raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(
100 file=configuration_file, pos=error_pos, message=e))
101 except Exception as e:
102 raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(
103 file=configuration_file, message=e))
106 def console_port_iterator():
107 '''this iterator deals with the http_console_ports
108 returning the ports one by one
111 while index < len(global_config["http_console_ports"]):
112 port = global_config["http_console_ports"][index]
113 #print("ports -> ", port)
114 if type(port) is int:
116 else: #this is dictionary with from to keys
118 #print("ports -> ", port, port2)
119 while port2 <= port["to"]:
120 #print("ports -> ", port, port2)
127 print("Usage: ", sys.argv[0], "[options]")
128 print( " -v|--version: prints current version")
129 print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)")
130 print( " -h|--help: shows this help")
131 print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)")
132 print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)")
133 #print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file")
134 print( " --log-socket-host HOST: send logs to this host")
135 print( " --log-socket-port PORT: send logs using this port (default: 9022)")
136 print( " --log-file FILE: send logs to this file")
140 def set_logging_file(log_file):
142 file_handler = logging.handlers.RotatingFileHandler(log_file, maxBytes=100e6, backupCount=9, delay=0)
143 file_handler.setFormatter(log_formatter_simple)
144 logger.addHandler(file_handler)
145 # logger.debug("moving logs to '%s'", global_config["log_file"])
146 # remove initial stream handler
147 logging.root.removeHandler(logging.root.handlers[0])
148 print ("logging on '{}'".format(log_file))
150 raise LoadConfigurationException(
151 "Cannot open logging file '{}': {}. Check folder exist and permissions".format(log_file, e))
154 if __name__=="__main__":
155 # Configure logging step 1
156 hostname = socket.gethostname()
157 # streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
158 # "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
159 log_formatter_complete = logging.Formatter('%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s '
160 'severity:%(levelname)s logger:%(name)s log:%(message)s'.format(
162 datefmt='%Y-%m-%dT%H:%M:%S')
163 log_format_simple = "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
164 log_formatter_simple = logging.Formatter(log_format_simple, datefmt='%Y-%m-%dT%H:%M:%S')
165 logging.basicConfig(format=log_format_simple, level= logging.DEBUG)
166 logger = logging.getLogger('openmano')
167 logger.setLevel(logging.DEBUG)
168 socket_handler = None
169 # Read parameters and configuration file
172 #load parameters and configuration
173 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="])
176 config_file = 'osm_ro/openmanod.cfg'
177 vnf_repository = None
179 log_socket_host = None
180 log_socket_port = None
183 if o in ("-v", "--version"):
184 print ("openmanod version " + __version__ + ' ' + version_date)
185 print ("(c) Copyright Telefonica")
187 elif o in ("-h", "--help"):
190 elif o in ("-V", "--vnf-repository"):
192 elif o in ("-c", "--config"):
194 elif o in ("-p", "--port"):
196 elif o in ("-P", "--adminport"):
198 elif o == "--log-socket-port":
200 elif o == "--log-socket-host":
202 elif o == "--log-file":
205 assert False, "Unhandled option"
207 set_logging_file(log_file)
208 global_config = load_configuration(config_file)
209 global_config["version"] = __version__
210 global_config["version_date"] = version_date
212 # Override parameters obtained by command line
214 global_config['http_port'] = port
216 global_config['http_admin_port'] = port_admin
218 global_config['log_socket_host'] = log_socket_host
220 global_config['log_socket_port'] = log_socket_port
221 # if vnf_repository is not None:
222 # global_config['vnf_repository'] = vnf_repository
224 # if not 'vnf_repository' in global_config:
225 # logger.error( os.getcwd() )
226 # global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
227 # #print global_config
228 # if not os.path.exists(global_config['vnf_repository']):
229 # logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
231 # os.makedirs(global_config['vnf_repository'])
232 # except Exception as e:
233 # logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
236 global_config["console_port_iterator"] = console_port_iterator
237 global_config["console_thread"]={}
238 global_config["console_ports"]={}
239 if not global_config["http_console_host"]:
240 global_config["http_console_host"] = global_config["http_host"]
241 if global_config["http_host"]=="0.0.0.0":
242 global_config["http_console_host"] = socket.gethostname()
244 # Configure logging STEP 2
245 if "log_host" in global_config:
246 socket_handler= log_handlers.SocketHandler(global_config["log_socket_host"], global_config["log_socket_port"])
247 socket_handler.setFormatter(log_formatter_complete)
248 if global_config.get("log_socket_level") and global_config["log_socket_level"] != global_config["log_level"]:
249 socket_handler.setLevel(global_config["log_socket_level"])
250 logger.addHandler(socket_handler)
252 # logger.addHandler(log_handlers.SysLogHandler())
254 global_config['log_file'] = log_file
255 elif global_config.get('log_file'):
256 set_logging_file(global_config['log_file'])
258 # logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
259 logger.setLevel(getattr(logging, global_config['log_level']))
260 logger.critical("Starting openmano server version: '%s %s' command: '%s'",
261 __version__, version_date, " ".join(sys.argv))
263 for log_module in ("nfvo", "http", "vim", "db", "console", "ovim"):
264 log_level_module = "log_level_" + log_module
265 log_file_module = "log_file_" + log_module
266 logger_module = logging.getLogger('openmano.' + log_module)
267 if log_level_module in global_config:
268 logger_module.setLevel(global_config[log_level_module])
269 if log_file_module in global_config:
271 file_handler = logging.handlers.RotatingFileHandler(global_config[log_file_module],
272 maxBytes=100e6, backupCount=9, delay=0)
273 file_handler.setFormatter(log_formatter_simple)
274 logger_module.addHandler(file_handler)
276 raise LoadConfigurationException(
277 "Cannot open logging file '{}': {}. Check folder exist and permissions".format(
278 global_config[log_file_module], str(e)) )
279 global_config["logger_"+log_module] = logger_module
280 #httpserver.logger = global_config["logger_http"]
281 #nfvo.logger = global_config["logger_nfvo"]
283 # Initialize DB connection
284 mydb = nfvo_db.nfvo_db();
285 mydb.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name'])
286 db_path = osm_ro.__path__[0] + "/database_utils"
287 if not os.path.exists(db_path + "/migrate_mano_db.sh"):
288 db_path = osm_ro.__path__[0] + "/../database_utils"
290 r = mydb.get_db_version()
291 if r[0] != database_version:
292 logger.critical("DATABASE wrong version '{current}'. Try to upgrade/downgrade to version '{target}'"
293 " with '{db_path}/migrate_mano_db.sh {target}'".format(
294 current=r[0], target=database_version, db_path=db_path))
296 except db_base_Exception as e:
297 logger.critical("DATABASE is not valid. If you think it is corrupted, you can init it with"
298 " '{db_path}/init_mano_db.sh' script".format(db_path=db_path))
301 nfvo.global_config=global_config
302 nfvo.start_service(mydb)
304 httpthread = httpserver.httpserver(mydb, False, global_config['http_host'], global_config['http_port'])
307 if 'http_admin_port' in global_config:
308 httpthreadadmin = httpserver.httpserver(mydb, True, global_config['http_host'], global_config['http_admin_port'])
309 httpthreadadmin.start()
311 logger.info('Waiting for http clients')
312 print('Waiting for http clients')
313 print('openmanod ready')
314 print('====================')
318 #TODO: Interactive console must be implemented here instead of join or sleep
321 #if 'http_admin_port' in global_config:
322 # httpthreadadmin.join()
326 except KeyboardInterrupt as e:
330 except getopt.GetoptError as e:
331 logger.critical(str(e)) # will print something like "option -a not recognized"
334 except LoadConfigurationException as e:
335 logger.critical(str(e))
337 except db_base_Exception as e:
338 logger.critical(str(e))
340 except nfvo.NfvoException as e:
341 logger.critical(str(e), exc_info=True)