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.12-r522"
52 version_date = "May 2017"
53 database_version = 20 #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}".format(file=configuration_file, pos=error_pos) )
93 except js_e.ValidationError as e:
96 error_pos=" at '" + ":".join(map(str, e.path))+"'"
97 raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(file=configuration_file, pos=error_pos, message=str(e)) )
98 except Exception as e:
99 raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(file=configuration_file, message=str(e) ) )
102 def console_port_iterator():
103 '''this iterator deals with the http_console_ports
104 returning the ports one by one
107 while index < len(global_config["http_console_ports"]):
108 port = global_config["http_console_ports"][index]
109 #print("ports -> ", port)
110 if type(port) is int:
112 else: #this is dictionary with from to keys
114 #print("ports -> ", port, port2)
115 while port2 <= port["to"]:
116 #print("ports -> ", port, port2)
123 print("Usage: ", sys.argv[0], "[options]")
124 print( " -v|--version: prints current version")
125 print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)")
126 print( " -h|--help: shows this help")
127 print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)")
128 print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)")
129 #print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file")
130 print( " --log-socket-host HOST: send logs to this host")
131 print( " --log-socket-port PORT: send logs using this port (default: 9022)")
132 print( " --log-file FILE: send logs to this file")
135 if __name__=="__main__":
136 #Configure logging step 1
137 hostname = socket.gethostname()
138 #streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
139 # "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
140 log_formatter_complete = logging.Formatter(
141 '%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s severity:%(levelname)s logger:%(name)s log:%(message)s'.format(host=hostname),
142 datefmt='%Y-%m-%dT%H:%M:%S',
144 log_format_simple = "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
145 log_formatter_simple = logging.Formatter(log_format_simple, datefmt='%Y-%m-%dT%H:%M:%S')
146 logging.basicConfig(format=log_format_simple, level= logging.DEBUG)
147 logger = logging.getLogger('openmano')
148 logger.setLevel(logging.DEBUG)
149 socket_handler = None
151 # Read parameters and configuration file
154 #load parameters and configuration
155 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="])
158 config_file = 'osm_ro/openmanod.cfg'
159 vnf_repository = None
161 log_socket_host = None
162 log_socket_port = None
165 if o in ("-v", "--version"):
166 print ("openmanod version " + __version__ + ' ' + version_date)
167 print ("(c) Copyright Telefonica")
169 elif o in ("-h", "--help"):
172 elif o in ("-V", "--vnf-repository"):
174 elif o in ("-c", "--config"):
176 elif o in ("-p", "--port"):
178 elif o in ("-P", "--adminport"):
180 elif o == "--log-socket-port":
182 elif o == "--log-socket-host":
184 elif o == "--log-file":
187 assert False, "Unhandled option"
188 global_config = load_configuration(config_file)
189 global_config["version"] = __version__
190 global_config["version_date"] = version_date
192 # Override parameters obtained by command line
194 global_config['http_port'] = port
196 global_config['http_admin_port'] = port_admin
198 global_config['log_socket_host'] = log_socket_host
200 global_config['log_socket_port'] = log_socket_port
202 global_config['log_file'] = log_file
203 # if vnf_repository is not None:
204 # global_config['vnf_repository'] = vnf_repository
206 # if not 'vnf_repository' in global_config:
207 # logger.error( os.getcwd() )
208 # global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
209 # #print global_config
210 # if not os.path.exists(global_config['vnf_repository']):
211 # logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
213 # os.makedirs(global_config['vnf_repository'])
214 # except Exception as e:
215 # logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
218 global_config["console_port_iterator"] = console_port_iterator
219 global_config["console_thread"]={}
220 global_config["console_ports"]={}
221 if not global_config["http_console_host"]:
222 global_config["http_console_host"] = global_config["http_host"]
223 if global_config["http_host"]=="0.0.0.0":
224 global_config["http_console_host"] = socket.gethostname()
226 #Configure logging STEP 2
227 if "log_host" in global_config:
228 socket_handler= log_handlers.SocketHandler(global_config["log_socket_host"], global_config["log_socket_port"])
229 socket_handler.setFormatter(log_formatter_complete)
230 if global_config.get("log_socket_level") and global_config["log_socket_level"] != global_config["log_level"]:
231 socket_handler.setLevel(global_config["log_socket_level"])
232 logger.addHandler(socket_handler)
233 #logger.addHandler(log_handlers.SysLogHandler())
234 if "log_file" in global_config:
236 file_handler= logging.handlers.RotatingFileHandler(global_config["log_file"], maxBytes=100e6, backupCount=9, delay=0)
237 file_handler.setFormatter(log_formatter_simple)
238 logger.addHandler(file_handler)
239 #logger.debug("moving logs to '%s'", global_config["log_file"])
240 #remove initial stream handler
241 logging.root.removeHandler(logging.root.handlers[0])
242 print ("logging on '{}'".format(global_config["log_file"]))
244 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config["log_file"], str(e)) )
245 #logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
246 logger.setLevel(getattr(logging, global_config['log_level']))
247 logger.critical("Starting openmano server version: '%s %s' command: '%s'",
248 __version__, version_date, " ".join(sys.argv))
250 for log_module in ("nfvo", "http", "vim", "db", "console", "ovim"):
251 log_level_module = "log_level_" + log_module
252 log_file_module = "log_file_" + log_module
253 logger_module = logging.getLogger('openmano.' + log_module)
254 if log_level_module in global_config:
255 logger_module.setLevel(global_config[log_level_module])
256 if log_file_module in global_config:
258 file_handler= logging.handlers.RotatingFileHandler(global_config[log_file_module], maxBytes=100e6, backupCount=9, delay=0)
259 file_handler.setFormatter(log_formatter_simple)
260 logger_module.addHandler(file_handler)
262 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config[log_file_module], str(e)) )
263 global_config["logger_"+log_module] = logger_module
264 #httpserver.logger = global_config["logger_http"]
265 #nfvo.logger = global_config["logger_nfvo"]
267 # Initialize DB connection
268 mydb = nfvo_db.nfvo_db();
269 mydb.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name'])
270 db_path = osm_ro.__path__[0] + "/database_utils"
271 if not os.path.exists(db_path + "/migrate_mano_db.sh"):
272 db_path = osm_ro.__path__[0] + "/../database_utils"
274 r = mydb.get_db_version()
275 if r[0] != database_version:
276 logger.critical("DATABASE wrong version '{current}'. Try to upgrade/downgrade to version '{target}'"
277 " with '{db_path}/migrate_mano_db.sh {target}'".format(
278 current=r[0], target=database_version, db_path=db_path))
280 except db_base_Exception as e:
281 logger.critical("DATABASE is not valid. If you think it is corrupted, you can init it with"
282 " '{db_path}/init_mano_db.sh' script".format(db_path=db_path))
285 nfvo.global_config=global_config
286 nfvo.start_service(mydb)
288 httpthread = httpserver.httpserver(mydb, False, global_config['http_host'], global_config['http_port'])
291 if 'http_admin_port' in global_config:
292 httpthreadadmin = httpserver.httpserver(mydb, True, global_config['http_host'], global_config['http_admin_port'])
293 httpthreadadmin.start()
295 logger.info('Waiting for http clients')
296 print('Waiting for http clients')
297 print('openmanod ready')
298 print('====================')
302 #TODO: Interactive console must be implemented here instead of join or sleep
305 #if 'http_admin_port' in global_config:
306 # httpthreadadmin.join()
310 except KeyboardInterrupt as e:
314 except getopt.GetoptError as e:
315 logger.critical(str(e)) # will print something like "option -a not recognized"
318 except LoadConfigurationException as e:
319 logger.critical(str(e))
321 except db_base_Exception as e:
322 logger.critical(str(e))