v0.4.41 logging added. Split database into a base class db_base with general methods...
[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.41-r478"
37 version_date="Jul 2016"
38 database_version="0.10" #expected database schema version
39
40 import httpserver
41 import time
42 import os
43 import sys
44 import getopt
45 import yaml
46 import nfvo_db
47 from jsonschema import validate as js_v, exceptions as js_e
48 import utils
49 from openmano_schemas import config_schema
50 import nfvo
51 import logging
52 import logging.handlers as log_handlers
53
54 global global_config
55 global logger
56 logger = logging.getLogger('openmano')
57
58 class LoadConfigurationException(Exception):
59 pass
60
61 def load_configuration(configuration_file):
62 default_tokens ={'http_port':9090,
63 'http_host':'localhost',
64 'log_level': 'DEBUG',
65 'log_level_db': 'ERROR',
66 'log_level_vimconn': 'DEBUG',
67 'log_level_nfvo': 'DEBUG',
68 }
69 try:
70 #Check config file exists
71 if not os.path.isfile(configuration_file):
72 raise LoadConfigurationException("Error: Configuration file '"+configuration_file+"' does not exist.")
73
74 #Read file
75 (return_status, code) = utils.read_file(configuration_file)
76 if not return_status:
77 raise LoadConfigurationException("Error loading configuration file '"+configuration_file+"': "+code)
78 #Parse configuration file
79 try:
80 config = yaml.load(code)
81 except yaml.YAMLError, exc:
82 error_pos = ""
83 if hasattr(exc, 'problem_mark'):
84 mark = exc.problem_mark
85 error_pos = " at position: (%s:%s)" % (mark.line+1, mark.column+1)
86 raise LoadConfigurationException("Error loading configuration file '"+configuration_file+"'"+error_pos+": content format error: Failed to parse yaml format")
87
88 #Validate configuration file with the config_schema
89 try:
90 js_v(config, config_schema)
91 except js_e.ValidationError, exc:
92 error_pos = ""
93 if len(exc.path)>0: error_pos=" at '" + ":".join(map(str, exc.path))+"'"
94 raise LoadConfigurationException("Error loading configuration file '"+configuration_file+"'"+error_pos+": "+exc.message)
95
96 #Check default values tokens
97 for k,v in default_tokens.items():
98 if k not in config: config[k]=v
99
100 except Exception,e:
101 raise LoadConfigurationException("Error loading configuration file '"+configuration_file+"': "+str(e))
102
103 return config
104
105 def console_port_iterator():
106 '''this iterator deals with the http_console_ports
107 returning the ports one by one
108 '''
109 index = 0
110 while index < len(global_config["http_console_ports"]):
111 port = global_config["http_console_ports"][index]
112 #print("ports -> ", port)
113 if type(port) is int:
114 yield port
115 else: #this is dictionary with from to keys
116 port2 = port["from"]
117 #print("ports -> ", port, port2)
118 while port2 <= port["to"]:
119 #print("ports -> ", port, port2)
120 yield port2
121 port2 += 1
122 index += 1
123
124
125 def usage():
126 print("Usage: ", sys.argv[0], "[options]")
127 print( " -v|--version: prints current version")
128 print( " -c|--config [configuration_file]: loads the configuration file (default: openmanod.cfg)")
129 print( " -h|--help: shows this help")
130 print( " -p|--port [port_number]: changes port number and overrides the port number in the configuration file (default: 9090)")
131 print( " -P|--adminport [port_number]: changes admin port number and overrides the port number in the configuration file (default: 9095)")
132 print( " -V|--vnf-repository: changes the path of the vnf-repository and overrides the path in the configuration file")
133 return
134
135 if __name__=="__main__":
136 #streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
137 logging_local_format = "%(asctime)s %(name)s %(levelname)s: %(message)s"
138 logging_complete_format = "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
139 logging.basicConfig(format=logging_local_format, level= logging.DEBUG)
140 logger.setLevel(logging.DEBUG)
141 file_handler = None
142 # Read parameters and configuration file
143 try:
144 opts, args = getopt.getopt(sys.argv[1:], "hvc:V:p:P:", ["config", "help", "version", "port", "vnf-repository", "adminport"])
145
146 port=None
147 port_admin = None
148 config_file = 'openmanod.cfg'
149 vnf_repository = None
150
151 for o, a in opts:
152 if o in ("-v", "--version"):
153 print "openmanod version", __version__, version_date
154 print "(c) Copyright Telefonica"
155 sys.exit()
156 elif o in ("-h", "--help"):
157 usage()
158 sys.exit()
159 elif o in ("-V", "--vnf-repository"):
160 vnf_repository = a
161 elif o in ("-c", "--config"):
162 config_file = a
163 elif o in ("-p", "--port"):
164 port = a
165 elif o in ("-P", "--adminport"):
166 port_admin = a
167 else:
168 assert False, "Unhandled option"
169
170 global_config = load_configuration(config_file)
171 #print global_config
172 logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
173 logger.setLevel(getattr(logging, global_config['log_level']))
174 if "log_host" in global_config:
175 socket_handler= log_handlers.SocketHandler(global_config["log_host"], global_config["log_port"])
176 logger.addHandler(socket_handler)
177 logger.addHandler(log_handlers.SysLogHandler())
178 if "log_file" in global_config:
179 try:
180 file_handler= logging.handlers.RotatingFileHandler(global_config["log_file"], maxBytes=100e6, backupCount=9, delay=0)
181 file_handler.setFormatter(logging.Formatter(fmt=logging_complete_format))
182 logger.addHandler(file_handler)
183 except IOError as e:
184 print "Error opening logging file '{}': {}. Check folder exist and permissions".fomat(global_config["log_file"], str(e))
185 # Override parameters obtained by command line
186 print logger.handlers
187 if port is not None: global_config['http_port'] = port
188 if port_admin is not None: global_config['http_admin_port'] = port_admin
189 if vnf_repository is not None:
190 global_config['vnf_repository'] = vnf_repository
191 else:
192 if not 'vnf_repository' in global_config:
193 logger.error( os.getcwd() )
194 global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
195 #print global_config
196
197 if not os.path.exists(global_config['vnf_repository']):
198 logger.error( "Creating folder vnf_repository folder: '%s'.", global_config['vnf_repository'])
199 try:
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)
203 exit(-1)
204
205 global_config["console_port_iterator"] = console_port_iterator
206 global_config["console_thread"]={}
207 global_config["console_ports"]={}
208 # Initialize DB connection
209 mydb = nfvo_db.nfvo_db(log_level=global_config["log_level_db"]);
210 if mydb.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name']) == -1:
211 logger.error("Error connecting to database %s at %s@%s", global_config['db_name'], global_config['db_user'], global_config['db_host'])
212 exit(-1)
213 r = mydb.get_db_version()
214 if r[0]<0:
215 logger.error("Error 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)
216 exit(-1)
217 elif r[1]!=database_version:
218 logger.error("Error DATABASE wrong version '%s'. Try to upgrade/downgrade to version '%s' with './database_utils/migrate_mano_db.sh'", r[1], database_version)
219 exit(-1)
220
221 nfvo.global_config=global_config
222
223 httpthread = httpserver.httpserver(mydb, False, global_config['http_host'], global_config['http_port'])
224
225 httpthread.start()
226 if 'http_admin_port' in global_config:
227 httpthreadadmin = httpserver.httpserver(mydb, True, global_config['http_host'], global_config['http_admin_port'])
228 httpthreadadmin.start()
229 time.sleep(1)
230 logger.info('Waiting for http clients')
231 print('openmanod ready')
232 print('====================')
233 time.sleep(20)
234 sys.stdout.flush()
235
236 #TODO: Interactive console must be implemented here instead of join or sleep
237
238 #httpthread.join()
239 #if 'http_admin_port' in global_config:
240 # httpthreadadmin.join()
241 while True:
242 time.sleep(86400)
243 for thread in global_config["console_thread"]:
244 thread.terminate = True
245
246 except KeyboardInterrupt:
247 logger.info('KyboardInterrupt')
248 except SystemExit:
249 pass
250 except getopt.GetoptError as e:
251 logger.error("Error: %s", str(e)) # will print something like "option -a not recognized"
252 #usage()
253 exit(-1)
254 except LoadConfigurationException as e:
255 logger.error("Error: %s", str(e))
256 exit(-1)
257