Proper error message shown when no image could be found at VIM
[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.5.8-r518"
37 version_date="Jan 2017"
38 database_version="0.19" #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 from db_base import db_base_Exception
49 import nfvo
50 import logging
51 import logging.handlers as log_handlers
52 import socket
53
54 global global_config
55 global logger
56
57 class LoadConfigurationException(Exception):
58 pass
59
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,
65 'log_level': 'DEBUG',
66 'log_socket_port': 9022,
67 'auto_push_VNF_to_VIMs': True
68 }
69 try:
70 #Check config file exists
71 with open(configuration_file, 'r') as f:
72 config_str = f.read()
73 #Parse configuration file
74 config = yaml.load(config_str)
75 #Validate configuration file with the config_schema
76 js_v(config, config_schema)
77
78 #Add default values tokens
79 for k,v in default_tokens.items():
80 if k not in config:
81 config[k]=v
82 return config
83
84 except yaml.YAMLError as e:
85 error_pos = ""
86 if hasattr(e, 'problem_mark'):
87 mark = 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:
91 error_pos = ""
92 if e.path:
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) ) )
97
98
99 def console_port_iterator():
100 '''this iterator deals with the http_console_ports
101 returning the ports one by one
102 '''
103 index = 0
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:
108 yield port
109 else: #this is dictionary with from to keys
110 port2 = port["from"]
111 #print("ports -> ", port, port2)
112 while port2 <= port["to"]:
113 #print("ports -> ", port, port2)
114 yield port2
115 port2 += 1
116 index += 1
117
118
119 def usage():
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")
130 return
131
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',
140 )
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
147 file_handler = None
148 # Read parameters and configuration file
149 try:
150 #load parameters and configuration
151 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 port=None
153 port_admin = None
154 config_file = 'openmanod.cfg'
155 vnf_repository = None
156 log_file = None
157 log_socket_host = None
158 log_socket_port = None
159
160 for o, a in opts:
161 if o in ("-v", "--version"):
162 print ("openmanod version " + __version__ + ' ' + version_date)
163 print ("(c) Copyright Telefonica")
164 sys.exit()
165 elif o in ("-h", "--help"):
166 usage()
167 sys.exit()
168 elif o in ("-V", "--vnf-repository"):
169 vnf_repository = a
170 elif o in ("-c", "--config"):
171 config_file = a
172 elif o in ("-p", "--port"):
173 port = a
174 elif o in ("-P", "--adminport"):
175 port_admin = a
176 elif o == "--log-socket-port":
177 log_socket_port = a
178 elif o == "--log-socket-host":
179 log_socket_host = a
180 elif o == "--log-file":
181 log_file = a
182 else:
183 assert False, "Unhandled option"
184 global_config = load_configuration(config_file)
185 #print global_config
186 # Override parameters obtained by command line
187 if port:
188 global_config['http_port'] = port
189 if port_admin:
190 global_config['http_admin_port'] = port_admin
191 if log_socket_host:
192 global_config['log_socket_host'] = log_socket_host
193 if log_socket_port:
194 global_config['log_socket_port'] = log_socket_port
195 if log_file:
196 global_config['log_file'] = log_file
197 # if vnf_repository is not None:
198 # global_config['vnf_repository'] = vnf_repository
199 # else:
200 # if not 'vnf_repository' in global_config:
201 # logger.error( os.getcwd() )
202 # global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
203 # #print global_config
204 # if not os.path.exists(global_config['vnf_repository']):
205 # logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
206 # try:
207 # os.makedirs(global_config['vnf_repository'])
208 # except Exception as e:
209 # logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
210 # exit(-1)
211
212 global_config["console_port_iterator"] = console_port_iterator
213 global_config["console_thread"]={}
214 global_config["console_ports"]={}
215 if not global_config["http_console_host"]:
216 global_config["http_console_host"] = global_config["http_host"]
217 if global_config["http_host"]=="0.0.0.0":
218 global_config["http_console_host"] = socket.gethostname()
219
220 #Configure logging STEP 2
221 if "log_host" in global_config:
222 socket_handler= log_handlers.SocketHandler(global_config["log_socket_host"], global_config["log_socket_port"])
223 socket_handler.setFormatter(log_formatter_complete)
224 if global_config.get("log_socket_level") and global_config["log_socket_level"] != global_config["log_level"]:
225 socket_handler.setLevel(global_config["log_socket_level"])
226 logger.addHandler(socket_handler)
227 #logger.addHandler(log_handlers.SysLogHandler())
228 if "log_file" in global_config:
229 try:
230 file_handler= logging.handlers.RotatingFileHandler(global_config["log_file"], maxBytes=100e6, backupCount=9, delay=0)
231 file_handler.setFormatter(log_formatter_simple)
232 logger.addHandler(file_handler)
233 #logger.debug("moving logs to '%s'", global_config["log_file"])
234 #remove initial stream handler
235 logging.root.removeHandler(logging.root.handlers[0])
236 print ("logging on '{}'".format(global_config["log_file"]))
237 except IOError as e:
238 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config["log_file"], str(e)) )
239 #logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
240 logger.setLevel(getattr(logging, global_config['log_level']))
241 logger.critical("Starting openmano server version: '%s %s' command: '%s'",
242 __version__, version_date, " ".join(sys.argv))
243
244 for log_module in ("nfvo", "http", "vim", "db", "console"):
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();
263 mydb.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name'])
264 try:
265 r = mydb.get_db_version()
266 if r[1] != database_version:
267 logger.critical("DATABASE wrong version '%s'. \
268 Try to upgrade/downgrade to version '%s' with './database_utils/migrate_mano_db.sh'",
269 r[1], database_version)
270 exit(-1)
271 except db_base_Exception as e:
272 logger.critical("DATABASE is not a MANO one or it is a '0.0' version. Try to upgrade to version '%s' with \
273 './database_utils/migrate_mano_db.sh'", database_version)
274 exit(-1)
275
276 nfvo.global_config=global_config
277
278 httpthread = httpserver.httpserver(mydb, False, global_config['http_host'], global_config['http_port'])
279
280 httpthread.start()
281 if 'http_admin_port' in global_config:
282 httpthreadadmin = httpserver.httpserver(mydb, True, global_config['http_host'], global_config['http_admin_port'])
283 httpthreadadmin.start()
284 time.sleep(1)
285 logger.info('Waiting for http clients')
286 print('Waiting for http clients')
287 print('openmanod ready')
288 print('====================')
289 time.sleep(20)
290 sys.stdout.flush()
291
292 #TODO: Interactive console must be implemented here instead of join or sleep
293
294 #httpthread.join()
295 #if 'http_admin_port' in global_config:
296 # httpthreadadmin.join()
297 while True:
298 time.sleep(86400)
299 for thread in global_config["console_thread"]:
300 thread.terminate = True
301
302 except KeyboardInterrupt as e:
303 logger.info(str(e))
304 except SystemExit:
305 pass
306 except getopt.GetoptError as e:
307 logger.critical(str(e)) # will print something like "option -a not recognized"
308 #usage()
309 exit(-1)
310 except LoadConfigurationException as e:
311 logger.critical(str(e))
312 exit(-1)
313 except db_base_Exception as e:
314 logger.critical(str(e))
315 exit(-1)
316