Restructuring code in osm_ro folder, and setup based on MANIFEST
[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 time
41 import sys
42 import getopt
43 import yaml
44 from jsonschema import validate as js_v, exceptions as js_e
45 import logging
46 import logging.handlers as log_handlers
47 import socket
48 from osm_ro import httpserver, nfvo, nfvo_db
49 from osm_ro.openmano_schemas import config_schema
50 from osm_ro.db_base import db_base_Exception
51
52 global global_config
53 global logger
54
55 class LoadConfigurationException(Exception):
56 pass
57
58 def load_configuration(configuration_file):
59 default_tokens ={'http_port':9090,
60 'http_host':'localhost',
61 'http_console_proxy': True,
62 'http_console_host': None,
63 'log_level': 'DEBUG',
64 'log_socket_port': 9022,
65 'auto_push_VNF_to_VIMs': True
66 }
67 try:
68 #Check config file exists
69 with open(configuration_file, 'r') as f:
70 config_str = f.read()
71 #Parse configuration file
72 config = yaml.load(config_str)
73 #Validate configuration file with the config_schema
74 js_v(config, config_schema)
75
76 #Add default values tokens
77 for k,v in default_tokens.items():
78 if k not in config:
79 config[k]=v
80 return config
81
82 except yaml.YAMLError as e:
83 error_pos = ""
84 if hasattr(e, 'problem_mark'):
85 mark = 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:
89 error_pos = ""
90 if e.path:
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) ) )
95
96
97 def console_port_iterator():
98 '''this iterator deals with the http_console_ports
99 returning the ports one by one
100 '''
101 index = 0
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:
106 yield port
107 else: #this is dictionary with from to keys
108 port2 = port["from"]
109 #print("ports -> ", port, port2)
110 while port2 <= port["to"]:
111 #print("ports -> ", port, port2)
112 yield port2
113 port2 += 1
114 index += 1
115
116
117 def usage():
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")
128 return
129
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',
138 )
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
145 file_handler = None
146 # Read parameters and configuration file
147 httpthread = None
148 try:
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="])
151 port=None
152 port_admin = None
153 config_file = 'openmanod.cfg'
154 vnf_repository = None
155 log_file = None
156 log_socket_host = None
157 log_socket_port = None
158
159 for o, a in opts:
160 if o in ("-v", "--version"):
161 print ("openmanod version " + __version__ + ' ' + version_date)
162 print ("(c) Copyright Telefonica")
163 sys.exit()
164 elif o in ("-h", "--help"):
165 usage()
166 sys.exit()
167 elif o in ("-V", "--vnf-repository"):
168 vnf_repository = a
169 elif o in ("-c", "--config"):
170 config_file = a
171 elif o in ("-p", "--port"):
172 port = a
173 elif o in ("-P", "--adminport"):
174 port_admin = a
175 elif o == "--log-socket-port":
176 log_socket_port = a
177 elif o == "--log-socket-host":
178 log_socket_host = a
179 elif o == "--log-file":
180 log_file = a
181 else:
182 assert False, "Unhandled option"
183 global_config = load_configuration(config_file)
184 #print global_config
185 # Override parameters obtained by command line
186 if port:
187 global_config['http_port'] = port
188 if port_admin:
189 global_config['http_admin_port'] = port_admin
190 if log_socket_host:
191 global_config['log_socket_host'] = log_socket_host
192 if log_socket_port:
193 global_config['log_socket_port'] = log_socket_port
194 if log_file:
195 global_config['log_file'] = log_file
196 # if vnf_repository is not None:
197 # global_config['vnf_repository'] = vnf_repository
198 # else:
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'])
205 # try:
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)
209 # exit(-1)
210
211 global_config["console_port_iterator"] = console_port_iterator
212 global_config["console_thread"]={}
213 global_config["console_ports"]={}
214 if not global_config["http_console_host"]:
215 global_config["http_console_host"] = global_config["http_host"]
216 if global_config["http_host"]=="0.0.0.0":
217 global_config["http_console_host"] = socket.gethostname()
218
219 #Configure logging STEP 2
220 if "log_host" in global_config:
221 socket_handler= log_handlers.SocketHandler(global_config["log_socket_host"], global_config["log_socket_port"])
222 socket_handler.setFormatter(log_formatter_complete)
223 if global_config.get("log_socket_level") and global_config["log_socket_level"] != global_config["log_level"]:
224 socket_handler.setLevel(global_config["log_socket_level"])
225 logger.addHandler(socket_handler)
226 #logger.addHandler(log_handlers.SysLogHandler())
227 if "log_file" in global_config:
228 try:
229 file_handler= logging.handlers.RotatingFileHandler(global_config["log_file"], maxBytes=100e6, backupCount=9, delay=0)
230 file_handler.setFormatter(log_formatter_simple)
231 logger.addHandler(file_handler)
232 #logger.debug("moving logs to '%s'", global_config["log_file"])
233 #remove initial stream handler
234 logging.root.removeHandler(logging.root.handlers[0])
235 print ("logging on '{}'".format(global_config["log_file"]))
236 except IOError as e:
237 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config["log_file"], str(e)) )
238 #logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
239 logger.setLevel(getattr(logging, global_config['log_level']))
240 logger.critical("Starting openmano server version: '%s %s' command: '%s'",
241 __version__, version_date, " ".join(sys.argv))
242
243 for log_module in ("nfvo", "http", "vim", "db", "console"):
244 log_level_module = "log_level_" + log_module
245 log_file_module = "log_file_" + log_module
246 logger_module = logging.getLogger('openmano.' + log_module)
247 if log_level_module in global_config:
248 logger_module.setLevel(global_config[log_level_module])
249 if log_file_module in global_config:
250 try:
251 file_handler= logging.handlers.RotatingFileHandler(global_config[log_file_module], maxBytes=100e6, backupCount=9, delay=0)
252 file_handler.setFormatter(log_formatter_simple)
253 logger_module.addHandler(file_handler)
254 except IOError as e:
255 raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config[log_file_module], str(e)) )
256 global_config["logger_"+log_module] = logger_module
257 #httpserver.logger = global_config["logger_http"]
258 #nfvo.logger = global_config["logger_nfvo"]
259
260 # Initialize DB connection
261 mydb = nfvo_db.nfvo_db();
262 mydb.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name'])
263 try:
264 r = mydb.get_db_version()
265 if r[1] != database_version:
266 logger.critical("DATABASE wrong version '%s'. \
267 Try to upgrade/downgrade to version '%s' with './database_utils/migrate_mano_db.sh'",
268 r[1], database_version)
269 exit(-1)
270 except db_base_Exception as e:
271 logger.critical("DATABASE is not a MANO one or it is a '0.0' version. Try to upgrade to version '%s' with \
272 './database_utils/migrate_mano_db.sh'", database_version)
273 exit(-1)
274
275 nfvo.global_config=global_config
276 nfvo.start_service(mydb)
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
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 except db_base_Exception as e:
312 logger.critical(str(e))
313 exit(-1)
314 nfvo.stop_service()
315 if httpthread:
316 httpthread.join(1)
317 for thread in global_config["console_thread"]:
318 thread.terminate = True
319