Bugs fixed in scripts related to database init and migrate
[osm/RO.git] / db_base.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
6 # All Rights Reserved.
7 #
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
11 #
12 # http://www.apache.org/licenses/LICENSE-2.0
13 #
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
18 # under the License.
19 #
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
22 ##
23
24 '''
25 Base class for openmano database manipulation
26 '''
27 __author__="Alfonso Tierno"
28 __date__ ="$4-Apr-2016 10:05:01$"
29
30 import MySQLdb as mdb
31 import uuid as myUuid
32 import utils as af
33 import json
34 #import yaml
35 import time
36 import logging
37 import datetime
38 from jsonschema import validate as js_v, exceptions as js_e
39
40 HTTP_Bad_Request = 400
41 HTTP_Unauthorized = 401
42 HTTP_Not_Found = 404
43 HTTP_Method_Not_Allowed = 405
44 HTTP_Request_Timeout = 408
45 HTTP_Conflict = 409
46 HTTP_Service_Unavailable = 503
47 HTTP_Internal_Server_Error = 500
48
49 def _check_valid_uuid(uuid):
50 id_schema = {"type" : "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
51 id_schema2 = {"type" : "string", "pattern": "^[a-fA-F0-9]{32}$"}
52 try:
53 js_v(uuid, id_schema)
54 return True
55 except js_e.ValidationError:
56 try:
57 js_v(uuid, id_schema2)
58 return True
59 except js_e.ValidationError:
60 return False
61 return False
62
63 def _convert_datetime2str(var):
64 '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
65 It enters recursively in the dict var finding this kind of variables
66 '''
67 if type(var) is dict:
68 for k,v in var.items():
69 if type(v) is datetime.datetime:
70 var[k]= v.strftime('%Y-%m-%dT%H:%M:%S')
71 elif type(v) is dict or type(v) is list or type(v) is tuple:
72 _convert_datetime2str(v)
73 if len(var) == 0: return True
74 elif type(var) is list or type(var) is tuple:
75 for v in var:
76 _convert_datetime2str(v)
77
78 def _convert_bandwidth(data, reverse=False):
79 '''Check the field bandwidth recursivelly and when found, it removes units and convert to number
80 It assumes that bandwidth is well formed
81 Attributes:
82 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
83 'reverse': by default convert form str to int (Mbps), if True it convert from number to units
84 Return:
85 None
86 '''
87 if type(data) is dict:
88 for k in data.keys():
89 if type(data[k]) is dict or type(data[k]) is tuple or type(data[k]) is list:
90 _convert_bandwidth(data[k], reverse)
91 if "bandwidth" in data:
92 try:
93 value=str(data["bandwidth"])
94 if not reverse:
95 pos = value.find("bps")
96 if pos>0:
97 if value[pos-1]=="G": data["bandwidth"] = int(data["bandwidth"][:pos-1]) * 1000
98 elif value[pos-1]=="k": data["bandwidth"]= int(data["bandwidth"][:pos-1]) / 1000
99 else: data["bandwidth"]= int(data["bandwidth"][:pos-1])
100 else:
101 value = int(data["bandwidth"])
102 if value % 1000 == 0: data["bandwidth"]=str(value/1000) + " Gbps"
103 else: data["bandwidth"]=str(value) + " Mbps"
104 except:
105 print "convert_bandwidth exception for type", type(data["bandwidth"]), " data", data["bandwidth"]
106 return
107 if type(data) is tuple or type(data) is list:
108 for k in data:
109 if type(k) is dict or type(k) is tuple or type(k) is list:
110 _convert_bandwidth(k, reverse)
111
112 def _convert_str2boolean(data, items):
113 '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
114 Done recursively
115 Attributes:
116 'data': dictionary variable to be checked. None or empty is considered valid
117 'items': tuple of keys to convert
118 Return:
119 None
120 '''
121 if type(data) is dict:
122 for k in data.keys():
123 if type(data[k]) is dict or type(data[k]) is tuple or type(data[k]) is list:
124 _convert_str2boolean(data[k], items)
125 if k in items:
126 if type(data[k]) is str:
127 if data[k]=="false" or data[k]=="False": data[k]=False
128 elif data[k]=="true" or data[k]=="True": data[k]=True
129 if type(data) is tuple or type(data) is list:
130 for k in data:
131 if type(k) is dict or type(k) is tuple or type(k) is list:
132 _convert_str2boolean(k, items)
133
134 class db_base_Exception(Exception):
135 '''Common Exception for all database exceptions'''
136
137 def __init__(self, message, http_code=HTTP_Bad_Request):
138 Exception.__init__(self, message)
139 self.http_code = http_code
140
141 class db_base():
142 tables_with_created_field=()
143
144 def __init__(self, host=None, user=None, passwd=None, database=None, log_name='db', log_level="ERROR"):
145 self.host = host
146 self.user = user
147 self.passwd = passwd
148 self.database = database
149 self.con = None
150 self.log_level=log_level
151 self.logger = logging.getLogger(log_name)
152 self.logger.setLevel( getattr(logging, log_level) )
153
154 def connect(self, host=None, user=None, passwd=None, database=None):
155 '''Connect to specific data base.
156 The first time a valid host, user, passwd and database must be provided,
157 Following calls can skip this parameters
158 '''
159 try:
160 if host: self.host = host
161 if user: self.user = user
162 if passwd: self.passwd = passwd
163 if database: self.database = database
164
165 self.con = mdb.connect(self.host, self.user, self.passwd, self.database)
166 print "DB: connected to %s@%s -> %s" % (self.user, self.host, self.database)
167 except mdb.Error, e:
168 raise db_base_Exception("Cannot connect to DB {}@{} -> {} Error {}: {}".format(self.user, self.host, self.database, e.args[0], e.args[1]),
169 code = HTTP_Internal_Server_Error )
170
171 def get_db_version(self):
172 ''' Obtain the database schema version.
173 Return: (negative, text) if error or version 0.0 where schema_version table is missing
174 (version_int, version_text) if ok
175 '''
176 cmd = "SELECT version_int,version FROM schema_version"
177 tries = 2
178 while tries:
179 try:
180 with self.con:
181 self.cur = self.con.cursor()
182 self.logger.debug(cmd)
183 self.cur.execute(cmd)
184 rows = self.cur.fetchall()
185 highest_version_int=0
186 highest_version=""
187 for row in rows: #look for the latest version
188 if row[0]>highest_version_int:
189 highest_version_int, highest_version = row[0:2]
190 return highest_version_int, highest_version
191 except (mdb.Error, AttributeError) as e:
192 #self.logger.error("get_db_version DB Exception %d: %s. Command %s",e.args[0], e.args[1], cmd)
193 self._format_error(e, tries)
194 tries -= 1
195
196 def disconnect(self):
197 '''disconnect from specific data base'''
198 try:
199 self.con.close()
200 self.con = None
201 except mdb.Error as e:
202 self.logger.error("while disconnecting from DB: Error %d: %s",e.args[0], e.args[1])
203 return
204 except AttributeError as e: #self.con not defined
205 if e[0][-5:] == "'con'":
206 self.logger.warn("while disconnecting from DB: Error %d: %s",e.args[0], e.args[1])
207 return
208 else:
209 raise
210
211 def _format_error(self, e, tries=1, command=None, extra=None):
212 '''Creates a text error base on the produced exception
213 Params:
214 e: mdb exception
215 retry: in case of timeout, if reconnecting to database and retry, or raise and exception
216 cmd: database command that produce the exception
217 command: if the intention is update or delete
218 extra: extra information to add to some commands
219 Return
220 HTTP error in negative, formatted error text
221 '''
222 if isinstance(e,AttributeError ):
223 raise db_base_Exception("DB Exception " + str(e), HTTP_Internal_Server_Error)
224 if e.args[0]==2006 or e.args[0]==2013 : #MySQL server has gone away (((or))) Exception 2013: Lost connection to MySQL server during query
225 if tries>1:
226 self.logger.warn("DB Exception '%s'. Retry", str(e))
227 #reconnect
228 self.connect()
229 return
230 else:
231 raise db_base_Exception("Database connection timeout Try Again", HTTP_Request_Timeout)
232
233 fk=e.args[1].find("foreign key constraint fails")
234 if fk>=0:
235 if command=="update":
236 raise db_base_Exception("tenant_id '{}' not found.".format(extra), HTTP_Not_Found)
237 elif command=="delete":
238 raise db_base_Exception("Resource is not free. There are {} that prevent deleting it.".format(extra), HTTP_Conflict)
239 de = e.args[1].find("Duplicate entry")
240 fk = e.args[1].find("for key")
241 uk = e.args[1].find("Unknown column")
242 wc = e.args[1].find("in 'where clause'")
243 fl = e.args[1].find("in 'field list'")
244 #print de, fk, uk, wc,fl
245 if de>=0:
246 if fk>=0: #error 1062
247 raise db_base_Exception("Value {} already in use for {}".format(e.args[1][de+15:fk], e.args[1][fk+7:]), HTTP_Conflict)
248 if uk>=0:
249 if wc>=0:
250 raise db_base_Exception("Field {} can not be used for filtering".format(e.args[1][uk+14:wc]), HTTP_Bad_Request)
251 if fl>=0:
252 raise db_base_Exception("Field {} does not exist".format(e.args[1][uk+14:wc]), HTTP_Bad_Request)
253 raise db_base_Exception("Database internal Error {}: {}".format(e.args[0], e.args[1]), HTTP_Internal_Server_Error)
254
255 def __str2db_format(self, data):
256 '''Convert string data to database format.
257 If data is None it returns the 'Null' text,
258 otherwise it returns the text surrounded by quotes ensuring internal quotes are escaped.
259 '''
260 if data==None:
261 return 'Null'
262 else:
263 return json.dumps(str(data))
264
265 def __tuple2db_format_set(self, data):
266 '''Compose the needed text for a SQL SET, parameter 'data' is a pair tuple (A,B),
267 and it returns the text 'A="B"', where A is a field of a table and B is the value
268 If B is None it returns the 'A=Null' text, without surrounding Null by quotes
269 If B is not None it returns the text "A='B'" or 'A="B"' where B is surrounded by quotes,
270 and it ensures internal quotes of B are escaped.
271 '''
272 if data[1]==None:
273 return str(data[0]) + "=Null"
274 else:
275 return str(data[0]) + '=' + json.dumps(str(data[1]))
276
277 def __tuple2db_format_where(self, data):
278 '''Compose the needed text for a SQL WHERE, parameter 'data' is a pair tuple (A,B),
279 and it returns the text 'A="B"', where A is a field of a table and B is the value
280 If B is None it returns the 'A is Null' text, without surrounding Null by quotes
281 If B is not None it returns the text "A='B'" or 'A="B"' where B is surrounded by quotes,
282 and it ensures internal quotes of B are escaped.
283 '''
284 if data[1]==None:
285 return str(data[0]) + " is Null"
286
287 # if type(data[1]) is tuple: #this can only happen in a WHERE_OR clause
288 # text =[]
289 # for d in data[1]:
290 # if d==None:
291 # text.append(str(data[0]) + " is Null")
292 # continue
293 # out=str(d)
294 # if "'" not in out:
295 # text.append( str(data[0]) + "='" + out + "'" )
296 # elif '"' not in out:
297 # text.append( str(data[0]) + '="' + out + '"' )
298 # else:
299 # text.append( str(data[0]) + '=' + json.dumps(out) )
300 # return " OR ".join(text)
301
302 out=str(data[1])
303 return str(data[0]) + '=' + json.dumps(out)
304
305 def __tuple2db_format_where_not(self, data):
306 '''Compose the needed text for a SQL WHERE(not). parameter 'data' is a pair tuple (A,B),
307 and it returns the text 'A<>"B"', where A is a field of a table and B is the value
308 If B is None it returns the 'A is not Null' text, without surrounding Null by quotes
309 If B is not None it returns the text "A<>'B'" or 'A<>"B"' where B is surrounded by quotes,
310 and it ensures internal quotes of B are escaped.
311 '''
312 if data[1]==None:
313 return str(data[0]) + " is not Null"
314 out=str(data[1])
315 return str(data[0]) + '<>' + json.dumps(out)
316
317 def __remove_quotes(self, data):
318 '''remove single quotes ' of any string content of data dictionary'''
319 for k,v in data.items():
320 if type(v) == str:
321 if "'" in v:
322 data[k] = data[k].replace("'","_")
323
324 def _update_rows(self, table, UPDATE, WHERE, modified_time=0):
325 ''' Update one or several rows into a table.
326 Atributes
327 UPDATE: dictionary with the key: value to change
328 table: table where to update
329 WHERE: dictionary of elements to update
330 Return: the number of updated rows, exception if error
331 '''
332 #gettting uuid
333 values = ",".join(map(self.__tuple2db_format_set, UPDATE.iteritems() ))
334 if modified_time:
335 values += ",modified_at={:f}".format(modified_time)
336 cmd= "UPDATE " + table +" SET " + values +\
337 " WHERE " + " and ".join(map(self.__tuple2db_format_where, WHERE.iteritems() ))
338 self.logger.debug(cmd)
339 self.cur.execute(cmd)
340 return self.cur.rowcount
341
342 def _new_row_internal(self, table, INSERT, add_uuid=False, root_uuid=None, created_time=0):
343 ''' Add one row into a table. It DOES NOT begin or end the transaction, so self.con.cursor must be created
344 Attribute
345 INSERT: dictionary with the key:value to insert
346 table: table where to insert
347 add_uuid: if True, it will create an uuid key entry at INSERT if not provided
348 created_time: time to add to the created_time column
349 It checks presence of uuid and add one automatically otherwise
350 Return: uuid
351 '''
352
353 if add_uuid:
354 #create uuid if not provided
355 if 'uuid' not in INSERT:
356 uuid = INSERT['uuid'] = str(myUuid.uuid1()) # create_uuid
357 else:
358 uuid = str(INSERT['uuid'])
359 else:
360 uuid=None
361 if add_uuid:
362 #defining root_uuid if not provided
363 if root_uuid is None:
364 root_uuid = uuid
365 if created_time:
366 created_at = created_time
367 else:
368 created_at=time.time()
369 #inserting new uuid
370 cmd = "INSERT INTO uuids (uuid, root_uuid, used_at, created_at) VALUES ('{:s}','{:s}','{:s}', {:f})".format(uuid, root_uuid, table, created_at)
371 self.logger.debug(cmd)
372 self.cur.execute(cmd)
373 #insertion
374 cmd= "INSERT INTO " + table +" SET " + \
375 ",".join(map(self.__tuple2db_format_set, INSERT.iteritems() ))
376 if created_time:
377 cmd += ",created_at=%f" % created_time
378 self.logger.debug(cmd)
379 self.cur.execute(cmd)
380 self.cur.rowcount
381 return uuid
382
383 def _get_rows(self,table,uuid):
384 cmd = "SELECT * FROM {} WHERE uuid='{}'".format(str(table), str(uuid))
385 self.logger.debug(cmd)
386 self.cur.execute(cmd)
387 rows = self.cur.fetchall()
388 return rows
389
390 def new_row(self, table, INSERT, add_uuid=False, created_time=0):
391 ''' Add one row into a table.
392 Attribute
393 INSERT: dictionary with the key: value to insert
394 table: table where to insert
395 tenant_id: only useful for logs. If provided, logs will use this tenant_id
396 add_uuid: if True, it will create an uuid key entry at INSERT if not provided
397 It checks presence of uuid and add one automatically otherwise
398 Return: (result, uuid) where result can be 0 if error, or 1 if ok
399 '''
400 if table in self.tables_with_created_field and created_time==0:
401 created_time=time.time()
402 tries = 2
403 while tries:
404 try:
405 with self.con:
406 self.cur = self.con.cursor()
407 return self._new_row_internal(table, INSERT, add_uuid, None, created_time)
408
409 except (mdb.Error, AttributeError) as e:
410 self._format_error(e, tries)
411 tries -= 1
412
413 def update_rows(self, table, UPDATE, WHERE, modified_time=0):
414 ''' Update one or several rows into a table.
415 Atributes
416 UPDATE: dictionary with the key: value to change
417 table: table where to update
418 WHERE: dictionary of elements to update
419 Return: (result, descriptive text) where result indicates the number of updated files
420 '''
421 if table in self.tables_with_created_field and modified_time==0:
422 modified_time=time.time()
423 tries = 2
424 while tries:
425 try:
426 with self.con:
427 self.cur = self.con.cursor()
428 return self._update_rows(table, UPDATE, WHERE)
429
430 except (mdb.Error, AttributeError) as e:
431 self._format_error(e, tries)
432 tries -= 1
433
434 def delete_row_by_id(self, table, uuid):
435 tries = 2
436 while tries:
437 try:
438 with self.con:
439 #delete host
440 self.cur = self.con.cursor()
441 cmd = "DELETE FROM {} WHERE uuid = '{}'".format(table, uuid)
442 self.logger.debug(cmd)
443 self.cur.execute(cmd)
444 deleted = self.cur.rowcount
445 if deleted:
446 #delete uuid
447 self.cur = self.con.cursor()
448 cmd = "DELETE FROM uuids WHERE root_uuid = '{}'".format(uuid)
449 self.logger.debug(cmd)
450 self.cur.execute(cmd)
451 return deleted
452 except (mdb.Error, AttributeError) as e:
453 self._format_error(e, tries, "delete", "dependencies")
454 tries -= 1
455
456 def delete_row(self, **sql_dict):
457 ''' Deletes rows from a table.
458 Attribute sql_dir: dictionary with the following key: value
459 'FROM': string of table name (Mandatory)
460 'WHERE': dict of key:values, translated to key=value AND ... (Optional)
461 'WHERE_NOT': dict of key:values, translated to key<>value AND ... (Optional)
462 if value is None, it is translated to key is not null
463 'LIMIT': limit of number of rows (Optional)
464 Return: the number of deleted or exception if error
465 '''
466 #print sql_dict
467 from_ = "FROM " + str(sql_dict['FROM'])
468 #print 'from_', from_
469 if 'WHERE' in sql_dict and len(sql_dict['WHERE']) > 0:
470 w=sql_dict['WHERE']
471 where_ = "WHERE " + " AND ".join(map(self.__tuple2db_format_where, w.iteritems()))
472 else: where_ = ""
473 if 'WHERE_NOT' in sql_dict and len(sql_dict['WHERE_NOT']) > 0:
474 w=sql_dict['WHERE_NOT']
475 where_2 = " AND ".join(map(self.__tuple2db_format_where_not, w.iteritems()))
476 if len(where_)==0: where_ = "WHERE " + where_2
477 else: where_ = where_ + " AND " + where_2
478 #print 'where_', where_
479 limit_ = "LIMIT " + str(sql_dict['LIMIT']) if 'LIMIT' in sql_dict else ""
480 #print 'limit_', limit_
481 cmd = " ".join( ("DELETE", from_, where_, limit_) )
482 tries = 2
483 while tries:
484 try:
485 with self.con:
486 self.cur = self.con.cursor()
487 self.logger.debug(cmd)
488 self.cur.execute(cmd)
489 deleted = self.cur.rowcount
490 return deleted
491 except (mdb.Error, AttributeError) as e:
492 self._format_error(e, tries)
493 tries -= 1
494
495 def get_rows_by_id(self, table, uuid):
496 '''get row from a table based on uuid'''
497 tries = 2
498 while tries:
499 try:
500 with self.con:
501 self.cur = self.con.cursor(mdb.cursors.DictCursor)
502 cmd="SELECT * FROM {} where uuid='{}'".format(str(table), str(uuid))
503 self.logger.debug(cmd)
504 self.cur.execute(cmd)
505 rows = self.cur.fetchall()
506 return rows
507 except (mdb.Error, AttributeError) as e:
508 self._format_error(e, tries)
509 tries -= 1
510
511 def get_rows(self, **sql_dict):
512 ''' Obtain rows from a table.
513 Attribute sql_dir: dictionary with the following key: value
514 'SELECT': list or tuple of fields to retrieve) (by default all)
515 'FROM': string of table name (Mandatory)
516 'WHERE': dict of key:values, translated to key=value (key is null) AND ... (Optional)
517 'WHERE_NOT': dict of key:values, translated to key<>value (key is not null) AND ... (Optional)
518 'WHERE_OR': dict of key:values, translated to key=value OR ... (Optional)
519 'WHERE_AND_OR: str 'AND' or 'OR'(by default) mark the priority to 'WHERE AND (WHERE_OR)' or (WHERE) OR WHERE_OR' (Optional)
520 'LIMIT': limit of number of rows (Optional)
521 'ORDER_BY': list or tuple of fields to order
522 Return: a list with dictionaries at each row
523 '''
524 #print sql_dict
525 select_= "SELECT " + ("*" if 'SELECT' not in sql_dict else ",".join(map(str,sql_dict['SELECT'])) )
526 #print 'select_', select_
527 from_ = "FROM " + str(sql_dict['FROM'])
528 #print 'from_', from_
529 where_and = ""
530 where_or = ""
531 w=sql_dict.get('WHERE')
532 if w:
533 where_and = " AND ".join(map(self.__tuple2db_format_where, w.iteritems() ))
534 w=sql_dict.get('WHERE_NOT')
535 if w:
536 if where_and: where_and += " AND "
537 where_and += " AND ".join(map(self.__tuple2db_format_where_not, w.iteritems() ) )
538 w=sql_dict.get('WHERE_OR')
539 if w:
540 where_or = " OR ".join(map(self.__tuple2db_format_where, w.iteritems() ))
541 if where_and and where_or:
542 if sql_dict.get("WHERE_AND_OR") == "AND":
543 where_ = "WHERE " + where_and + " AND (" + where_or + ")"
544 else:
545 where_ = "WHERE (" + where_and + ") OR " + where_or
546 elif where_and and not where_or:
547 where_ = "WHERE " + where_and
548 elif not where_and and where_or:
549 where_ = "WHERE " + where_or
550 else:
551 where_ = ""
552 #print 'where_', where_
553 limit_ = "LIMIT " + str(sql_dict['LIMIT']) if 'LIMIT' in sql_dict else ""
554 order_ = "ORDER BY " + ",".join(map(str,sql_dict['SELECT'])) if 'ORDER_BY' in sql_dict else ""
555
556 #print 'limit_', limit_
557 cmd = " ".join( (select_, from_, where_, limit_, order_) )
558 tries = 2
559 while tries:
560 try:
561 with self.con:
562 self.cur = self.con.cursor(mdb.cursors.DictCursor)
563 self.logger.debug(cmd)
564 self.cur.execute(cmd)
565 rows = self.cur.fetchall()
566 return rows
567 except (mdb.Error, AttributeError) as e:
568 self._format_error(e, tries)
569 tries -= 1
570
571 def get_table_by_uuid_name(self, table, uuid_name, error_item_text=None, allow_serveral=False, WHERE_OR={}, WHERE_AND_OR="OR"):
572 ''' Obtain One row from a table based on name or uuid.
573 Attribute:
574 table: string of table name
575 uuid_name: name or uuid. If not uuid format is found, it is considered a name
576 allow_severeral: if False return ERROR if more than one row are founded
577 error_item_text: in case of error it identifies the 'item' name for a proper output text
578 'WHERE_OR': dict of key:values, translated to key=value OR ... (Optional)
579 'WHERE_AND_OR: str 'AND' or 'OR'(by default) mark the priority to 'WHERE AND (WHERE_OR)' or (WHERE) OR WHERE_OR' (Optional
580 Return: if allow_several==False, a dictionary with this row, or error if no item is found or more than one is found
581 if allow_several==True, a list of dictionaries with the row or rows, error if no item is found
582 '''
583
584 if error_item_text==None:
585 error_item_text = table
586 what = 'uuid' if af.check_valid_uuid(uuid_name) else 'name'
587 cmd = " SELECT * FROM {} WHERE {}='{}'".format(table, what, uuid_name)
588 if WHERE_OR:
589 where_or = " OR ".join(map(self.__tuple2db_format_where, WHERE_OR.iteritems() ))
590 if WHERE_AND_OR == "AND":
591 cmd += " AND (" + where_or + ")"
592 else:
593 cmd += " OR " + where_or
594
595
596 tries = 2
597 while tries:
598 try:
599 with self.con:
600 self.cur = self.con.cursor(mdb.cursors.DictCursor)
601 self.logger.debug(cmd)
602 self.cur.execute(cmd)
603 number = self.cur.rowcount
604 if number==0:
605 return -HTTP_Not_Found, "No %s found with %s '%s'" %(error_item_text, what, uuid_name)
606 elif number>1 and not allow_serveral:
607 return -HTTP_Bad_Request, "More than one %s found with %s '%s'" %(error_item_text, what, uuid_name)
608 if allow_serveral:
609 rows = self.cur.fetchall()
610 else:
611 rows = self.cur.fetchone()
612 return rows
613 except (mdb.Error, AttributeError) as e:
614 self._format_error(e, tries)
615 tries -= 1
616
617 def get_uuid(self, uuid):
618 '''check in the database if this uuid is already present'''
619 for retry_ in range(0,2):
620 try:
621 with self.con:
622 self.cur = self.con.cursor(mdb.cursors.DictCursor)
623 self.cur.execute("SELECT * FROM uuids where uuid='" + str(uuid) + "'")
624 rows = self.cur.fetchall()
625 return self.cur.rowcount, rows
626 except (mdb.Error, AttributeError) as e:
627 print "nfvo_db.get_uuid DB Exception %d: %s" % (e.args[0], e.args[1])
628 r,c = self._format_error(e)
629 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
630
631 def get_uuid_from_name(self, table, name):
632 '''Searchs in table the name and returns the uuid
633 '''
634 tries = 2
635 while tries:
636 try:
637 with self.con:
638 self.cur = self.con.cursor(mdb.cursors.DictCursor)
639 where_text = "name='" + name +"'"
640 self.cur.execute("SELECT * FROM " + table + " WHERE "+ where_text)
641 rows = self.cur.fetchall()
642 if self.cur.rowcount==0:
643 return 0, "Name %s not found in table %s" %(name, table)
644 elif self.cur.rowcount>1:
645 return self.cur.rowcount, "More than one VNF with name %s found in table %s" %(name, table)
646 return self.cur.rowcount, rows[0]["uuid"]
647 except (mdb.Error, AttributeError) as e:
648 self._format_error(e, tries)
649 tries -= 1
650