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.8-r518"
37 version_date
="Jan 2017"
38 database_version
="0.19" #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
70 #Check config file exists
71 with
open(configuration_file
, 'r') as f
:
73 #Parse configuration file
74 config
= yaml
.load(config_str
)
75 #Validate configuration file with the config_schema
76 js_v(config
, config_schema
)
78 #Add default values tokens
79 for k
,v
in default_tokens
.items():
84 except yaml
.YAMLError
as e
:
86 if hasattr(e
, 'problem_mark'):
88 error_pos
= " at line:{} column:{}".format(mark
.line
+1, mark
.column
+1)
89 raise LoadConfigurationException("Bad YAML format at configuration file '{file}'{pos}".format(file=configuration_file
, pos
=error_pos
) )
90 except js_e
.ValidationError
as e
:
93 error_pos
=" at '" + ":".join(map(str, e
.path
))+"'"
94 raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(file=configuration_file
, pos
=error_pos
, message
=str(e
)) )
95 except Exception as e
:
96 raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(file=configuration_file
, message
=str(e
) ) )
99 def console_port_iterator():
100 '''this iterator deals with the http_console_ports
101 returning the ports one by one
104 while index
< len(global_config
["http_console_ports"]):
105 port
= global_config
["http_console_ports"][index
]
106 #print("ports -> ", port)
107 if type(port
) is int:
109 else: #this is dictionary with from to keys
111 #print("ports -> ", port, port2)
112 while port2
<= port
["to"]:
113 #print("ports -> ", port, port2)
120 print("Usage: ", sys
.argv
[0], "[options]")
121 print( " -v|--version: prints current version")
122 print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)")
123 print( " -h|--help: shows this help")
124 print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)")
125 print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)")
126 #print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file")
127 print( " --log-socket-host HOST: send logs to this host")
128 print( " --log-socket-port PORT: send logs using this port (default: 9022)")
129 print( " --log-file FILE: send logs to this file")
132 if __name__
=="__main__":
133 #Configure logging step 1
134 hostname
= socket
.gethostname()
135 #streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
136 # "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
137 log_formatter_complete
= logging
.Formatter(
138 '%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s severity:%(levelname)s logger:%(name)s log:%(message)s'.format(host
=hostname
),
139 datefmt
='%Y-%m-%dT%H:%M:%S',
141 log_format_simple
= "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
142 log_formatter_simple
= logging
.Formatter(log_format_simple
, datefmt
='%Y-%m-%dT%H:%M:%S')
143 logging
.basicConfig(format
=log_format_simple
, level
= logging
.DEBUG
)
144 logger
= logging
.getLogger('openmano')
145 logger
.setLevel(logging
.DEBUG
)
146 socket_handler
= None
148 # Read parameters and configuration file
151 #load parameters and configuration
152 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="])
155 config_file
= 'openmanod.cfg'
156 vnf_repository
= None
158 log_socket_host
= None
159 log_socket_port
= None
162 if o
in ("-v", "--version"):
163 print ("openmanod version " + __version__
+ ' ' + version_date
)
164 print ("(c) Copyright Telefonica")
166 elif o
in ("-h", "--help"):
169 elif o
in ("-V", "--vnf-repository"):
171 elif o
in ("-c", "--config"):
173 elif o
in ("-p", "--port"):
175 elif o
in ("-P", "--adminport"):
177 elif o
== "--log-socket-port":
179 elif o
== "--log-socket-host":
181 elif o
== "--log-file":
184 assert False, "Unhandled option"
185 global_config
= load_configuration(config_file
)
187 # Override parameters obtained by command line
189 global_config
['http_port'] = port
191 global_config
['http_admin_port'] = port_admin
193 global_config
['log_socket_host'] = log_socket_host
195 global_config
['log_socket_port'] = log_socket_port
197 global_config
['log_file'] = log_file
198 # if vnf_repository is not None:
199 # global_config['vnf_repository'] = vnf_repository
201 # if not 'vnf_repository' in global_config:
202 # logger.error( os.getcwd() )
203 # global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
204 # #print global_config
205 # if not os.path.exists(global_config['vnf_repository']):
206 # logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
208 # os.makedirs(global_config['vnf_repository'])
209 # except Exception as e:
210 # logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
213 global_config
["console_port_iterator"] = console_port_iterator
214 global_config
["console_thread"]={}
215 global_config
["console_ports"]={}
216 if not global_config
["http_console_host"]:
217 global_config
["http_console_host"] = global_config
["http_host"]
218 if global_config
["http_host"]=="0.0.0.0":
219 global_config
["http_console_host"] = socket
.gethostname()
221 #Configure logging STEP 2
222 if "log_host" in global_config
:
223 socket_handler
= log_handlers
.SocketHandler(global_config
["log_socket_host"], global_config
["log_socket_port"])
224 socket_handler
.setFormatter(log_formatter_complete
)
225 if global_config
.get("log_socket_level") and global_config
["log_socket_level"] != global_config
["log_level"]:
226 socket_handler
.setLevel(global_config
["log_socket_level"])
227 logger
.addHandler(socket_handler
)
228 #logger.addHandler(log_handlers.SysLogHandler())
229 if "log_file" in global_config
:
231 file_handler
= logging
.handlers
.RotatingFileHandler(global_config
["log_file"], maxBytes
=100e6
, backupCount
=9, delay
=0)
232 file_handler
.setFormatter(log_formatter_simple
)
233 logger
.addHandler(file_handler
)
234 #logger.debug("moving logs to '%s'", global_config["log_file"])
235 #remove initial stream handler
236 logging
.root
.removeHandler(logging
.root
.handlers
[0])
237 print ("logging on '{}'".format(global_config
["log_file"]))
239 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config
["log_file"], str(e
)) )
240 #logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
241 logger
.setLevel(getattr(logging
, global_config
['log_level']))
242 logger
.critical("Starting openmano server version: '%s %s' command: '%s'",
243 __version__
, version_date
, " ".join(sys
.argv
))
245 for log_module
in ("nfvo", "http", "vim", "db", "console"):
246 log_level_module
= "log_level_" + log_module
247 log_file_module
= "log_file_" + log_module
248 logger_module
= logging
.getLogger('openmano.' + log_module
)
249 if log_level_module
in global_config
:
250 logger_module
.setLevel(global_config
[log_level_module
])
251 if log_file_module
in global_config
:
253 file_handler
= logging
.handlers
.RotatingFileHandler(global_config
[log_file_module
], maxBytes
=100e6
, backupCount
=9, delay
=0)
254 file_handler
.setFormatter(log_formatter_simple
)
255 logger_module
.addHandler(file_handler
)
257 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config
[log_file_module
], str(e
)) )
258 global_config
["logger_"+log_module
] = logger_module
259 #httpserver.logger = global_config["logger_http"]
260 #nfvo.logger = global_config["logger_nfvo"]
262 # Initialize DB connection
263 mydb
= nfvo_db
.nfvo_db();
264 mydb
.connect(global_config
['db_host'], global_config
['db_user'], global_config
['db_passwd'], global_config
['db_name'])
266 r
= mydb
.get_db_version()
267 if r
[1] != database_version
:
268 logger
.critical("DATABASE wrong version '%s'. \
269 Try to upgrade/downgrade to version '%s' with './database_utils/migrate_mano_db.sh'",
270 r
[1], database_version
)
272 except db_base_Exception
as e
:
273 logger
.critical("DATABASE is not a MANO one or it is a '0.0' version. Try to upgrade to version '%s' with \
274 './database_utils/migrate_mano_db.sh'", database_version
)
277 nfvo
.global_config
=global_config
278 nfvo
.start_service(mydb
)
280 httpthread
= httpserver
.httpserver(mydb
, False, global_config
['http_host'], global_config
['http_port'])
283 if 'http_admin_port' in global_config
:
284 httpthreadadmin
= httpserver
.httpserver(mydb
, True, global_config
['http_host'], global_config
['http_admin_port'])
285 httpthreadadmin
.start()
287 logger
.info('Waiting for http clients')
288 print('Waiting for http clients')
289 print('openmanod ready')
290 print('====================')
294 #TODO: Interactive console must be implemented here instead of join or sleep
297 #if 'http_admin_port' in global_config:
298 # httpthreadadmin.join()
302 except KeyboardInterrupt as e
:
306 except getopt
.GetoptError
as e
:
307 logger
.critical(str(e
)) # will print something like "option -a not recognized"
310 except LoadConfigurationException
as e
:
311 logger
.critical(str(e
))
313 except db_base_Exception
as e
:
314 logger
.critical(str(e
))
319 for thread
in global_config
["console_thread"]:
320 thread
.terminate
= True