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.9-r519"
37 version_date
="Mar 2017"
38 database_version
="0.20" #expected database schema version
46 from jsonschema
import validate
as js_v
, exceptions
as js_e
47 from openmano_schemas
import config_schema
48 from db_base
import db_base_Exception
51 import logging
.handlers
as log_handlers
57 class LoadConfigurationException(Exception):
60 def load_configuration(configuration_file
):
61 default_tokens
= {'http_port':9090,
62 'http_host':'localhost',
63 'http_console_proxy': True,
64 'http_console_host': None,
66 'log_socket_port': 9022,
67 'auto_push_VNF_to_VIMs': True,
68 'db_host': 'localhost',
69 'db_ovim_host': 'localhost'
72 #Check config file exists
73 with
open(configuration_file
, 'r') as f
:
75 #Parse configuration file
76 config
= yaml
.load(config_str
)
77 #Validate configuration file with the config_schema
78 js_v(config
, config_schema
)
80 #Add default values tokens
81 for k
,v
in default_tokens
.items():
86 except yaml
.YAMLError
as e
:
88 if hasattr(e
, 'problem_mark'):
90 error_pos
= " at line:{} column:{}".format(mark
.line
+1, mark
.column
+1)
91 raise LoadConfigurationException("Bad YAML format at configuration file '{file}'{pos}".format(file=configuration_file
, pos
=error_pos
) )
92 except js_e
.ValidationError
as e
:
95 error_pos
=" at '" + ":".join(map(str, e
.path
))+"'"
96 raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(file=configuration_file
, pos
=error_pos
, message
=str(e
)) )
97 except Exception as e
:
98 raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(file=configuration_file
, message
=str(e
) ) )
101 def console_port_iterator():
102 '''this iterator deals with the http_console_ports
103 returning the ports one by one
106 while index
< len(global_config
["http_console_ports"]):
107 port
= global_config
["http_console_ports"][index
]
108 #print("ports -> ", port)
109 if type(port
) is int:
111 else: #this is dictionary with from to keys
113 #print("ports -> ", port, port2)
114 while port2
<= port
["to"]:
115 #print("ports -> ", port, port2)
122 print("Usage: ", sys
.argv
[0], "[options]")
123 print( " -v|--version: prints current version")
124 print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)")
125 print( " -h|--help: shows this help")
126 print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)")
127 print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)")
128 #print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file")
129 print( " --log-socket-host HOST: send logs to this host")
130 print( " --log-socket-port PORT: send logs using this port (default: 9022)")
131 print( " --log-file FILE: send logs to this file")
134 if __name__
=="__main__":
135 #Configure logging step 1
136 hostname
= socket
.gethostname()
137 #streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
138 # "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
139 log_formatter_complete
= logging
.Formatter(
140 '%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s severity:%(levelname)s logger:%(name)s log:%(message)s'.format(host
=hostname
),
141 datefmt
='%Y-%m-%dT%H:%M:%S',
143 log_format_simple
= "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
144 log_formatter_simple
= logging
.Formatter(log_format_simple
, datefmt
='%Y-%m-%dT%H:%M:%S')
145 logging
.basicConfig(format
=log_format_simple
, level
= logging
.DEBUG
)
146 logger
= logging
.getLogger('openmano')
147 logger
.setLevel(logging
.DEBUG
)
148 socket_handler
= None
150 # Read parameters and configuration file
153 #load parameters and configuration
154 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="])
157 config_file
= 'openmanod.cfg'
158 vnf_repository
= None
160 log_socket_host
= None
161 log_socket_port
= None
164 if o
in ("-v", "--version"):
165 print ("openmanod version " + __version__
+ ' ' + version_date
)
166 print ("(c) Copyright Telefonica")
168 elif o
in ("-h", "--help"):
171 elif o
in ("-V", "--vnf-repository"):
173 elif o
in ("-c", "--config"):
175 elif o
in ("-p", "--port"):
177 elif o
in ("-P", "--adminport"):
179 elif o
== "--log-socket-port":
181 elif o
== "--log-socket-host":
183 elif o
== "--log-file":
186 assert False, "Unhandled option"
187 global_config
= load_configuration(config_file
)
189 # Override parameters obtained by command line
191 global_config
['http_port'] = port
193 global_config
['http_admin_port'] = port_admin
195 global_config
['log_socket_host'] = log_socket_host
197 global_config
['log_socket_port'] = log_socket_port
199 global_config
['log_file'] = log_file
200 # if vnf_repository is not None:
201 # global_config['vnf_repository'] = vnf_repository
203 # if not 'vnf_repository' in global_config:
204 # logger.error( os.getcwd() )
205 # global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
206 # #print global_config
207 # if not os.path.exists(global_config['vnf_repository']):
208 # logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
210 # os.makedirs(global_config['vnf_repository'])
211 # except Exception as e:
212 # logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
215 global_config
["console_port_iterator"] = console_port_iterator
216 global_config
["console_thread"]={}
217 global_config
["console_ports"]={}
218 if not global_config
["http_console_host"]:
219 global_config
["http_console_host"] = global_config
["http_host"]
220 if global_config
["http_host"]=="0.0.0.0":
221 global_config
["http_console_host"] = socket
.gethostname()
223 #Configure logging STEP 2
224 if "log_host" in global_config
:
225 socket_handler
= log_handlers
.SocketHandler(global_config
["log_socket_host"], global_config
["log_socket_port"])
226 socket_handler
.setFormatter(log_formatter_complete
)
227 if global_config
.get("log_socket_level") and global_config
["log_socket_level"] != global_config
["log_level"]:
228 socket_handler
.setLevel(global_config
["log_socket_level"])
229 logger
.addHandler(socket_handler
)
230 #logger.addHandler(log_handlers.SysLogHandler())
231 if "log_file" in global_config
:
233 file_handler
= logging
.handlers
.RotatingFileHandler(global_config
["log_file"], maxBytes
=100e6
, backupCount
=9, delay
=0)
234 file_handler
.setFormatter(log_formatter_simple
)
235 logger
.addHandler(file_handler
)
236 #logger.debug("moving logs to '%s'", global_config["log_file"])
237 #remove initial stream handler
238 logging
.root
.removeHandler(logging
.root
.handlers
[0])
239 print ("logging on '{}'".format(global_config
["log_file"]))
241 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config
["log_file"], str(e
)) )
242 #logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
243 logger
.setLevel(getattr(logging
, global_config
['log_level']))
244 logger
.critical("Starting openmano server version: '%s %s' command: '%s'",
245 __version__
, version_date
, " ".join(sys
.argv
))
247 for log_module
in ("nfvo", "http", "vim", "db", "console", "ovim"):
248 log_level_module
= "log_level_" + log_module
249 log_file_module
= "log_file_" + log_module
250 logger_module
= logging
.getLogger('openmano.' + log_module
)
251 if log_level_module
in global_config
:
252 logger_module
.setLevel(global_config
[log_level_module
])
253 if log_file_module
in global_config
:
255 file_handler
= logging
.handlers
.RotatingFileHandler(global_config
[log_file_module
], maxBytes
=100e6
, backupCount
=9, delay
=0)
256 file_handler
.setFormatter(log_formatter_simple
)
257 logger_module
.addHandler(file_handler
)
259 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config
[log_file_module
], str(e
)) )
260 global_config
["logger_"+log_module
] = logger_module
261 #httpserver.logger = global_config["logger_http"]
262 #nfvo.logger = global_config["logger_nfvo"]
264 # Initialize DB connection
265 mydb
= nfvo_db
.nfvo_db();
266 mydb
.connect(global_config
['db_host'], global_config
['db_user'], global_config
['db_passwd'], global_config
['db_name'])
268 r
= mydb
.get_db_version()
269 if r
[1] != database_version
:
270 logger
.critical("DATABASE wrong version '%s'. \
271 Try to upgrade/downgrade to version '%s' with './database_utils/migrate_mano_db.sh'",
272 r
[1], database_version
)
274 except db_base_Exception
as e
:
275 logger
.critical("DATABASE is not a MANO one or it is a '0.0' version. Try to upgrade to version '%s' with \
276 './database_utils/migrate_mano_db.sh'", database_version
)
279 nfvo
.global_config
=global_config
280 nfvo
.start_service(mydb
)
282 httpthread
= httpserver
.httpserver(mydb
, False, global_config
['http_host'], global_config
['http_port'])
285 if 'http_admin_port' in global_config
:
286 httpthreadadmin
= httpserver
.httpserver(mydb
, True, global_config
['http_host'], global_config
['http_admin_port'])
287 httpthreadadmin
.start()
289 logger
.info('Waiting for http clients')
290 print('Waiting for http clients')
291 print('openmanod ready')
292 print('====================')
296 #TODO: Interactive console must be implemented here instead of join or sleep
299 #if 'http_admin_port' in global_config:
300 # httpthreadadmin.join()
304 except KeyboardInterrupt as e
:
308 except getopt
.GetoptError
as e
:
309 logger
.critical(str(e
)) # will print something like "option -a not recognized"
312 except LoadConfigurationException
as e
:
313 logger
.critical(str(e
))
315 except db_base_Exception
as e
:
316 logger
.critical(str(e
))