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.0-r507"
37 version_date
="Oct 2016"
38 database_version
="0.15" #expected database schema version
46 from jsonschema
import validate
as js_v
, exceptions
as js_e
47 from openmano_schemas
import config_schema
50 import logging
.handlers
as log_handlers
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,
68 #Check config file exists
69 with
open(configuration_file
, 'r') as f
:
71 #Parse configuration file
72 config
= yaml
.load(config_str
)
73 #Validate configuration file with the config_schema
74 js_v(config
, config_schema
)
76 #Add default values tokens
77 for k
,v
in default_tokens
.items():
82 except yaml
.YAMLError
as e
:
84 if hasattr(e
, 'problem_mark'):
86 error_pos
= " at line:{} column:{}".format(mark
.line
+1, mark
.column
+1)
87 raise LoadConfigurationException("Bad YAML format at configuration file '{file}'{pos}".format(file=configuration_file
, pos
=error_pos
) )
88 except js_e
.ValidationError
as e
:
91 error_pos
=" at '" + ":".join(map(str, e
.path
))+"'"
92 raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(file=configuration_file
, pos
=error_pos
, message
=str(e
)) )
93 except Exception as e
:
94 raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(file=configuration_file
, message
=str(e
) ) )
97 def console_port_iterator():
98 '''this iterator deals with the http_console_ports
99 returning the ports one by one
102 while index
< len(global_config
["http_console_ports"]):
103 port
= global_config
["http_console_ports"][index
]
104 #print("ports -> ", port)
105 if type(port
) is int:
107 else: #this is dictionary with from to keys
109 #print("ports -> ", port, port2)
110 while port2
<= port
["to"]:
111 #print("ports -> ", port, port2)
118 print("Usage: ", sys
.argv
[0], "[options]")
119 print( " -v|--version: prints current version")
120 print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)")
121 print( " -h|--help: shows this help")
122 print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)")
123 print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)")
124 #print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file")
125 print( " --log-socket-host HOST: send logs to this host")
126 print( " --log-socket-port PORT: send logs using this port (default: 9022)")
127 print( " --log-file FILE: send logs to this file")
130 if __name__
=="__main__":
131 #Configure logging step 1
132 hostname
= socket
.gethostname()
133 #streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
134 # "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
135 log_formatter_complete
= logging
.Formatter(
136 '%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s severity:%(levelname)s logger:%(name)s log:%(message)s'.format(host
=hostname
),
137 datefmt
='%Y-%m-%dT%H:%M:%S',
139 log_format_simple
= "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
140 log_formatter_simple
= logging
.Formatter(log_format_simple
, datefmt
='%Y-%m-%dT%H:%M:%S')
141 logging
.basicConfig(format
=log_format_simple
, level
= logging
.DEBUG
)
142 logger
= logging
.getLogger('openmano')
143 logger
.setLevel(logging
.DEBUG
)
144 socket_handler
= None
146 # Read parameters and configuration file
148 #load parameters and configuration
149 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="])
152 config_file
= 'openmanod.cfg'
153 vnf_repository
= None
155 log_socket_host
= None
156 log_socket_port
= None
159 if o
in ("-v", "--version"):
160 print ("openmanod version " + __version__
+ ' ' + version_date
)
161 print ("(c) Copyright Telefonica")
163 elif o
in ("-h", "--help"):
166 elif o
in ("-V", "--vnf-repository"):
168 elif o
in ("-c", "--config"):
170 elif o
in ("-p", "--port"):
172 elif o
in ("-P", "--adminport"):
174 elif o
== "--log-socket-port":
176 elif o
== "--log-socket-host":
178 elif o
== "--log-file":
181 assert False, "Unhandled option"
182 global_config
= load_configuration(config_file
)
184 # Override parameters obtained by command line
186 global_config
['http_port'] = port
188 global_config
['http_admin_port'] = port_admin
190 global_config
['log_socket_host'] = log_socket_host
192 global_config
['log_socket_port'] = log_socket_port
194 global_config
['log_file'] = log_file
195 # if vnf_repository is not None:
196 # global_config['vnf_repository'] = vnf_repository
198 # if not 'vnf_repository' in global_config:
199 # logger.error( os.getcwd() )
200 # global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
201 # #print global_config
202 # if not os.path.exists(global_config['vnf_repository']):
203 # logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
205 # os.makedirs(global_config['vnf_repository'])
206 # except Exception as e:
207 # logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
210 global_config
["console_port_iterator"] = console_port_iterator
211 global_config
["console_thread"]={}
212 global_config
["console_ports"]={}
213 if not global_config
["http_console_host"]:
214 global_config
["http_console_host"] = global_config
["http_host"]
215 if global_config
["http_host"]=="0.0.0.0":
216 global_config
["http_console_host"] = socket
.gethostname()
218 #Configure logging STEP 2
219 if "log_host" in global_config
:
220 socket_handler
= log_handlers
.SocketHandler(global_config
["log_socket_host"], global_config
["log_socket_port"])
221 socket_handler
.setFormatter(log_formatter_complete
)
222 if global_config
.get("log_socket_level") and global_config
["log_socket_level"] != global_config
["log_level"]:
223 socket_handler
.setLevel(global_config
["log_socket_level"])
224 logger
.addHandler(socket_handler
)
225 #logger.addHandler(log_handlers.SysLogHandler())
226 if "log_file" in global_config
:
228 file_handler
= logging
.handlers
.RotatingFileHandler(global_config
["log_file"], maxBytes
=100e6
, backupCount
=9, delay
=0)
229 file_handler
.setFormatter(log_formatter_simple
)
230 logger
.addHandler(file_handler
)
231 #logger.debug("moving logs to '%s'", global_config["log_file"])
232 #remove initial stream handler
233 logging
.root
.removeHandler(logging
.root
.handlers
[0])
234 print ("logging on '{}'".format(global_config
["log_file"]))
236 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config
["log_file"], str(e
)) )
237 #logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
238 logger
.setLevel(getattr(logging
, global_config
['log_level']))
239 logger
.critical("Starting openmano server command: '%s'", sys
.argv
[0])
241 for log_module
in ("nfvo", "http", "vim", "db"):
242 log_level_module
= "log_level_" + log_module
243 log_file_module
= "log_file_" + log_module
244 logger_module
= logging
.getLogger('openmano.' + log_module
)
245 if log_level_module
in global_config
:
246 logger_module
.setLevel(global_config
[log_level_module
])
247 if log_file_module
in global_config
:
249 file_handler
= logging
.handlers
.RotatingFileHandler(global_config
[log_file_module
], maxBytes
=100e6
, backupCount
=9, delay
=0)
250 file_handler
.setFormatter(log_formatter_simple
)
251 logger_module
.addHandler(file_handler
)
253 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config
[log_file_module
], str(e
)) )
254 global_config
["logger_"+log_module
] = logger_module
255 #httpserver.logger = global_config["logger_http"]
256 #nfvo.logger = global_config["logger_nfvo"]
258 # Initialize DB connection
259 mydb
= nfvo_db
.nfvo_db();
260 if mydb
.connect(global_config
['db_host'], global_config
['db_user'], global_config
['db_passwd'], global_config
['db_name']) == -1:
261 logger
.critical("Cannot connect to database %s at %s@%s", global_config
['db_name'], global_config
['db_user'], global_config
['db_host'])
263 r
= mydb
.get_db_version()
265 logger
.critical("DATABASE is not a MANO one or it is a '0.0' version. Try to upgrade to version '%s' with './database_utils/migrate_mano_db.sh'", database_version
)
267 elif r
[1]!=database_version
:
268 logger
.critical("DATABASE wrong version '%s'. Try to upgrade/downgrade to version '%s' with './database_utils/migrate_mano_db.sh'", r
[1], database_version
)
271 nfvo
.global_config
=global_config
273 httpthread
= httpserver
.httpserver(mydb
, False, global_config
['http_host'], global_config
['http_port'])
276 if 'http_admin_port' in global_config
:
277 httpthreadadmin
= httpserver
.httpserver(mydb
, True, global_config
['http_host'], global_config
['http_admin_port'])
278 httpthreadadmin
.start()
280 logger
.info('Waiting for http clients')
281 print('Waiting for http clients')
282 print('openmanod ready')
283 print('====================')
287 #TODO: Interactive console must be implemented here instead of join or sleep
290 #if 'http_admin_port' in global_config:
291 # httpthreadadmin.join()
294 for thread
in global_config
["console_thread"]:
295 thread
.terminate
= True
297 except KeyboardInterrupt as e
:
301 except getopt
.GetoptError
as e
:
302 logger
.critical(str(e
)) # will print something like "option -a not recognized"
305 except LoadConfigurationException
as e
:
306 logger
.critical(str(e
))