Bugs fixed in scripts related to database init and migrate
[osm/RO.git] / openmanod.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 ##
5 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
6 # This file is part of openmano
7 # All Rights Reserved.
8 #
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
12 #
13 # http://www.apache.org/licenses/LICENSE-2.0
14 #
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
19 # under the License.
20 #
21 # For those usages not covered by the Apache License, Version 2.0 please
22 # contact with: nfvlabs@tid.es
23 ##
24
25 '''
26 openmano server.
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.
31
32 It loads the configuration file and launches the http_server thread that will listen requests using openmano API.
33 '''
34 __author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes"
35 __date__ ="$26-aug-2014 11:09:29$"
36 __version__="0.4.54-r496"
37 version_date="Sep 2016"
38 database_version="0.13" #expected database schema version
39
40 import httpserver
41 import time
42 import sys
43 import getopt
44 import yaml
45 import nfvo_db
46 from jsonschema import validate as js_v, exceptions as js_e
47 from openmano_schemas import config_schema
48 import nfvo
49 import logging
50 import logging.handlers as log_handlers
51 import socket
52
53 global global_config
54 global logger
55
56 class LoadConfigurationException(Exception):
57 pass
58
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,
64 'log_level': 'DEBUG',
65 'log_level_db': 'ERROR',
66 'log_level_vimconn': 'DEBUG',
67 'log_level_nfvo': 'DEBUG',
68 'log_socket_port': 9022,
69 }
70 try:
71 #Check config file exists
72 with open(configuration_file, 'r') as f:
73 config_str = f.read()
74 #Parse configuration file
75 config = yaml.load(config_str)
76 #Validate configuration file with the config_schema
77 js_v(config, config_schema)
78
79 #Add default values tokens
80 for k,v in default_tokens.items():
81 if k not in config:
82 config[k]=v
83 return config
84
85 except yaml.YAMLError as e:
86 error_pos = ""
87 if hasattr(e, 'problem_mark'):
88 mark = e.problem_mark
89 error_pos = " at line:{} column:{}".format(mark.line+1, mark.column+1)
90 raise LoadConfigurationException("Bad YAML format at configuration file '{file}'{pos}".format(file=configuration_file, pos=error_pos) )
91 except js_e.ValidationError as e:
92 error_pos = ""
93 if e.path:
94 error_pos=" at '" + ":".join(map(str, e.path))+"'"
95 raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(file=configuration_file, pos=error_pos, message=str(e)) )
96 except Exception as e:
97 raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(file=configuration_file, message=str(e) ) )
98
99
100 def console_port_iterator():
101 '''this iterator deals with the http_console_ports
102 returning the ports one by one
103 '''
104 index = 0
105 while index < len(global_config["http_console_ports"]):
106 port = global_config["http_console_ports"][index]
107 #print("ports -> ", port)
108 if type(port) is int:
109 yield port
110 else: #this is dictionary with from to keys
111 port2 = port["from"]
112 #print("ports -> ", port, port2)
113 while port2 <= port["to"]:
114 #print("ports -> ", port, port2)
115 yield port2
116 port2 += 1
117 index += 1
118
119
120 def usage():
121 print("Usage: ", sys.argv[0], "[options]")
122 print( " -v|--version: prints current version")
123 print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)")
124 print( " -h|--help: shows this help")
125 print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)")
126 print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)")
127 #print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file")
128 print( " --log-socket-host HOST: send logs to this host")
129 print( " --log-socket-port PORT: send logs using this port (default: 9022)")
130 print( " --log-file FILE: send logs to this file")
131 return
132
133 if __name__=="__main__":
134 #Configure logging step 1
135 hostname = socket.gethostname()
136 #streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
137 # "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
138 log_formatter_complete = logging.Formatter(
139 '%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s severity:%(levelname)s logger:%(name)s log:%(message)s'.format(host=hostname),
140 datefmt='%Y-%m-%dT%H:%M:%S',
141 )
142 log_format_simple = "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(message)s"
143 log_formatter_simple = logging.Formatter(log_format_simple, datefmt='%Y-%m-%dT%H:%M:%S')
144 logging.basicConfig(format=log_format_simple, level= logging.DEBUG)
145 logger = logging.getLogger('openmano')
146 logger.setLevel(logging.DEBUG)
147 socket_handler = None
148 file_handler = None
149 # Read parameters and configuration file
150 try:
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="])
153 port=None
154 port_admin = None
155 config_file = 'openmanod.cfg'
156 vnf_repository = None
157 log_file = None
158 log_socket_host = None
159 log_socket_port = None
160
161 for o, a in opts:
162 if o in ("-v", "--version"):
163 print ("openmanod version " + __version__ + ' ' + version_date)
164 print ("(c) Copyright Telefonica")
165 sys.exit()
166 elif o in ("-h", "--help"):
167 usage()
168 sys.exit()
169 elif o in ("-V", "--vnf-repository"):
170 vnf_repository = a
171 elif o in ("-c", "--config"):
172 config_file = a
173 elif o in ("-p", "--port"):
174 port = a
175 elif o in ("-P", "--adminport"):
176 port_admin = a
177 elif o == "--log-socket-port":
178 log_socket_port = a
179 elif o == "--log-socket-port":
180 log_socket_host = a
181 elif o == "--log-file":
182 log_file = a
183 else:
184 assert False, "Unhandled option"
185 global_config = load_configuration(config_file)
186 #print global_config
187 # Override parameters obtained by command line
188 if port:
189 global_config['http_port'] = port
190 if port_admin:
191 global_config['http_admin_port'] = port_admin
192 if log_socket_host:
193 global_config['log_socket_host'] = log_socket_host
194 if log_socket_port:
195 global_config['log_socket_port'] = log_socket_port
196 if log_file:
197 global_config['log_file'] = log_file
198 # if vnf_repository is not None:
199 # global_config['vnf_repository'] = vnf_repository
200 # else:
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'])
207 # try:
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)
211 # exit(-1)
212
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()
220
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:
230 try:
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"]))
238 except IOError as e:
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 command: '%s'", sys.argv[0])
243
244 for log_module in ("nfvo", "http", "vim", "db"):
245 log_level_module = "log_level_" + log_module
246 log_file_module = "log_file_" + log_module
247 logger_module = logging.getLogger('openmano.' + log_module)
248 if log_level_module in global_config:
249 logger_module.setLevel(global_config[log_level_module])
250 if log_file_module in global_config:
251 try:
252 file_handler= logging.handlers.RotatingFileHandler(global_config[log_file_module], maxBytes=100e6, backupCount=9, delay=0)
253 file_handler.setFormatter(log_formatter_simple)
254 logger_module.addHandler(file_handler)
255 except IOError as e:
256 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config[log_file_module], str(e)) )
257 global_config["logger_"+log_module] = logger_module
258 #httpserver.logger = global_config["logger_http"]
259 #nfvo.logger = global_config["logger_nfvo"]
260
261 # Initialize DB connection
262 mydb = nfvo_db.nfvo_db(log_level=global_config["log_level_db"]);
263 if mydb.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name']) == -1:
264 logger.critical("Cannot connect to database %s at %s@%s", global_config['db_name'], global_config['db_user'], global_config['db_host'])
265 exit(-1)
266 r = mydb.get_db_version()
267 if r[0]<0:
268 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)
269 exit(-1)
270 elif r[1]!=database_version:
271 logger.critical("DATABASE wrong version '%s'. Try to upgrade/downgrade to version '%s' with './database_utils/migrate_mano_db.sh'", r[1], database_version)
272 exit(-1)
273
274 nfvo.global_config=global_config
275
276 httpthread = httpserver.httpserver(mydb, False, global_config['http_host'], global_config['http_port'])
277
278 httpthread.start()
279 if 'http_admin_port' in global_config:
280 httpthreadadmin = httpserver.httpserver(mydb, True, global_config['http_host'], global_config['http_admin_port'])
281 httpthreadadmin.start()
282 time.sleep(1)
283 logger.info('Waiting for http clients')
284 print('Waiting for http clients')
285 print('openmanod ready')
286 print('====================')
287 time.sleep(20)
288 sys.stdout.flush()
289
290 #TODO: Interactive console must be implemented here instead of join or sleep
291
292 #httpthread.join()
293 #if 'http_admin_port' in global_config:
294 # httpthreadadmin.join()
295 while True:
296 time.sleep(86400)
297 for thread in global_config["console_thread"]:
298 thread.terminate = True
299
300 except KeyboardInterrupt as e:
301 logger.info(str(e))
302 except SystemExit:
303 pass
304 except getopt.GetoptError as e:
305 logger.critical(str(e)) # will print something like "option -a not recognized"
306 #usage()
307 exit(-1)
308 except LoadConfigurationException as e:
309 logger.critical(str(e))
310 exit(-1)
311