1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
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
12 # http://www.apache.org/licenses/LICENSE-2.0
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
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
25 Base class for openmano database manipulation
27 __author__
="Alfonso Tierno"
28 __date__
="$4-Apr-2016 10:05:01$"
38 from jsonschema
import validate
as js_v
, exceptions
as js_e
40 HTTP_Bad_Request
= 400
41 HTTP_Unauthorized
= 401
43 HTTP_Method_Not_Allowed
= 405
44 HTTP_Request_Timeout
= 408
46 HTTP_Service_Unavailable
= 503
47 HTTP_Internal_Server_Error
= 500
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}$"}
55 except js_e
.ValidationError
:
57 js_v(uuid
, id_schema2
)
59 except js_e
.ValidationError
:
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
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:
76 _convert_datetime2str(v
)
78 def _convert_bandwidth(data
, reverse
=False, logger
=None):
79 '''Check the field bandwidth recursivelly and when found, it removes units and convert to number
80 It assumes that bandwidth is well formed
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
87 if type(data
) is dict:
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
, logger
)
91 if "bandwidth" in data
:
93 value
=str(data
["bandwidth"])
95 pos
= value
.find("bps")
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])
101 value
= int(data
["bandwidth"])
102 if value
% 1000 == 0: data
["bandwidth"]=str(value
/1000) + " Gbps"
103 else: data
["bandwidth"]=str(value
) + " Mbps"
106 logger
.error("convert_bandwidth exception for type '%s' data '%s'", type(data
["bandwidth"]), data
["bandwidth"])
108 if type(data
) is tuple or type(data
) is list:
110 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
111 _convert_bandwidth(k
, reverse
, logger
)
113 def _convert_str2boolean(data
, items
):
114 '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
117 'data': dictionary variable to be checked. None or empty is considered valid
118 'items': tuple of keys to convert
122 if type(data
) is dict:
123 for k
in data
.keys():
124 if type(data
[k
]) is dict or type(data
[k
]) is tuple or type(data
[k
]) is list:
125 _convert_str2boolean(data
[k
], items
)
127 if type(data
[k
]) is str:
128 if data
[k
]=="false" or data
[k
]=="False": data
[k
]=False
129 elif data
[k
]=="true" or data
[k
]=="True": data
[k
]=True
130 if type(data
) is tuple or type(data
) is list:
132 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
133 _convert_str2boolean(k
, items
)
135 class db_base_Exception(Exception):
136 '''Common Exception for all database exceptions'''
138 def __init__(self
, message
, http_code
=HTTP_Bad_Request
):
139 Exception.__init
__(self
, message
)
140 self
.http_code
= http_code
143 tables_with_created_field
=()
145 def __init__(self
, host
=None, user
=None, passwd
=None, database
=None, log_name
='db', log_level
=None):
149 self
.database
= database
151 self
.log_level
=log_level
152 self
.logger
= logging
.getLogger(log_name
)
154 self
.logger
.setLevel( getattr(logging
, log_level
) )
156 def connect(self
, host
=None, user
=None, passwd
=None, database
=None):
157 '''Connect to specific data base.
158 The first time a valid host, user, passwd and database must be provided,
159 Following calls can skip this parameters
162 if host
: self
.host
= host
163 if user
: self
.user
= user
164 if passwd
: self
.passwd
= passwd
165 if database
: self
.database
= database
167 self
.con
= mdb
.connect(self
.host
, self
.user
, self
.passwd
, self
.database
)
168 self
.logger
.debug("DB: connected to '%s' at '%s@%s'", self
.database
, self
.user
, self
.host
)
169 except mdb
.Error
as e
:
170 raise db_base_Exception("Cannot connect to DataBase '{}' at '{}@{}' Error {}: {}".format(
171 self
.database
, self
.user
, self
.host
, e
.args
[0], e
.args
[1]),
172 http_code
= HTTP_Unauthorized
)
174 def get_db_version(self
):
175 ''' Obtain the database schema version.
176 Return: (negative, text) if error or version 0.0 where schema_version table is missing
177 (version_int, version_text) if ok
179 cmd
= "SELECT version_int,version FROM schema_version"
184 self
.cur
= self
.con
.cursor()
185 self
.logger
.debug(cmd
)
186 self
.cur
.execute(cmd
)
187 rows
= self
.cur
.fetchall()
188 highest_version_int
=0
190 for row
in rows
: #look for the latest version
191 if row
[0]>highest_version_int
:
192 highest_version_int
, highest_version
= row
[0:2]
193 return highest_version_int
, highest_version
194 except (mdb
.Error
, AttributeError) as e
:
195 #self.logger.error("get_db_version DB Exception %d: %s. Command %s",e.args[0], e.args[1], cmd)
196 self
._format
_error
(e
, tries
)
199 def disconnect(self
):
200 '''disconnect from specific data base'''
204 except mdb
.Error
as e
:
205 self
.logger
.error("while disconnecting from DB: Error %d: %s",e
.args
[0], e
.args
[1])
207 except AttributeError as e
: #self.con not defined
208 if e
[0][-5:] == "'con'":
209 self
.logger
.warn("while disconnecting from DB: Error %d: %s",e
.args
[0], e
.args
[1])
214 def _format_error(self
, e
, tries
=1, command
=None, extra
=None):
215 '''Creates a text error base on the produced exception
218 retry: in case of timeout, if reconnecting to database and retry, or raise and exception
219 cmd: database command that produce the exception
220 command: if the intention is update or delete
221 extra: extra information to add to some commands
223 HTTP error in negative, formatted error text
225 if isinstance(e
,AttributeError ):
226 raise db_base_Exception("DB Exception " + str(e
), HTTP_Internal_Server_Error
)
227 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
229 self
.logger
.warn("DB Exception '%s'. Retry", str(e
))
234 raise db_base_Exception("Database connection timeout Try Again", HTTP_Request_Timeout
)
236 fk
=e
.args
[1].find("foreign key constraint fails")
238 if command
=="update":
239 raise db_base_Exception("tenant_id '{}' not found.".format(extra
), HTTP_Not_Found
)
240 elif command
=="delete":
241 raise db_base_Exception("Resource is not free. There are {} that prevent deleting it.".format(extra
), HTTP_Conflict
)
242 de
= e
.args
[1].find("Duplicate entry")
243 fk
= e
.args
[1].find("for key")
244 uk
= e
.args
[1].find("Unknown column")
245 wc
= e
.args
[1].find("in 'where clause'")
246 fl
= e
.args
[1].find("in 'field list'")
247 #print de, fk, uk, wc,fl
249 if fk
>=0: #error 1062
250 raise db_base_Exception("Value {} already in use for {}".format(e
.args
[1][de
+15:fk
], e
.args
[1][fk
+7:]), HTTP_Conflict
)
253 raise db_base_Exception("Field {} can not be used for filtering".format(e
.args
[1][uk
+14:wc
]), HTTP_Bad_Request
)
255 raise db_base_Exception("Field {} does not exist".format(e
.args
[1][uk
+14:wc
]), HTTP_Bad_Request
)
256 raise db_base_Exception("Database internal Error {}: {}".format(e
.args
[0], e
.args
[1]), HTTP_Internal_Server_Error
)
258 def __str2db_format(self
, data
):
259 '''Convert string data to database format.
260 If data is None it returns the 'Null' text,
261 otherwise it returns the text surrounded by quotes ensuring internal quotes are escaped.
266 return json
.dumps(str(data
))
268 def __tuple2db_format_set(self
, data
):
269 '''Compose the needed text for a SQL SET, parameter 'data' is a pair tuple (A,B),
270 and it returns the text 'A="B"', where A is a field of a table and B is the value
271 If B is None it returns the 'A=Null' text, without surrounding Null by quotes
272 If B is not None it returns the text "A='B'" or 'A="B"' where B is surrounded by quotes,
273 and it ensures internal quotes of B are escaped.
276 return str(data
[0]) + "=Null"
278 return str(data
[0]) + '=' + json
.dumps(str(data
[1]))
280 def __tuple2db_format_where(self
, data
):
281 '''Compose the needed text for a SQL WHERE, parameter 'data' is a pair tuple (A,B),
282 and it returns the text 'A="B"', where A is a field of a table and B is the value
283 If B is None it returns the 'A is Null' text, without surrounding Null by quotes
284 If B is not None it returns the text "A='B'" or 'A="B"' where B is surrounded by quotes,
285 and it ensures internal quotes of B are escaped.
288 return str(data
[0]) + " is Null"
290 # if type(data[1]) is tuple: #this can only happen in a WHERE_OR clause
294 # text.append(str(data[0]) + " is Null")
298 # text.append( str(data[0]) + "='" + out + "'" )
299 # elif '"' not in out:
300 # text.append( str(data[0]) + '="' + out + '"' )
302 # text.append( str(data[0]) + '=' + json.dumps(out) )
303 # return " OR ".join(text)
306 return str(data
[0]) + '=' + json
.dumps(out
)
308 def __tuple2db_format_where_not(self
, data
):
309 '''Compose the needed text for a SQL WHERE(not). parameter 'data' is a pair tuple (A,B),
310 and it returns the text 'A<>"B"', where A is a field of a table and B is the value
311 If B is None it returns the 'A is not Null' text, without surrounding Null by quotes
312 If B is not None it returns the text "A<>'B'" or 'A<>"B"' where B is surrounded by quotes,
313 and it ensures internal quotes of B are escaped.
316 return str(data
[0]) + " is not Null"
318 return str(data
[0]) + '<>' + json
.dumps(out
)
320 def __remove_quotes(self
, data
):
321 '''remove single quotes ' of any string content of data dictionary'''
322 for k
,v
in data
.items():
325 data
[k
] = data
[k
].replace("'","_")
327 def _update_rows(self
, table
, UPDATE
, WHERE
, modified_time
=0):
328 ''' Update one or several rows into a table.
330 UPDATE: dictionary with the key: value to change
331 table: table where to update
332 WHERE: dictionary of elements to update
333 Return: the number of updated rows, exception if error
336 values
= ",".join(map(self
.__tuple
2db
_format
_set
, UPDATE
.iteritems() ))
338 values
+= ",modified_at={:f}".format(modified_time
)
339 cmd
= "UPDATE " + table
+" SET " + values
+\
340 " WHERE " + " and ".join(map(self
.__tuple
2db
_format
_where
, WHERE
.iteritems() ))
341 self
.logger
.debug(cmd
)
342 self
.cur
.execute(cmd
)
343 return self
.cur
.rowcount
345 def _new_row_internal(self
, table
, INSERT
, add_uuid
=False, root_uuid
=None, created_time
=0):
346 ''' Add one row into a table. It DOES NOT begin or end the transaction, so self.con.cursor must be created
348 INSERT: dictionary with the key:value to insert
349 table: table where to insert
350 add_uuid: if True, it will create an uuid key entry at INSERT if not provided
351 created_time: time to add to the created_time column
352 It checks presence of uuid and add one automatically otherwise
357 #create uuid if not provided
358 if 'uuid' not in INSERT
:
359 uuid
= INSERT
['uuid'] = str(myUuid
.uuid1()) # create_uuid
361 uuid
= str(INSERT
['uuid'])
365 #defining root_uuid if not provided
366 if root_uuid
is None:
369 created_at
= created_time
371 created_at
=time
.time()
373 cmd
= "INSERT INTO uuids (uuid, root_uuid, used_at, created_at) VALUES ('{:s}','{:s}','{:s}', {:f})".format(uuid
, root_uuid
, table
, created_at
)
374 self
.logger
.debug(cmd
)
375 self
.cur
.execute(cmd
)
377 cmd
= "INSERT INTO " + table
+" SET " + \
378 ",".join(map(self
.__tuple
2db
_format
_set
, INSERT
.iteritems() ))
380 cmd
+= ",created_at=%f" % created_time
381 self
.logger
.debug(cmd
)
382 self
.cur
.execute(cmd
)
386 def _get_rows(self
,table
,uuid
):
387 cmd
= "SELECT * FROM {} WHERE uuid='{}'".format(str(table
), str(uuid
))
388 self
.logger
.debug(cmd
)
389 self
.cur
.execute(cmd
)
390 rows
= self
.cur
.fetchall()
393 def new_row(self
, table
, INSERT
, add_uuid
=False, created_time
=0):
394 ''' Add one row into a table.
396 INSERT: dictionary with the key: value to insert
397 table: table where to insert
398 tenant_id: only useful for logs. If provided, logs will use this tenant_id
399 add_uuid: if True, it will create an uuid key entry at INSERT if not provided
400 It checks presence of uuid and add one automatically otherwise
401 Return: (result, uuid) where result can be 0 if error, or 1 if ok
403 if table
in self
.tables_with_created_field
and created_time
==0:
404 created_time
=time
.time()
409 self
.cur
= self
.con
.cursor()
410 return self
._new
_row
_internal
(table
, INSERT
, add_uuid
, None, created_time
)
412 except (mdb
.Error
, AttributeError) as e
:
413 self
._format
_error
(e
, tries
)
416 def update_rows(self
, table
, UPDATE
, WHERE
, modified_time
=0):
417 ''' Update one or several rows into a table.
419 UPDATE: dictionary with the key: value to change
420 table: table where to update
421 WHERE: dictionary of elements to update
422 Return: (result, descriptive text) where result indicates the number of updated files
424 if table
in self
.tables_with_created_field
and modified_time
==0:
425 modified_time
=time
.time()
430 self
.cur
= self
.con
.cursor()
431 return self
._update
_rows
(table
, UPDATE
, WHERE
)
433 except (mdb
.Error
, AttributeError) as e
:
434 self
._format
_error
(e
, tries
)
437 def delete_row_by_id(self
, table
, uuid
):
443 self
.cur
= self
.con
.cursor()
444 cmd
= "DELETE FROM {} WHERE uuid = '{}'".format(table
, uuid
)
445 self
.logger
.debug(cmd
)
446 self
.cur
.execute(cmd
)
447 deleted
= self
.cur
.rowcount
450 self
.cur
= self
.con
.cursor()
451 cmd
= "DELETE FROM uuids WHERE root_uuid = '{}'".format(uuid
)
452 self
.logger
.debug(cmd
)
453 self
.cur
.execute(cmd
)
455 except (mdb
.Error
, AttributeError) as e
:
456 self
._format
_error
(e
, tries
, "delete", "dependencies")
459 def delete_row(self
, **sql_dict
):
460 ''' Deletes rows from a table.
461 Attribute sql_dir: dictionary with the following key: value
462 'FROM': string of table name (Mandatory)
463 'WHERE': dict of key:values, translated to key=value AND ... (Optional)
464 'WHERE_NOT': dict of key:values, translated to key<>value AND ... (Optional)
465 if value is None, it is translated to key is not null
466 'LIMIT': limit of number of rows (Optional)
467 Return: the number of deleted or exception if error
470 from_
= "FROM " + str(sql_dict
['FROM'])
471 #print 'from_', from_
472 if 'WHERE' in sql_dict
and len(sql_dict
['WHERE']) > 0:
474 where_
= "WHERE " + " AND ".join(map(self
.__tuple
2db
_format
_where
, w
.iteritems()))
476 if 'WHERE_NOT' in sql_dict
and len(sql_dict
['WHERE_NOT']) > 0:
477 w
=sql_dict
['WHERE_NOT']
478 where_2
= " AND ".join(map(self
.__tuple
2db
_format
_where
_not
, w
.iteritems()))
479 if len(where_
)==0: where_
= "WHERE " + where_2
480 else: where_
= where_
+ " AND " + where_2
481 #print 'where_', where_
482 limit_
= "LIMIT " + str(sql_dict
['LIMIT']) if 'LIMIT' in sql_dict
else ""
483 #print 'limit_', limit_
484 cmd
= " ".join( ("DELETE", from_
, where_
, limit_
) )
489 self
.cur
= self
.con
.cursor()
490 self
.logger
.debug(cmd
)
491 self
.cur
.execute(cmd
)
492 deleted
= self
.cur
.rowcount
494 except (mdb
.Error
, AttributeError) as e
:
495 self
._format
_error
(e
, tries
)
498 def get_rows_by_id(self
, table
, uuid
):
499 '''get row from a table based on uuid'''
504 self
.cur
= self
.con
.cursor(mdb
.cursors
.DictCursor
)
505 cmd
="SELECT * FROM {} where uuid='{}'".format(str(table
), str(uuid
))
506 self
.logger
.debug(cmd
)
507 self
.cur
.execute(cmd
)
508 rows
= self
.cur
.fetchall()
510 except (mdb
.Error
, AttributeError) as e
:
511 self
._format
_error
(e
, tries
)
514 def get_rows(self
, **sql_dict
):
515 ''' Obtain rows from a table.
516 Attribute sql_dir: dictionary with the following key: value
517 'SELECT': list or tuple of fields to retrieve) (by default all)
518 'FROM': string of table name (Mandatory)
519 'WHERE': dict of key:values, translated to key=value (key is null) AND ... (Optional)
520 'WHERE_NOT': dict of key:values, translated to key<>value (key is not null) AND ... (Optional)
521 'WHERE_OR': dict of key:values, translated to key=value OR ... (Optional)
522 'WHERE_AND_OR: str 'AND' or 'OR'(by default) mark the priority to 'WHERE AND (WHERE_OR)' or (WHERE) OR WHERE_OR' (Optional)
523 'LIMIT': limit of number of rows (Optional)
524 'ORDER_BY': list or tuple of fields to order
525 Return: a list with dictionaries at each row
528 select_
= "SELECT " + ("*" if 'SELECT' not in sql_dict
else ",".join(map(str,sql_dict
['SELECT'])) )
529 #print 'select_', select_
530 from_
= "FROM " + str(sql_dict
['FROM'])
531 #print 'from_', from_
534 w
=sql_dict
.get('WHERE')
536 where_and
= " AND ".join(map(self
.__tuple
2db
_format
_where
, w
.iteritems() ))
537 w
=sql_dict
.get('WHERE_NOT')
539 if where_and
: where_and
+= " AND "
540 where_and
+= " AND ".join(map(self
.__tuple
2db
_format
_where
_not
, w
.iteritems() ) )
541 w
=sql_dict
.get('WHERE_OR')
543 where_or
= " OR ".join(map(self
.__tuple
2db
_format
_where
, w
.iteritems() ))
544 if where_and
and where_or
:
545 if sql_dict
.get("WHERE_AND_OR") == "AND":
546 where_
= "WHERE " + where_and
+ " AND (" + where_or
+ ")"
548 where_
= "WHERE (" + where_and
+ ") OR " + where_or
549 elif where_and
and not where_or
:
550 where_
= "WHERE " + where_and
551 elif not where_and
and where_or
:
552 where_
= "WHERE " + where_or
555 #print 'where_', where_
556 limit_
= "LIMIT " + str(sql_dict
['LIMIT']) if 'LIMIT' in sql_dict
else ""
557 order_
= "ORDER BY " + ",".join(map(str,sql_dict
['SELECT'])) if 'ORDER_BY' in sql_dict
else ""
559 #print 'limit_', limit_
560 cmd
= " ".join( (select_
, from_
, where_
, limit_
, order_
) )
565 self
.cur
= self
.con
.cursor(mdb
.cursors
.DictCursor
)
566 self
.logger
.debug(cmd
)
567 self
.cur
.execute(cmd
)
568 rows
= self
.cur
.fetchall()
570 except (mdb
.Error
, AttributeError) as e
:
571 self
._format
_error
(e
, tries
)
574 def get_table_by_uuid_name(self
, table
, uuid_name
, error_item_text
=None, allow_serveral
=False, WHERE_OR
={}, WHERE_AND_OR
="OR"):
575 ''' Obtain One row from a table based on name or uuid.
577 table: string of table name
578 uuid_name: name or uuid. If not uuid format is found, it is considered a name
579 allow_severeral: if False return ERROR if more than one row are founded
580 error_item_text: in case of error it identifies the 'item' name for a proper output text
581 'WHERE_OR': dict of key:values, translated to key=value OR ... (Optional)
582 'WHERE_AND_OR: str 'AND' or 'OR'(by default) mark the priority to 'WHERE AND (WHERE_OR)' or (WHERE) OR WHERE_OR' (Optional
583 Return: if allow_several==False, a dictionary with this row, or error if no item is found or more than one is found
584 if allow_several==True, a list of dictionaries with the row or rows, error if no item is found
587 if error_item_text
==None:
588 error_item_text
= table
589 what
= 'uuid' if af
.check_valid_uuid(uuid_name
) else 'name'
590 cmd
= " SELECT * FROM {} WHERE {}='{}'".format(table
, what
, uuid_name
)
592 where_or
= " OR ".join(map(self
.__tuple
2db
_format
_where
, WHERE_OR
.iteritems() ))
593 if WHERE_AND_OR
== "AND":
594 cmd
+= " AND (" + where_or
+ ")"
596 cmd
+= " OR " + where_or
603 self
.cur
= self
.con
.cursor(mdb
.cursors
.DictCursor
)
604 self
.logger
.debug(cmd
)
605 self
.cur
.execute(cmd
)
606 number
= self
.cur
.rowcount
608 return -HTTP_Not_Found
, "No %s found with %s '%s'" %(error_item_text
, what
, uuid_name
)
609 elif number
>1 and not allow_serveral
:
610 return -HTTP_Bad_Request
, "More than one %s found with %s '%s'" %(error_item_text
, what
, uuid_name
)
612 rows
= self
.cur
.fetchall()
614 rows
= self
.cur
.fetchone()
616 except (mdb
.Error
, AttributeError) as e
:
617 self
._format
_error
(e
, tries
)
620 def get_uuid(self
, uuid
):
621 '''check in the database if this uuid is already present'''
622 for retry_
in range(0,2):
625 self
.cur
= self
.con
.cursor(mdb
.cursors
.DictCursor
)
626 self
.cur
.execute("SELECT * FROM uuids where uuid='" + str(uuid
) + "'")
627 rows
= self
.cur
.fetchall()
628 return self
.cur
.rowcount
, rows
629 except (mdb
.Error
, AttributeError) as e
:
630 print "nfvo_db.get_uuid DB Exception %d: %s" % (e
.args
[0], e
.args
[1])
631 r
,c
= self
._format
_error
(e
)
632 if r
!=-HTTP_Request_Timeout
or retry_
==1: return r
,c
634 def get_uuid_from_name(self
, table
, name
):
635 '''Searchs in table the name and returns the uuid
641 self
.cur
= self
.con
.cursor(mdb
.cursors
.DictCursor
)
642 where_text
= "name='" + name
+"'"
643 self
.cur
.execute("SELECT * FROM " + table
+ " WHERE "+ where_text
)
644 rows
= self
.cur
.fetchall()
645 if self
.cur
.rowcount
==0:
646 return 0, "Name %s not found in table %s" %(name
, table
)
647 elif self
.cur
.rowcount
>1:
648 return self
.cur
.rowcount
, "More than one VNF with name %s found in table %s" %(name
, table
)
649 return self
.cur
.rowcount
, rows
[0]["uuid"]
650 except (mdb
.Error
, AttributeError) as e
:
651 self
._format
_error
(e
, tries
)