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.42-r479"
37 version_date
="Jul 2016"
38 database_version
="0.10" #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)")
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"])
152 config_file
= 'openmanod.cfg'
153 vnf_repository
= None
154 log_socket_host
= None
155 log_socket_port
= None
158 if o
in ("-v", "--version"):
159 print ("openmanod version " + __version__
+ ' ' + version_date
)
160 print ("(c) Copyright Telefonica")
162 elif o
in ("-h", "--help"):
165 elif o
in ("-V", "--vnf-repository"):
167 elif o
in ("-c", "--config"):
169 elif o
in ("-p", "--port"):
171 elif o
in ("-P", "--adminport"):
173 elif o
== "--log-socket-port":
175 elif o
== "--log-socket-port":
178 assert False, "Unhandled option"
179 global_config
= load_configuration(config_file
)
181 # Override parameters obtained by command line
183 global_config
['http_port'] = port
185 global_config
['http_admin_port'] = port_admin
187 global_config
['log_socket_host'] = log_socket_host
189 global_config
['log_socket_port'] = log_socket_port
190 # if vnf_repository is not None:
191 # global_config['vnf_repository'] = vnf_repository
193 # if not 'vnf_repository' in global_config:
194 # logger.error( os.getcwd() )
195 # global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
196 # #print global_config
197 # if not os.path.exists(global_config['vnf_repository']):
198 # logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
200 # os.makedirs(global_config['vnf_repository'])
201 # except Exception as e:
202 # logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
205 global_config
["console_port_iterator"] = console_port_iterator
206 global_config
["console_thread"]={}
207 global_config
["console_ports"]={}
209 #Configure logging STEP 2
210 logging
.basicConfig(level
= getattr(logging
, global_config
.get('log_level',"debug")))
211 logger
.setLevel(getattr(logging
, global_config
['log_level']))
212 if "log_host" in global_config
:
213 socket_handler
= log_handlers
.SocketHandler(global_config
["log_socket_host"], global_config
["log_socket_port"])
214 socket_handler
.setFormatter(log_formatter_complete
)
215 if global_config
.get("log_socket_level") and global_config
["log_socket_level"] != global_config
["log_level"]:
216 socket_handler
.setLevel(global_config
["log_socket_level"])
217 logger
.addHandler(socket_handler
)
218 logger
.addHandler(log_handlers
.SysLogHandler())
219 if "log_file" in global_config
:
221 file_handler
= logging
.handlers
.RotatingFileHandler(global_config
["log_file"], maxBytes
=100e6
, backupCount
=9, delay
=0)
222 file_handler
.setFormatter(log_formatter_simple
)
223 logger
.addHandler(file_handler
)
225 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config
["log_file"], str(e
)) )
227 # Initialize DB connection
228 mydb
= nfvo_db
.nfvo_db(log_level
=global_config
["log_level_db"]);
229 if mydb
.connect(global_config
['db_host'], global_config
['db_user'], global_config
['db_passwd'], global_config
['db_name']) == -1:
230 logger
.critical("Cannot connect to database %s at %s@%s", global_config
['db_name'], global_config
['db_user'], global_config
['db_host'])
232 r
= mydb
.get_db_version()
234 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
)
236 elif r
[1]!=database_version
:
237 logger
.critical("DATABASE wrong version '%s'. Try to upgrade/downgrade to version '%s' with './database_utils/migrate_mano_db.sh'", r
[1], database_version
)
240 nfvo
.global_config
=global_config
242 httpthread
= httpserver
.httpserver(mydb
, False, global_config
['http_host'], global_config
['http_port'])
245 if 'http_admin_port' in global_config
:
246 httpthreadadmin
= httpserver
.httpserver(mydb
, True, global_config
['http_host'], global_config
['http_admin_port'])
247 httpthreadadmin
.start()
249 logger
.info('Waiting for http clients')
250 print('openmanod ready')
251 print('====================')
255 #TODO: Interactive console must be implemented here instead of join or sleep
258 #if 'http_admin_port' in global_config:
259 # httpthreadadmin.join()
262 for thread
in global_config
["console_thread"]:
263 thread
.terminate
= True
265 except KeyboardInterrupt as e
:
269 except getopt
.GetoptError
as e
:
270 logger
.critical(str(e
)) # will print something like "option -a not recognized"
273 except LoadConfigurationException
as e
:
274 logger
.critical(str(e
))