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.4.44-r482"
37 version_date
="Jul 2016"
38 database_version
="0.11" #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',
63 'log_level_db': 'ERROR',
64 'log_level_vimconn': 'DEBUG',
65 'log_level_nfvo': 'DEBUG',
66 'log_socket_port': 9022,
69 #Check config file exists
70 with
open(configuration_file
, 'r') as f
:
72 #Parse configuration file
73 config
= yaml
.load(config_str
)
74 #Validate configuration file with the config_schema
75 js_v(config
, config_schema
)
77 #Add default values tokens
78 for k
,v
in default_tokens
.items():
83 except yaml
.YAMLError
as e
:
85 if hasattr(e
, 'problem_mark'):
87 error_pos
= " at line:{} column:{}".format(mark
.line
+1, mark
.column
+1)
88 raise LoadConfigurationException("Bad YAML format at configuration file '{file}'{pos}".format(file=configuration_file
, pos
=error_pos
) )
89 except js_e
.ValidationError
as e
:
92 error_pos
=" at '" + ":".join(map(str, e
.path
))+"'"
93 raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(file=configuration_file
, pos
=error_pos
, message
=str(e
)) )
94 except Exception as e
:
95 raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(file=configuration_file
, message
=str(e
) ) )
98 def console_port_iterator():
99 '''this iterator deals with the http_console_ports
100 returning the ports one by one
103 while index
< len(global_config
["http_console_ports"]):
104 port
= global_config
["http_console_ports"][index
]
105 #print("ports -> ", port)
106 if type(port
) is int:
108 else: #this is dictionary with from to keys
110 #print("ports -> ", port, port2)
111 while port2
<= port
["to"]:
112 #print("ports -> ", port, port2)
119 print("Usage: ", sys
.argv
[0], "[options]")
120 print( " -v|--version: prints current version")
121 print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)")
122 print( " -h|--help: shows this help")
123 print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)")
124 print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)")
125 #print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file")
126 print( " --log-socket-host: send logs to this host")
127 print( " --log-socket-port: send logs using this port (default: 9022)")
128 print( " --log-file: send logs to this file")
131 if __name__
=="__main__":
132 #Configure logging step 1
133 hostname
= socket
.gethostname()
134 #streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
135 # "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
136 log_formatter_complete
= logging
.Formatter(
137 '%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s severity:%(levelname)s logger:%(name)s log:%(message)s'.format(host
=hostname
),
138 datefmt
='%Y-%m-%dT%H:%M:%S',
140 log_format_simple
= "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
141 log_formatter_simple
= logging
.Formatter(log_format_simple
, datefmt
='%Y-%m-%dT%H:%M:%S')
142 logging
.basicConfig(format
=log_format_simple
, level
= logging
.DEBUG
)
143 logger
= logging
.getLogger('openmano')
144 logger
.setLevel(logging
.DEBUG
)
145 socket_handler
= None
147 # Read parameters and configuration file
149 #load parameters and configuration
150 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="])
153 config_file
= 'openmanod.cfg'
154 vnf_repository
= None
156 log_socket_host
= None
157 log_socket_port
= None
160 if o
in ("-v", "--version"):
161 print ("openmanod version " + __version__
+ ' ' + version_date
)
162 print ("(c) Copyright Telefonica")
164 elif o
in ("-h", "--help"):
167 elif o
in ("-V", "--vnf-repository"):
169 elif o
in ("-c", "--config"):
171 elif o
in ("-p", "--port"):
173 elif o
in ("-P", "--adminport"):
175 elif o
== "--log-socket-port":
177 elif o
== "--log-socket-port":
179 elif o
== "--log-file":
182 assert False, "Unhandled option"
183 global_config
= load_configuration(config_file
)
185 # Override parameters obtained by command line
187 global_config
['http_port'] = port
189 global_config
['http_admin_port'] = port_admin
191 global_config
['log_socket_host'] = log_socket_host
193 global_config
['log_socket_port'] = log_socket_port
195 global_config
['log_file'] = log_file
196 # if vnf_repository is not None:
197 # global_config['vnf_repository'] = vnf_repository
199 # if not 'vnf_repository' in global_config:
200 # logger.error( os.getcwd() )
201 # global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
202 # #print global_config
203 # if not os.path.exists(global_config['vnf_repository']):
204 # logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
206 # os.makedirs(global_config['vnf_repository'])
207 # except Exception as e:
208 # logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
211 global_config
["console_port_iterator"] = console_port_iterator
212 global_config
["console_thread"]={}
213 global_config
["console_ports"]={}
215 #Configure logging STEP 2
216 if "log_host" in global_config
:
217 socket_handler
= log_handlers
.SocketHandler(global_config
["log_socket_host"], global_config
["log_socket_port"])
218 socket_handler
.setFormatter(log_formatter_complete
)
219 if global_config
.get("log_socket_level") and global_config
["log_socket_level"] != global_config
["log_level"]:
220 socket_handler
.setLevel(global_config
["log_socket_level"])
221 logger
.addHandler(socket_handler
)
222 #logger.addHandler(log_handlers.SysLogHandler())
223 if "log_file" in global_config
:
225 file_handler
= logging
.handlers
.RotatingFileHandler(global_config
["log_file"], maxBytes
=100e6
, backupCount
=9, delay
=0)
226 file_handler
.setFormatter(log_formatter_simple
)
227 logger
.addHandler(file_handler
)
228 logger
.debug("moving logs to '%s'", global_config
["log_file"])
229 #remove initial strema handler
230 logging
.root
.removeHandler(logging
.root
.handlers
[0])
232 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config
["log_file"], str(e
)) )
233 #logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
234 logger
.setLevel(getattr(logging
, global_config
['log_level']))
236 # Initialize DB connection
237 mydb
= nfvo_db
.nfvo_db(log_level
=global_config
["log_level_db"]);
238 if mydb
.connect(global_config
['db_host'], global_config
['db_user'], global_config
['db_passwd'], global_config
['db_name']) == -1:
239 logger
.critical("Cannot connect to database %s at %s@%s", global_config
['db_name'], global_config
['db_user'], global_config
['db_host'])
241 r
= mydb
.get_db_version()
243 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
)
245 elif r
[1]!=database_version
:
246 logger
.critical("DATABASE wrong version '%s'. Try to upgrade/downgrade to version '%s' with './database_utils/migrate_mano_db.sh'", r
[1], database_version
)
249 nfvo
.global_config
=global_config
251 httpthread
= httpserver
.httpserver(mydb
, False, global_config
['http_host'], global_config
['http_port'])
254 if 'http_admin_port' in global_config
:
255 httpthreadadmin
= httpserver
.httpserver(mydb
, True, global_config
['http_host'], global_config
['http_admin_port'])
256 httpthreadadmin
.start()
258 logger
.info('Waiting for http clients')
259 print('Waiting for http clients')
260 print('openmanod ready')
261 print('====================')
265 #TODO: Interactive console must be implemented here instead of join or sleep
268 #if 'http_admin_port' in global_config:
269 # httpthreadadmin.join()
272 for thread
in global_config
["console_thread"]:
273 thread
.terminate
= True
275 except KeyboardInterrupt as e
:
279 except getopt
.GetoptError
as e
:
280 logger
.critical(str(e
)) # will print something like "option -a not recognized"
283 except LoadConfigurationException
as e
:
284 logger
.critical(str(e
))