| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | |
| 3 | ## |
| tierno | 9202102 | 2018-09-12 16:29:23 +0200 | [diff] [blame] | 4 | # Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U. |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 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 | |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 40 | from .http_tools import errors as httperrors |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 41 | |
| 42 | def _check_valid_uuid(uuid): |
| 43 | id_schema = {"type" : "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"} |
| 44 | id_schema2 = {"type" : "string", "pattern": "^[a-fA-F0-9]{32}$"} |
| 45 | try: |
| 46 | js_v(uuid, id_schema) |
| 47 | return True |
| 48 | except js_e.ValidationError: |
| 49 | try: |
| 50 | js_v(uuid, id_schema2) |
| 51 | return True |
| 52 | except js_e.ValidationError: |
| 53 | return False |
| 54 | return False |
| 55 | |
| 56 | def _convert_datetime2str(var): |
| 57 | '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s' |
| 58 | It enters recursively in the dict var finding this kind of variables |
| 59 | ''' |
| 60 | if type(var) is dict: |
| 61 | for k,v in var.items(): |
| 62 | if type(v) is datetime.datetime: |
| 63 | var[k]= v.strftime('%Y-%m-%dT%H:%M:%S') |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 64 | elif type(v) is dict or type(v) is list or type(v) is tuple: |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 65 | _convert_datetime2str(v) |
| 66 | if len(var) == 0: return True |
| 67 | elif type(var) is list or type(var) is tuple: |
| 68 | for v in var: |
| 69 | _convert_datetime2str(v) |
| 70 | |
| tierno | 44528e4 | 2016-10-11 12:06:25 +0000 | [diff] [blame] | 71 | def _convert_bandwidth(data, reverse=False, logger=None): |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 72 | '''Check the field bandwidth recursivelly and when found, it removes units and convert to number |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 73 | It assumes that bandwidth is well formed |
| 74 | Attributes: |
| 75 | 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid |
| 76 | 'reverse': by default convert form str to int (Mbps), if True it convert from number to units |
| 77 | Return: |
| 78 | None |
| 79 | ''' |
| 80 | if type(data) is dict: |
| 81 | for k in data.keys(): |
| 82 | if type(data[k]) is dict or type(data[k]) is tuple or type(data[k]) is list: |
| tierno | 44528e4 | 2016-10-11 12:06:25 +0000 | [diff] [blame] | 83 | _convert_bandwidth(data[k], reverse, logger) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 84 | if "bandwidth" in data: |
| 85 | try: |
| 86 | value=str(data["bandwidth"]) |
| 87 | if not reverse: |
| 88 | pos = value.find("bps") |
| 89 | if pos>0: |
| 90 | if value[pos-1]=="G": data["bandwidth"] = int(data["bandwidth"][:pos-1]) * 1000 |
| 91 | elif value[pos-1]=="k": data["bandwidth"]= int(data["bandwidth"][:pos-1]) / 1000 |
| 92 | else: data["bandwidth"]= int(data["bandwidth"][:pos-1]) |
| 93 | else: |
| 94 | value = int(data["bandwidth"]) |
| 95 | if value % 1000 == 0: data["bandwidth"]=str(value/1000) + " Gbps" |
| 96 | else: data["bandwidth"]=str(value) + " Mbps" |
| 97 | except: |
| tierno | 44528e4 | 2016-10-11 12:06:25 +0000 | [diff] [blame] | 98 | if logger: |
| 99 | logger.error("convert_bandwidth exception for type '%s' data '%s'", type(data["bandwidth"]), data["bandwidth"]) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 100 | return |
| 101 | if type(data) is tuple or type(data) is list: |
| 102 | for k in data: |
| 103 | if type(k) is dict or type(k) is tuple or type(k) is list: |
| tierno | 44528e4 | 2016-10-11 12:06:25 +0000 | [diff] [blame] | 104 | _convert_bandwidth(k, reverse, logger) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 105 | |
| 106 | def _convert_str2boolean(data, items): |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 107 | '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 108 | Done recursively |
| 109 | Attributes: |
| 110 | 'data': dictionary variable to be checked. None or empty is considered valid |
| 111 | 'items': tuple of keys to convert |
| 112 | Return: |
| 113 | None |
| 114 | ''' |
| 115 | if type(data) is dict: |
| 116 | for k in data.keys(): |
| 117 | if type(data[k]) is dict or type(data[k]) is tuple or type(data[k]) is list: |
| 118 | _convert_str2boolean(data[k], items) |
| 119 | if k in items: |
| 120 | if type(data[k]) is str: |
| montesmoreno | 2a1fc4e | 2017-01-09 16:46:04 +0000 | [diff] [blame] | 121 | if data[k]=="false" or data[k]=="False" or data[k]=="0": data[k]=False |
| 122 | elif data[k]=="true" or data[k]=="True" or data[k]=="1": data[k]=True |
| 123 | elif type(data[k]) is int: |
| 124 | if data[k]==0: data[k]=False |
| 125 | elif data[k]==1: data[k]=True |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 126 | if type(data) is tuple or type(data) is list: |
| 127 | for k in data: |
| 128 | if type(k) is dict or type(k) is tuple or type(k) is list: |
| 129 | _convert_str2boolean(k, items) |
| 130 | |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 131 | class db_base_Exception(httperrors.HttpMappedError): |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 132 | '''Common Exception for all database exceptions''' |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 133 | |
| 134 | def __init__(self, message, http_code=httperrors.Bad_Request): |
| 135 | super(db_base_Exception, self).__init__(message, http_code) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 136 | |
| 137 | class db_base(): |
| 138 | tables_with_created_field=() |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 139 | |
| tierno | b13f3cc | 2016-09-26 10:14:44 +0200 | [diff] [blame] | 140 | def __init__(self, host=None, user=None, passwd=None, database=None, log_name='db', log_level=None): |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 141 | self.host = host |
| 142 | self.user = user |
| 143 | self.passwd = passwd |
| 144 | self.database = database |
| 145 | self.con = None |
| 146 | self.log_level=log_level |
| 147 | self.logger = logging.getLogger(log_name) |
| tierno | b13f3cc | 2016-09-26 10:14:44 +0200 | [diff] [blame] | 148 | if self.log_level: |
| 149 | self.logger.setLevel( getattr(logging, log_level) ) |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 150 | |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 151 | def connect(self, host=None, user=None, passwd=None, database=None): |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 152 | '''Connect to specific data base. |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 153 | The first time a valid host, user, passwd and database must be provided, |
| 154 | Following calls can skip this parameters |
| 155 | ''' |
| 156 | try: |
| 157 | if host: self.host = host |
| 158 | if user: self.user = user |
| 159 | if passwd: self.passwd = passwd |
| 160 | if database: self.database = database |
| 161 | |
| 162 | self.con = mdb.connect(self.host, self.user, self.passwd, self.database) |
| tierno | 44528e4 | 2016-10-11 12:06:25 +0000 | [diff] [blame] | 163 | self.logger.debug("DB: connected to '%s' at '%s@%s'", self.database, self.user, self.host) |
| 164 | except mdb.Error as e: |
| 165 | raise db_base_Exception("Cannot connect to DataBase '{}' at '{}@{}' Error {}: {}".format( |
| 166 | self.database, self.user, self.host, e.args[0], e.args[1]), |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 167 | http_code = httperrors.Unauthorized ) |
| 168 | |
| 169 | def escape(self, value): |
| 170 | return self.con.escape(value) |
| 171 | |
| 172 | |
| 173 | def escape_string(self, value): |
| 174 | return self.con.escape_string(value) |
| 175 | |
| 176 | |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 177 | def get_db_version(self): |
| 178 | ''' Obtain the database schema version. |
| 179 | Return: (negative, text) if error or version 0.0 where schema_version table is missing |
| 180 | (version_int, version_text) if ok |
| 181 | ''' |
| 182 | cmd = "SELECT version_int,version FROM schema_version" |
| 183 | tries = 2 |
| 184 | while tries: |
| 185 | try: |
| 186 | with self.con: |
| 187 | self.cur = self.con.cursor() |
| 188 | self.logger.debug(cmd) |
| 189 | self.cur.execute(cmd) |
| 190 | rows = self.cur.fetchall() |
| 191 | highest_version_int=0 |
| 192 | highest_version="" |
| 193 | for row in rows: #look for the latest version |
| 194 | if row[0]>highest_version_int: |
| 195 | highest_version_int, highest_version = row[0:2] |
| 196 | return highest_version_int, highest_version |
| 197 | except (mdb.Error, AttributeError) as e: |
| tierno | fc5f80b | 2018-05-29 16:00:43 +0200 | [diff] [blame] | 198 | self.logger.error("Exception '{}' with command '{}'".format(e, cmd)) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 199 | #self.logger.error("get_db_version DB Exception %d: %s. Command %s",e.args[0], e.args[1], cmd) |
| 200 | self._format_error(e, tries) |
| 201 | tries -= 1 |
| 202 | |
| 203 | def disconnect(self): |
| 204 | '''disconnect from specific data base''' |
| 205 | try: |
| 206 | self.con.close() |
| 207 | self.con = None |
| 208 | except mdb.Error as e: |
| 209 | self.logger.error("while disconnecting from DB: Error %d: %s",e.args[0], e.args[1]) |
| 210 | return |
| 211 | except AttributeError as e: #self.con not defined |
| 212 | if e[0][-5:] == "'con'": |
| 213 | self.logger.warn("while disconnecting from DB: Error %d: %s",e.args[0], e.args[1]) |
| 214 | return |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 215 | else: |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 216 | raise |
| 217 | |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 218 | def _format_error(self, e, tries=1, command=None, extra=None, table=None): |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 219 | '''Creates a text error base on the produced exception |
| 220 | Params: |
| 221 | e: mdb exception |
| 222 | retry: in case of timeout, if reconnecting to database and retry, or raise and exception |
| 223 | cmd: database command that produce the exception |
| 224 | command: if the intention is update or delete |
| 225 | extra: extra information to add to some commands |
| 226 | Return |
| 227 | HTTP error in negative, formatted error text |
| 228 | ''' |
| 229 | if isinstance(e,AttributeError ): |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 230 | self.logger.debug(str(e), exc_info=True) |
| 231 | raise db_base_Exception("DB Exception " + str(e), httperrors.Internal_Server_Error) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 232 | 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 |
| 233 | if tries>1: |
| 234 | self.logger.warn("DB Exception '%s'. Retry", str(e)) |
| 235 | #reconnect |
| 236 | self.connect() |
| 237 | return |
| 238 | else: |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 239 | raise db_base_Exception("Database connection timeout Try Again", httperrors.Request_Timeout) |
| 240 | |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 241 | fk=e.args[1].find("foreign key constraint fails") |
| 242 | if fk>=0: |
| 243 | if command=="update": |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 244 | raise db_base_Exception("tenant_id '{}' not found.".format(extra), httperrors.Not_Found) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 245 | elif command=="delete": |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 246 | raise db_base_Exception("Resource is not free. There are {} that prevent deleting it.".format(extra), httperrors.Conflict) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 247 | de = e.args[1].find("Duplicate entry") |
| 248 | fk = e.args[1].find("for key") |
| 249 | uk = e.args[1].find("Unknown column") |
| 250 | wc = e.args[1].find("in 'where clause'") |
| 251 | fl = e.args[1].find("in 'field list'") |
| 252 | #print de, fk, uk, wc,fl |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 253 | table_info = ' (table `{}`)'.format(table) if table else '' |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 254 | if de>=0: |
| 255 | if fk>=0: #error 1062 |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 256 | raise db_base_Exception( |
| 257 | "Value {} already in use for {}{}".format( |
| 258 | e.args[1][de+15:fk], e.args[1][fk+7:], table_info), |
| 259 | httperrors.Conflict) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 260 | if uk>=0: |
| 261 | if wc>=0: |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 262 | raise db_base_Exception( |
| 263 | "Field {} can not be used for filtering{}".format( |
| 264 | e.args[1][uk+14:wc], table_info), |
| 265 | httperrors.Bad_Request) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 266 | if fl>=0: |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 267 | raise db_base_Exception( |
| 268 | "Field {} does not exist{}".format( |
| 269 | e.args[1][uk+14:wc], table_info), |
| 270 | httperrors.Bad_Request) |
| 271 | raise db_base_Exception( |
| 272 | "Database internal Error{} {}: {}".format( |
| 273 | table_info, e.args[0], e.args[1]), |
| 274 | httperrors.Internal_Server_Error) |
| 275 | |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 276 | def __str2db_format(self, data): |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 277 | '''Convert string data to database format. |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 278 | If data is None it returns the 'Null' text, |
| 279 | otherwise it returns the text surrounded by quotes ensuring internal quotes are escaped. |
| 280 | ''' |
| 281 | if data==None: |
| 282 | return 'Null' |
| tierno | 86fad56 | 2017-04-05 19:53:54 +0200 | [diff] [blame] | 283 | elif isinstance(data[1], str): |
| 284 | return json.dumps(data) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 285 | else: |
| 286 | return json.dumps(str(data)) |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 287 | |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 288 | def __tuple2db_format_set(self, data): |
| tierno | 868220c | 2017-09-26 00:11:05 +0200 | [diff] [blame] | 289 | """Compose the needed text for a SQL SET, parameter 'data' is a pair tuple (A,B), |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 290 | and it returns the text 'A="B"', where A is a field of a table and B is the value |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 291 | If B is None it returns the 'A=Null' text, without surrounding Null by quotes |
| 292 | If B is not None it returns the text "A='B'" or 'A="B"' where B is surrounded by quotes, |
| 293 | and it ensures internal quotes of B are escaped. |
| tierno | 868220c | 2017-09-26 00:11:05 +0200 | [diff] [blame] | 294 | B can be also a dict with special keys: |
| 295 | {"INCREMENT": NUMBER}, then it produce "A=A+NUMBER" |
| 296 | """ |
| 297 | if data[1] == None: |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 298 | return str(data[0]) + "=Null" |
| tierno | 86fad56 | 2017-04-05 19:53:54 +0200 | [diff] [blame] | 299 | elif isinstance(data[1], str): |
| 300 | return str(data[0]) + '=' + json.dumps(data[1]) |
| tierno | 868220c | 2017-09-26 00:11:05 +0200 | [diff] [blame] | 301 | elif isinstance(data[1], dict): |
| 302 | if "INCREMENT" in data[1]: |
| 303 | return "{A}={A}{N:+d}".format(A=data[0], N=data[1]["INCREMENT"]) |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 304 | raise db_base_Exception("Format error for UPDATE field: {!r}".format(data[0])) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 305 | else: |
| 306 | return str(data[0]) + '=' + json.dumps(str(data[1])) |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 307 | |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 308 | def __create_where(self, data, use_or=None): |
| 309 | """ |
| 310 | Compose the needed text for a SQL WHERE, parameter 'data' can be a dict or a list of dict. By default lists are |
| 311 | concatenated with OR and dict with AND, unless parameter 'use_or' indicates other thing. |
| 312 | If a dict it will generate 'key1="value1" AND key2="value2" AND ...'. |
| 313 | If value is None, it will produce 'key is null' |
| 314 | If value is a list or tuple, it will produce 'key="value[0]" OR key="value[1]" OR ...' |
| tierno | 16e3dd4 | 2018-04-24 12:52:40 +0200 | [diff] [blame] | 315 | keys can be suffixed by >,<,<>,>=,<=,' LIKE ' so that this is used to compare key and value instead of "=" |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 316 | The special keys "OR", "AND" with a dict value is used to create a nested WHERE |
| 317 | If a list, each item will be a dictionary that will be concatenated with OR by default |
| 318 | :param data: dict or list of dicts |
| 319 | :param use_or: Can be None (use default behaviour), True (use OR) or False (use AND) |
| 320 | :return: a string with the content to send to mysql |
| 321 | """ |
| 322 | cmd = [] |
| 323 | if isinstance(data, dict): |
| 324 | for k, v in data.items(): |
| 325 | if k == "OR": |
| 326 | cmd.append("(" + self.__create_where(v, use_or=True) + ")") |
| 327 | continue |
| 328 | elif k == "AND": |
| 329 | cmd.append("(" + self.__create_where(v, use_or=False) + ")") |
| 330 | continue |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 331 | |
| tierno | 16e3dd4 | 2018-04-24 12:52:40 +0200 | [diff] [blame] | 332 | if k.endswith(">") or k.endswith("<") or k.endswith("=") or k.endswith(" LIKE "): |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 333 | pass |
| 334 | else: |
| 335 | k += "=" |
| 336 | |
| 337 | if v is None: |
| 338 | cmd.append(k.replace("=", " is").replace("<>", " is not") + " Null") |
| 339 | elif isinstance(v, (tuple, list)): |
| 340 | cmd2 = [] |
| 341 | for v2 in v: |
| 342 | if v2 is None: |
| 343 | cmd2.append(k.replace("=", " is").replace("<>", " is not") + " Null") |
| 344 | else: |
| 345 | cmd2.append(k + json.dumps(str(v2))) |
| 346 | cmd.append("(" + " OR ".join(cmd2) + ")") |
| 347 | else: |
| 348 | cmd.append(k + json.dumps(str(v))) |
| 349 | elif isinstance(data, (tuple, list)): |
| 350 | if use_or is None: |
| 351 | use_or = True |
| 352 | for k in data: |
| 353 | cmd.append("(" + self.__create_where(k) + ")") |
| tierno | 86fad56 | 2017-04-05 19:53:54 +0200 | [diff] [blame] | 354 | else: |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 355 | raise db_base_Exception("invalid WHERE clause at '{}'".format(data)) |
| 356 | if use_or: |
| 357 | return " OR ".join(cmd) |
| 358 | return " AND ".join(cmd) |
| 359 | |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 360 | def __remove_quotes(self, data): |
| 361 | '''remove single quotes ' of any string content of data dictionary''' |
| 362 | for k,v in data.items(): |
| 363 | if type(v) == str: |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 364 | if "'" in v: |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 365 | data[k] = data[k].replace("'","_") |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 366 | |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 367 | def _update_rows(self, table, UPDATE, WHERE, modified_time=0): |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 368 | """ Update one or several rows of a table. |
| 369 | :param UPDATE: dictionary with the changes. dict keys are database columns that will be set with the dict values |
| 370 | :param table: database table to update |
| 371 | :param WHERE: dict or list of dicts to compose the SQL WHERE clause. |
| 372 | If a dict it will generate 'key1="value1" AND key2="value2" AND ...'. |
| 373 | If value is None, it will produce 'key is null' |
| 374 | If value is a list or tuple, it will produce 'key="value[0]" OR key="value[1]" OR ...' |
| 375 | keys can be suffixed by >,<,<>,>=,<= so that this is used to compare key and value instead of "=" |
| 376 | The special keys "OR", "AND" with a dict value is used to create a nested WHERE |
| 377 | If a list, each item will be a dictionary that will be concatenated with OR |
| 378 | :return: the number of updated rows, raises exception upon error |
| 379 | """ |
| 380 | # gettting uuid |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 381 | values = ",".join(map(self.__tuple2db_format_set, UPDATE.iteritems() )) |
| 382 | if modified_time: |
| 383 | values += ",modified_at={:f}".format(modified_time) |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 384 | cmd= "UPDATE " + table + " SET " + values + " WHERE " + self.__create_where(WHERE) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 385 | self.logger.debug(cmd) |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 386 | self.cur.execute(cmd) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 387 | return self.cur.rowcount |
| tierno | 8e69032 | 2017-08-10 15:58:50 +0200 | [diff] [blame] | 388 | |
| 389 | def _new_uuid(self, root_uuid=None, used_table=None, created_time=0): |
| 390 | """ |
| 391 | Generate a new uuid. It DOES NOT begin or end the transaction, so self.con.cursor must be created |
| 392 | :param root_uuid: master uuid of the transaction |
| 393 | :param used_table: the table this uuid is intended for |
| 394 | :param created_time: time of creation |
| 395 | :return: the created uuid |
| 396 | """ |
| 397 | |
| 398 | uuid = str(myUuid.uuid1()) |
| 399 | # defining root_uuid if not provided |
| 400 | if root_uuid is None: |
| 401 | root_uuid = uuid |
| 402 | if created_time: |
| 403 | created_at = created_time |
| 404 | else: |
| 405 | created_at = time.time() |
| 406 | # inserting new uuid |
| 407 | cmd = "INSERT INTO uuids (uuid, root_uuid, used_at, created_at) VALUES ('{:s}','{:s}','{:s}', {:f})".format( |
| 408 | uuid, root_uuid, used_table, created_at) |
| 409 | self.logger.debug(cmd) |
| 410 | self.cur.execute(cmd) |
| 411 | return uuid |
| 412 | |
| gcalvino | c62cfa5 | 2017-10-05 18:21:25 +0200 | [diff] [blame] | 413 | def _new_row_internal(self, table, INSERT, add_uuid=False, root_uuid=None, created_time=0, confidential_data=False): |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 414 | ''' Add one row into a table. It DOES NOT begin or end the transaction, so self.con.cursor must be created |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 415 | Attribute |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 416 | INSERT: dictionary with the key:value to insert |
| 417 | table: table where to insert |
| 418 | add_uuid: if True, it will create an uuid key entry at INSERT if not provided |
| 419 | created_time: time to add to the created_time column |
| 420 | It checks presence of uuid and add one automatically otherwise |
| 421 | Return: uuid |
| 422 | ''' |
| 423 | |
| 424 | if add_uuid: |
| 425 | #create uuid if not provided |
| 426 | if 'uuid' not in INSERT: |
| 427 | uuid = INSERT['uuid'] = str(myUuid.uuid1()) # create_uuid |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 428 | else: |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 429 | uuid = str(INSERT['uuid']) |
| 430 | else: |
| 431 | uuid=None |
| 432 | if add_uuid: |
| 433 | #defining root_uuid if not provided |
| 434 | if root_uuid is None: |
| 435 | root_uuid = uuid |
| 436 | if created_time: |
| 437 | created_at = created_time |
| 438 | else: |
| 439 | created_at=time.time() |
| 440 | #inserting new uuid |
| 441 | cmd = "INSERT INTO uuids (uuid, root_uuid, used_at, created_at) VALUES ('{:s}','{:s}','{:s}', {:f})".format(uuid, root_uuid, table, created_at) |
| 442 | self.logger.debug(cmd) |
| 443 | self.cur.execute(cmd) |
| 444 | #insertion |
| 445 | cmd= "INSERT INTO " + table +" SET " + \ |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 446 | ",".join(map(self.__tuple2db_format_set, INSERT.iteritems() )) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 447 | if created_time: |
| 448 | cmd += ",created_at=%f" % created_time |
| gcalvino | c62cfa5 | 2017-10-05 18:21:25 +0200 | [diff] [blame] | 449 | if confidential_data: |
| 450 | index = cmd.find("SET") |
| 451 | subcmd = cmd[:index] + 'SET...' |
| 452 | self.logger.debug(subcmd) |
| 453 | else: |
| 454 | self.logger.debug(cmd) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 455 | self.cur.execute(cmd) |
| 456 | self.cur.rowcount |
| 457 | return uuid |
| 458 | |
| 459 | def _get_rows(self,table,uuid): |
| 460 | cmd = "SELECT * FROM {} WHERE uuid='{}'".format(str(table), str(uuid)) |
| 461 | self.logger.debug(cmd) |
| 462 | self.cur.execute(cmd) |
| 463 | rows = self.cur.fetchall() |
| 464 | return rows |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 465 | |
| gcalvino | c62cfa5 | 2017-10-05 18:21:25 +0200 | [diff] [blame] | 466 | def new_row(self, table, INSERT, add_uuid=False, created_time=0, confidential_data=False): |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 467 | ''' Add one row into a table. |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 468 | Attribute |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 469 | INSERT: dictionary with the key: value to insert |
| 470 | table: table where to insert |
| 471 | tenant_id: only useful for logs. If provided, logs will use this tenant_id |
| 472 | add_uuid: if True, it will create an uuid key entry at INSERT if not provided |
| 473 | It checks presence of uuid and add one automatically otherwise |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 474 | Return: uuid |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 475 | ''' |
| 476 | if table in self.tables_with_created_field and created_time==0: |
| 477 | created_time=time.time() |
| 478 | tries = 2 |
| 479 | while tries: |
| 480 | try: |
| 481 | with self.con: |
| 482 | self.cur = self.con.cursor() |
| gcalvino | c62cfa5 | 2017-10-05 18:21:25 +0200 | [diff] [blame] | 483 | return self._new_row_internal(table, INSERT, add_uuid, None, created_time, confidential_data) |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 484 | |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 485 | except (mdb.Error, AttributeError) as e: |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 486 | self._format_error(e, tries, table=table) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 487 | tries -= 1 |
| 488 | |
| 489 | def update_rows(self, table, UPDATE, WHERE, modified_time=0): |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 490 | """ Update one or several rows of a table. |
| 491 | :param UPDATE: dictionary with the changes. dict keys are database columns that will be set with the dict values |
| 492 | :param table: database table to update |
| 493 | :param WHERE: dict or list of dicts to compose the SQL WHERE clause. |
| 494 | If a dict it will generate 'key1="value1" AND key2="value2" AND ...'. |
| 495 | If value is None, it will produce 'key is null' |
| 496 | If value is a list or tuple, it will produce 'key="value[0]" OR key="value[1]" OR ...' |
| 497 | keys can be suffixed by >,<,<>,>=,<= so that this is used to compare key and value instead of "=" |
| 498 | The special keys "OR", "AND" with a dict value is used to create a nested WHERE |
| 499 | If a list, each item will be a dictionary that will be concatenated with OR |
| 500 | :param modified_time: Can contain the time to be set to the table row |
| 501 | :return: the number of updated rows, raises exception upon error |
| 502 | """ |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 503 | if table in self.tables_with_created_field and modified_time==0: |
| 504 | modified_time=time.time() |
| 505 | tries = 2 |
| 506 | while tries: |
| 507 | try: |
| 508 | with self.con: |
| 509 | self.cur = self.con.cursor() |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 510 | return self._update_rows( |
| 511 | table, UPDATE, WHERE, modified_time) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 512 | except (mdb.Error, AttributeError) as e: |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 513 | self._format_error(e, tries, table=table) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 514 | tries -= 1 |
| 515 | |
| tierno | fc5f80b | 2018-05-29 16:00:43 +0200 | [diff] [blame] | 516 | def _delete_row_by_id_internal(self, table, uuid): |
| 517 | cmd = "DELETE FROM {} WHERE uuid = '{}'".format(table, uuid) |
| 518 | self.logger.debug(cmd) |
| 519 | self.cur.execute(cmd) |
| 520 | deleted = self.cur.rowcount |
| 521 | # delete uuid |
| 522 | self.cur = self.con.cursor() |
| 523 | cmd = "DELETE FROM uuids WHERE root_uuid = '{}'".format(uuid) |
| 524 | self.logger.debug(cmd) |
| 525 | self.cur.execute(cmd) |
| 526 | return deleted |
| 527 | |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 528 | def delete_row_by_id(self, table, uuid): |
| 529 | tries = 2 |
| 530 | while tries: |
| 531 | try: |
| 532 | with self.con: |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 533 | self.cur = self.con.cursor() |
| tierno | fc5f80b | 2018-05-29 16:00:43 +0200 | [diff] [blame] | 534 | return self._delete_row_by_id_internal(table, uuid) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 535 | except (mdb.Error, AttributeError) as e: |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 536 | self._format_error( |
| 537 | e, tries, "delete", "dependencies", table=table) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 538 | tries -= 1 |
| 539 | |
| 540 | def delete_row(self, **sql_dict): |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 541 | """ Deletes rows from a table. |
| 542 | :param UPDATE: dictionary with the changes. dict keys are database columns that will be set with the dict values |
| 543 | :param FROM: string with table name (Mandatory) |
| 544 | :param WHERE: dict or list of dicts to compose the SQL WHERE clause. (Optional) |
| 545 | If a dict it will generate 'key1="value1" AND key2="value2" AND ...'. |
| 546 | If value is None, it will produce 'key is null' |
| 547 | If value is a list or tuple, it will produce 'key="value[0]" OR key="value[1]" OR ...' |
| 548 | keys can be suffixed by >,<,<>,>=,<= so that this is used to compare key and value instead of "=" |
| 549 | The special keys "OR", "AND" with a dict value is used to create a nested WHERE |
| 550 | If a list, each item will be a dictionary that will be concatenated with OR |
| 551 | :return: the number of deleted rows, raises exception upon error |
| 552 | """ |
| 553 | # print sql_dict |
| 554 | cmd = "DELETE FROM " + str(sql_dict['FROM']) |
| 555 | if sql_dict.get('WHERE'): |
| 556 | cmd += " WHERE " + self.__create_where(sql_dict['WHERE']) |
| 557 | if sql_dict.get('LIMIT'): |
| 558 | cmd += " LIMIT " + str(sql_dict['LIMIT']) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 559 | tries = 2 |
| 560 | while tries: |
| 561 | try: |
| 562 | with self.con: |
| 563 | self.cur = self.con.cursor() |
| 564 | self.logger.debug(cmd) |
| 565 | self.cur.execute(cmd) |
| 566 | deleted = self.cur.rowcount |
| 567 | return deleted |
| 568 | except (mdb.Error, AttributeError) as e: |
| 569 | self._format_error(e, tries) |
| 570 | tries -= 1 |
| 571 | |
| 572 | def get_rows_by_id(self, table, uuid): |
| 573 | '''get row from a table based on uuid''' |
| 574 | tries = 2 |
| 575 | while tries: |
| 576 | try: |
| 577 | with self.con: |
| 578 | self.cur = self.con.cursor(mdb.cursors.DictCursor) |
| 579 | cmd="SELECT * FROM {} where uuid='{}'".format(str(table), str(uuid)) |
| 580 | self.logger.debug(cmd) |
| 581 | self.cur.execute(cmd) |
| 582 | rows = self.cur.fetchall() |
| 583 | return rows |
| 584 | except (mdb.Error, AttributeError) as e: |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 585 | self._format_error(e, tries, table=table) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 586 | tries -= 1 |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 587 | |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 588 | def get_rows(self, **sql_dict): |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 589 | """ Obtain rows from a table. |
| 590 | :param SELECT: list or tuple of fields to retrieve) (by default all) |
| 591 | :param FROM: string with table name (Mandatory) |
| 592 | :param WHERE: dict or list of dicts to compose the SQL WHERE clause. (Optional) |
| 593 | If a dict it will generate 'key1="value1" AND key2="value2" AND ...'. |
| 594 | If value is None, it will produce 'key is null' |
| 595 | If value is a list or tuple, it will produce 'key="value[0]" OR key="value[1]" OR ...' |
| 596 | keys can be suffixed by >,<,<>,>=,<= so that this is used to compare key and value instead of "=" |
| 597 | The special keys "OR", "AND" with a dict value is used to create a nested WHERE |
| 598 | If a list, each item will be a dictionary that will be concatenated with OR |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 599 | :param LIMIT: limit the number of obtained entries (Optional) |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 600 | :param ORDER_BY: list or tuple of fields to order, add ' DESC' to each item if inverse order is required |
| 601 | :return: a list with dictionaries at each row, raises exception upon error |
| 602 | """ |
| 603 | # print sql_dict |
| 604 | cmd = "SELECT " |
| 605 | if 'SELECT' in sql_dict: |
| 606 | if isinstance(sql_dict['SELECT'], (tuple, list)): |
| 607 | cmd += ",".join(map(str, sql_dict['SELECT'])) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 608 | else: |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 609 | cmd += sql_dict['SELECT'] |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 610 | else: |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 611 | cmd += "*" |
| 612 | |
| 613 | cmd += " FROM " + str(sql_dict['FROM']) |
| 614 | if sql_dict.get('WHERE'): |
| 615 | cmd += " WHERE " + self.__create_where(sql_dict['WHERE']) |
| 616 | |
| 617 | if 'ORDER_BY' in sql_dict: |
| 618 | cmd += " ORDER BY " |
| 619 | if isinstance(sql_dict['ORDER_BY'], (tuple, list)): |
| 620 | cmd += ",".join(map(str, sql_dict['ORDER_BY'])) |
| 621 | else: |
| 622 | cmd += str(sql_dict['ORDER_BY']) |
| 623 | |
| 624 | if 'LIMIT' in sql_dict: |
| 625 | cmd += " LIMIT " + str(sql_dict['LIMIT']) |
| 626 | |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 627 | tries = 2 |
| 628 | while tries: |
| 629 | try: |
| 630 | with self.con: |
| 631 | self.cur = self.con.cursor(mdb.cursors.DictCursor) |
| 632 | self.logger.debug(cmd) |
| 633 | self.cur.execute(cmd) |
| 634 | rows = self.cur.fetchall() |
| 635 | return rows |
| 636 | except (mdb.Error, AttributeError) as e: |
| tierno | fc5f80b | 2018-05-29 16:00:43 +0200 | [diff] [blame] | 637 | self.logger.error("Exception '{}' with command '{}'".format(e, cmd)) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 638 | self._format_error(e, tries) |
| 639 | tries -= 1 |
| 640 | |
| 641 | def get_table_by_uuid_name(self, table, uuid_name, error_item_text=None, allow_serveral=False, WHERE_OR={}, WHERE_AND_OR="OR"): |
| 642 | ''' Obtain One row from a table based on name or uuid. |
| 643 | Attribute: |
| 644 | table: string of table name |
| 645 | uuid_name: name or uuid. If not uuid format is found, it is considered a name |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 646 | allow_severeral: if False return ERROR if more than one row are founded |
| 647 | error_item_text: in case of error it identifies the 'item' name for a proper output text |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 648 | 'WHERE_OR': dict of key:values, translated to key=value OR ... (Optional) |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 649 | 'WHERE_AND_OR: str 'AND' or 'OR'(by default) mark the priority to 'WHERE AND (WHERE_OR)' or (WHERE) OR WHERE_OR' (Optional |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 650 | Return: if allow_several==False, a dictionary with this row, or error if no item is found or more than one is found |
| 651 | if allow_several==True, a list of dictionaries with the row or rows, error if no item is found |
| 652 | ''' |
| 653 | |
| 654 | if error_item_text==None: |
| 655 | error_item_text = table |
| 656 | what = 'uuid' if af.check_valid_uuid(uuid_name) else 'name' |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 657 | cmd = " SELECT * FROM {} WHERE {}='{}'".format(table, what, uuid_name) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 658 | if WHERE_OR: |
| tierno | 3fcfdb7 | 2017-10-24 07:48:24 +0200 | [diff] [blame] | 659 | where_or = self.__create_where(WHERE_OR, use_or=True) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 660 | if WHERE_AND_OR == "AND": |
| 661 | cmd += " AND (" + where_or + ")" |
| 662 | else: |
| 663 | cmd += " OR " + where_or |
| 664 | |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 665 | tries = 2 |
| 666 | while tries: |
| 667 | try: |
| 668 | with self.con: |
| 669 | self.cur = self.con.cursor(mdb.cursors.DictCursor) |
| 670 | self.logger.debug(cmd) |
| 671 | self.cur.execute(cmd) |
| 672 | number = self.cur.rowcount |
| tierno | cec213a | 2018-06-27 16:06:17 +0200 | [diff] [blame] | 673 | if number == 0: |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 674 | raise db_base_Exception("No {} found with {} '{}'".format(error_item_text, what, uuid_name), http_code=httperrors.Not_Found) |
| tierno | cec213a | 2018-06-27 16:06:17 +0200 | [diff] [blame] | 675 | elif number > 1 and not allow_serveral: |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 676 | raise db_base_Exception("More than one {} found with {} '{}'".format(error_item_text, what, uuid_name), http_code=httperrors.Conflict) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 677 | if allow_serveral: |
| 678 | rows = self.cur.fetchall() |
| 679 | else: |
| 680 | rows = self.cur.fetchone() |
| 681 | return rows |
| 682 | except (mdb.Error, AttributeError) as e: |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 683 | self._format_error(e, tries, table=table) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 684 | tries -= 1 |
| 685 | |
| 686 | def get_uuid(self, uuid): |
| 687 | '''check in the database if this uuid is already present''' |
| tierno | cec213a | 2018-06-27 16:06:17 +0200 | [diff] [blame] | 688 | tries = 2 |
| 689 | while tries: |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 690 | try: |
| 691 | with self.con: |
| 692 | self.cur = self.con.cursor(mdb.cursors.DictCursor) |
| 693 | self.cur.execute("SELECT * FROM uuids where uuid='" + str(uuid) + "'") |
| 694 | rows = self.cur.fetchall() |
| 695 | return self.cur.rowcount, rows |
| 696 | except (mdb.Error, AttributeError) as e: |
| tierno | cec213a | 2018-06-27 16:06:17 +0200 | [diff] [blame] | 697 | self._format_error(e, tries) |
| 698 | tries -= 1 |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 699 | |
| 700 | def get_uuid_from_name(self, table, name): |
| 701 | '''Searchs in table the name and returns the uuid |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 702 | ''' |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 703 | tries = 2 |
| 704 | while tries: |
| 705 | try: |
| 706 | with self.con: |
| 707 | self.cur = self.con.cursor(mdb.cursors.DictCursor) |
| 708 | where_text = "name='" + name +"'" |
| 709 | self.cur.execute("SELECT * FROM " + table + " WHERE "+ where_text) |
| 710 | rows = self.cur.fetchall() |
| 711 | if self.cur.rowcount==0: |
| 712 | return 0, "Name %s not found in table %s" %(name, table) |
| 713 | elif self.cur.rowcount>1: |
| 714 | return self.cur.rowcount, "More than one VNF with name %s found in table %s" %(name, table) |
| 715 | return self.cur.rowcount, rows[0]["uuid"] |
| 716 | except (mdb.Error, AttributeError) as e: |
| Anderson Bravalheri | 0446cd5 | 2018-08-17 15:26:19 +0100 | [diff] [blame] | 717 | self._format_error(e, tries, table=table) |
| tierno | f97fd27 | 2016-07-11 14:32:37 +0200 | [diff] [blame] | 718 | tries -= 1 |
| 719 | |