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.
34 __author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes"
35 __date__ ="$26-aug-2014 11:09:29$"
36 __version__="0.5.10-r520"
37 version_date="Apr 2017"
38 database_version="0.20" #expected database schema version
44 from jsonschema import validate as js_v, exceptions as js_e
46 import logging.handlers as log_handlers
48 from osm_ro import httpserver, nfvo, nfvo_db
49 from osm_ro.openmano_schemas import config_schema
50 from osm_ro.db_base import db_base_Exception
56 class LoadConfigurationException(Exception):
59 def load_configuration(configuration_file):
60 default_tokens = {'http_port':9090,
61 'http_host':'localhost',
62 'http_console_proxy': True,
63 'http_console_host': None,
65 'log_socket_port': 9022,
66 'auto_push_VNF_to_VIMs': True,
67 'db_host': 'localhost',
68 'db_ovim_host': 'localhost'
71 #Check config file exists
72 with open(configuration_file, 'r') as f:
74 #Parse configuration file
75 config = yaml.load(config_str)
76 #Validate configuration file with the config_schema
77 js_v(config, config_schema)
79 #Add default values tokens
80 for k,v in default_tokens.items():
85 except yaml.YAMLError as e:
87 if hasattr(e, 'problem_mark'):
89 error_pos = " at line:{} column:{}".format(mark.line+1, mark.column+1)
90 raise LoadConfigurationException("Bad YAML format at configuration file '{file}'{pos}".format(file=configuration_file, pos=error_pos) )
91 except js_e.ValidationError as e:
94 error_pos=" at '" + ":".join(map(str, e.path))+"'"
95 raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(file=configuration_file, pos=error_pos, message=str(e)) )
96 except Exception as e:
97 raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(file=configuration_file, message=str(e) ) )
100 def console_port_iterator():
101 '''this iterator deals with the http_console_ports
102 returning the ports one by one
105 while index < len(global_config["http_console_ports"]):
106 port = global_config["http_console_ports"][index]
107 #print("ports -> ", port)
108 if type(port) is int:
110 else: #this is dictionary with from to keys
112 #print("ports -> ", port, port2)
113 while port2 <= port["to"]:
114 #print("ports -> ", port, port2)
121 print("Usage: ", sys.argv[0], "[options]")
122 print( " -v|--version: prints current version")
123 print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)")
124 print( " -h|--help: shows this help")
125 print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)")
126 print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)")
127 #print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file")
128 print( " --log-socket-host HOST: send logs to this host")
129 print( " --log-socket-port PORT: send logs using this port (default: 9022)")
130 print( " --log-file FILE: send logs to this file")
133 if __name__=="__main__":
134 #Configure logging step 1
135 hostname = socket.gethostname()
136 #streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
137 # "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
138 log_formatter_complete = logging.Formatter(
139 '%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s severity:%(levelname)s logger:%(name)s log:%(message)s'.format(host=hostname),
140 datefmt='%Y-%m-%dT%H:%M:%S',
142 log_format_simple = "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
143 log_formatter_simple = logging.Formatter(log_format_simple, datefmt='%Y-%m-%dT%H:%M:%S')
144 logging.basicConfig(format=log_format_simple, level= logging.DEBUG)
145 logger = logging.getLogger('openmano')
146 logger.setLevel(logging.DEBUG)
147 socket_handler = None
149 # Read parameters and configuration file
152 #load parameters and configuration
153 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="])
156 config_file = 'osm_ro/openmanod.cfg'
157 vnf_repository = None
159 log_socket_host = None
160 log_socket_port = None
163 if o in ("-v", "--version"):
164 print ("openmanod version " + __version__ + ' ' + version_date)
165 print ("(c) Copyright Telefonica")
167 elif o in ("-h", "--help"):
170 elif o in ("-V", "--vnf-repository"):
172 elif o in ("-c", "--config"):
174 elif o in ("-p", "--port"):
176 elif o in ("-P", "--adminport"):
178 elif o == "--log-socket-port":
180 elif o == "--log-socket-host":
182 elif o == "--log-file":
185 assert False, "Unhandled option"
186 global_config = load_configuration(config_file)
188 # Override parameters obtained by command line
190 global_config['http_port'] = port
192 global_config['http_admin_port'] = port_admin
194 global_config['log_socket_host'] = log_socket_host
196 global_config['log_socket_port'] = log_socket_port
198 global_config['log_file'] = log_file
199 # if vnf_repository is not None:
200 # global_config['vnf_repository'] = vnf_repository
202 # if not 'vnf_repository' in global_config:
203 # logger.error( os.getcwd() )
204 # global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
205 # #print global_config
206 # if not os.path.exists(global_config['vnf_repository']):
207 # logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
209 # os.makedirs(global_config['vnf_repository'])
210 # except Exception as e:
211 # logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
214 global_config["console_port_iterator"] = console_port_iterator
215 global_config["console_thread"]={}
216 global_config["console_ports"]={}
217 if not global_config["http_console_host"]:
218 global_config["http_console_host"] = global_config["http_host"]
219 if global_config["http_host"]=="0.0.0.0":
220 global_config["http_console_host"] = socket.gethostname()
222 #Configure logging STEP 2
223 if "log_host" in global_config:
224 socket_handler= log_handlers.SocketHandler(global_config["log_socket_host"], global_config["log_socket_port"])
225 socket_handler.setFormatter(log_formatter_complete)
226 if global_config.get("log_socket_level") and global_config["log_socket_level"] != global_config["log_level"]:
227 socket_handler.setLevel(global_config["log_socket_level"])
228 logger.addHandler(socket_handler)
229 #logger.addHandler(log_handlers.SysLogHandler())
230 if "log_file" in global_config:
232 file_handler= logging.handlers.RotatingFileHandler(global_config["log_file"], maxBytes=100e6, backupCount=9, delay=0)
233 file_handler.setFormatter(log_formatter_simple)
234 logger.addHandler(file_handler)
235 #logger.debug("moving logs to '%s'", global_config["log_file"])
236 #remove initial stream handler
237 logging.root.removeHandler(logging.root.handlers[0])
238 print ("logging on '{}'".format(global_config["log_file"]))
240 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config["log_file"], str(e)) )
241 #logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
242 logger.setLevel(getattr(logging, global_config['log_level']))
243 logger.critical("Starting openmano server version: '%s %s' command: '%s'",
244 __version__, version_date, " ".join(sys.argv))
246 for log_module in ("nfvo", "http", "vim", "db", "console", "ovim"):
247 log_level_module = "log_level_" + log_module
248 log_file_module = "log_file_" + log_module
249 logger_module = logging.getLogger('openmano.' + log_module)
250 if log_level_module in global_config:
251 logger_module.setLevel(global_config[log_level_module])
252 if log_file_module in global_config:
254 file_handler= logging.handlers.RotatingFileHandler(global_config[log_file_module], maxBytes=100e6, backupCount=9, delay=0)
255 file_handler.setFormatter(log_formatter_simple)
256 logger_module.addHandler(file_handler)
258 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config[log_file_module], str(e)) )
259 global_config["logger_"+log_module] = logger_module
260 #httpserver.logger = global_config["logger_http"]
261 #nfvo.logger = global_config["logger_nfvo"]
263 # Initialize DB connection
264 mydb = nfvo_db.nfvo_db();
265 mydb.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name'])
267 r = mydb.get_db_version()
268 if r[1] != database_version:
269 logger.critical("DATABASE wrong version '%s'. \
270 Try to upgrade/downgrade to version '%s' with '%s/database_utils/migrate_mano_db.sh'",
271 r[1], database_version, osm_ro.__path__[0])
273 except db_base_Exception as e:
274 logger.critical("DATABASE is not a MANO one or it is a '0.0' version. Try to upgrade to version '%s' with \
275 './database_utils/migrate_mano_db.sh'", database_version)
278 nfvo.global_config=global_config
279 nfvo.start_service(mydb)
281 httpthread = httpserver.httpserver(mydb, False, global_config['http_host'], global_config['http_port'])
284 if 'http_admin_port' in global_config:
285 httpthreadadmin = httpserver.httpserver(mydb, True, global_config['http_host'], global_config['http_admin_port'])
286 httpthreadadmin.start()
288 logger.info('Waiting for http clients')
289 print('Waiting for http clients')
290 print('openmanod ready')
291 print('====================')
295 #TODO: Interactive console must be implemented here instead of join or sleep
298 #if 'http_admin_port' in global_config:
299 # httpthreadadmin.join()
303 except KeyboardInterrupt as e:
307 except getopt.GetoptError as e:
308 logger.critical(str(e)) # will print something like "option -a not recognized"
311 except LoadConfigurationException as e:
312 logger.critical(str(e))
314 except db_base_Exception as e:
315 logger.critical(str(e))