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.50-r560"
52 version_date = "Jan 2018"
53 database_version = 27 # expected database schema version
60 class LoadConfigurationException(Exception):
64 def load_configuration(configuration_file):
65 default_tokens = {'http_port': 9090,
66 'http_host': 'localhost',
67 'http_console_proxy': True,
68 'http_console_host': None,
70 'log_socket_port': 9022,
71 'auto_push_VNF_to_VIMs': True,
72 'db_host': 'localhost',
73 'db_ovim_host': 'localhost'
76 #Check config file exists
77 with open(configuration_file, 'r') as f:
79 #Parse configuration file
80 config = yaml.load(config_str)
81 #Validate configuration file with the config_schema
82 js_v(config, config_schema)
84 #Add default values tokens
85 for k,v in default_tokens.items():
90 except yaml.YAMLError as e:
92 if hasattr(e, 'problem_mark'):
94 error_pos = " at line:{} column:{}".format(mark.line+1, mark.column+1)
95 raise LoadConfigurationException("Bad YAML format at configuration file '{file}'{pos}: {message}".format(
96 file=configuration_file, pos=error_pos, message=e))
97 except js_e.ValidationError as e:
100 error_pos=" at '" + ":".join(map(str, e.path))+"'"
101 raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(
102 file=configuration_file, pos=error_pos, message=e))
103 except Exception as e:
104 raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(
105 file=configuration_file, message=e))
108 def console_port_iterator():
109 '''this iterator deals with the http_console_ports
110 returning the ports one by one
113 while index < len(global_config["http_console_ports"]):
114 port = global_config["http_console_ports"][index]
115 #print("ports -> ", port)
116 if type(port) is int:
118 else: #this is dictionary with from to keys
120 #print("ports -> ", port, port2)
121 while port2 <= port["to"]:
122 #print("ports -> ", port, port2)
129 print("Usage: ", sys.argv[0], "[options]")
130 print( " -v|--version: prints current version")
131 print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)")
132 print( " -h|--help: shows this help")
133 print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)")
134 print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)")
135 #print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file")
136 print( " --log-socket-host HOST: send logs to this host")
137 print( " --log-socket-port PORT: send logs using this port (default: 9022)")
138 print( " --log-file FILE: send logs to this file")
142 def set_logging_file(log_file):
144 file_handler = logging.handlers.RotatingFileHandler(log_file, maxBytes=100e6, backupCount=9, delay=0)
145 file_handler.setFormatter(log_formatter_simple)
146 logger.addHandler(file_handler)
147 # logger.debug("moving logs to '%s'", global_config["log_file"])
148 # remove initial stream handler
149 logging.root.removeHandler(logging.root.handlers[0])
150 print ("logging on '{}'".format(log_file))
152 raise LoadConfigurationException(
153 "Cannot open logging file '{}': {}. Check folder exist and permissions".format(log_file, e))
156 if __name__=="__main__":
157 # Configure logging step 1
158 hostname = socket.gethostname()
159 # streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
160 # "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
161 log_formatter_complete = logging.Formatter('%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s '
162 'severity:%(levelname)s logger:%(name)s log:%(message)s'.format(
164 datefmt='%Y-%m-%dT%H:%M:%S')
165 log_format_simple = "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
166 log_formatter_simple = logging.Formatter(log_format_simple, datefmt='%Y-%m-%dT%H:%M:%S')
167 logging.basicConfig(format=log_format_simple, level= logging.DEBUG)
168 logger = logging.getLogger('openmano')
169 logger.setLevel(logging.DEBUG)
170 socket_handler = None
171 # Read parameters and configuration file
174 #load parameters and configuration
175 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="])
178 config_file = 'osm_ro/openmanod.cfg'
179 vnf_repository = None
181 log_socket_host = None
182 log_socket_port = None
185 if o in ("-v", "--version"):
186 print ("openmanod version " + __version__ + ' ' + version_date)
187 print ("(c) Copyright Telefonica")
189 elif o in ("-h", "--help"):
192 elif o in ("-V", "--vnf-repository"):
194 elif o in ("-c", "--config"):
196 elif o in ("-p", "--port"):
198 elif o in ("-P", "--adminport"):
200 elif o == "--log-socket-port":
202 elif o == "--log-socket-host":
204 elif o == "--log-file":
207 assert False, "Unhandled option"
209 set_logging_file(log_file)
210 global_config = load_configuration(config_file)
211 global_config["version"] = __version__
212 global_config["version_date"] = version_date
214 # Override parameters obtained by command line
216 global_config['http_port'] = port
218 global_config['http_admin_port'] = port_admin
220 global_config['log_socket_host'] = log_socket_host
222 global_config['log_socket_port'] = log_socket_port
223 # if vnf_repository is not None:
224 # global_config['vnf_repository'] = vnf_repository
226 # if not 'vnf_repository' in global_config:
227 # logger.error( os.getcwd() )
228 # global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
229 # #print global_config
230 # if not os.path.exists(global_config['vnf_repository']):
231 # logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
233 # os.makedirs(global_config['vnf_repository'])
234 # except Exception as e:
235 # logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
238 global_config["console_port_iterator"] = console_port_iterator
239 global_config["console_thread"]={}
240 global_config["console_ports"]={}
241 if not global_config["http_console_host"]:
242 global_config["http_console_host"] = global_config["http_host"]
243 if global_config["http_host"]=="0.0.0.0":
244 global_config["http_console_host"] = socket.gethostname()
246 # Configure logging STEP 2
247 if "log_host" in global_config:
248 socket_handler= log_handlers.SocketHandler(global_config["log_socket_host"], global_config["log_socket_port"])
249 socket_handler.setFormatter(log_formatter_complete)
250 if global_config.get("log_socket_level") and global_config["log_socket_level"] != global_config["log_level"]:
251 socket_handler.setLevel(global_config["log_socket_level"])
252 logger.addHandler(socket_handler)
254 # logger.addHandler(log_handlers.SysLogHandler())
256 global_config['log_file'] = log_file
257 elif global_config.get('log_file'):
258 set_logging_file(global_config['log_file'])
260 # logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
261 logger.setLevel(getattr(logging, global_config['log_level']))
262 logger.critical("Starting openmano server version: '%s %s' command: '%s'",
263 __version__, version_date, " ".join(sys.argv))
265 for log_module in ("nfvo", "http", "vim", "db", "console", "ovim"):
266 log_level_module = "log_level_" + log_module
267 log_file_module = "log_file_" + log_module
268 logger_module = logging.getLogger('openmano.' + log_module)
269 if log_level_module in global_config:
270 logger_module.setLevel(global_config[log_level_module])
271 if log_file_module in global_config:
273 file_handler = logging.handlers.RotatingFileHandler(global_config[log_file_module],
274 maxBytes=100e6, backupCount=9, delay=0)
275 file_handler.setFormatter(log_formatter_simple)
276 logger_module.addHandler(file_handler)
278 raise LoadConfigurationException(
279 "Cannot open logging file '{}': {}. Check folder exist and permissions".format(
280 global_config[log_file_module], str(e)) )
281 global_config["logger_"+log_module] = logger_module
282 #httpserver.logger = global_config["logger_http"]
283 #nfvo.logger = global_config["logger_nfvo"]
285 # Initialize DB connection
286 mydb = nfvo_db.nfvo_db();
287 mydb.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name'])
288 db_path = osm_ro.__path__[0] + "/database_utils"
289 if not os.path.exists(db_path + "/migrate_mano_db.sh"):
290 db_path = osm_ro.__path__[0] + "/../database_utils"
292 r = mydb.get_db_version()
293 if r[0] != database_version:
294 logger.critical("DATABASE wrong version '{current}'. Try to upgrade/downgrade to version '{target}'"
295 " with '{db_path}/migrate_mano_db.sh {target}'".format(
296 current=r[0], target=database_version, db_path=db_path))
298 except db_base_Exception as e:
299 logger.critical("DATABASE is not valid. If you think it is corrupted, you can init it with"
300 " '{db_path}/init_mano_db.sh' script".format(db_path=db_path))
303 nfvo.global_config=global_config
304 nfvo.start_service(mydb)
306 httpthread = httpserver.httpserver(mydb, False, global_config['http_host'], global_config['http_port'])
309 if 'http_admin_port' in global_config:
310 httpthreadadmin = httpserver.httpserver(mydb, True, global_config['http_host'], global_config['http_admin_port'])
311 httpthreadadmin.start()
313 logger.info('Waiting for http clients')
314 print('Waiting for http clients')
315 print('openmanod ready')
316 print('====================')
320 #TODO: Interactive console must be implemented here instead of join or sleep
323 #if 'http_admin_port' in global_config:
324 # httpthreadadmin.join()
328 except KeyboardInterrupt as e:
332 except getopt.GetoptError as e:
333 logger.critical(str(e)) # will print something like "option -a not recognized"
336 except LoadConfigurationException as e:
337 logger.critical(str(e))
339 except db_base_Exception as e:
340 logger.critical(str(e))
342 except nfvo.NfvoException as e:
343 logger.critical(str(e), exc_info=True)