Allow several pci for of_port_mapping. Log enhancement
[osm/openvim.git] / vim_db.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 openvim
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 This module interact with the openvim database,
27 It implements general table management
28 and complex writings 'transactional' sures,
29 that is, or all is changed or nothing
30 '''
31
32 __author__="Alfonso Tierno"
33 __date__ ="$10-jul-2014 12:07:15$"
34
35 import MySQLdb as mdb
36 import uuid as myUuid
37 import auxiliary_functions as af
38 import json
39 import logging
40 from netaddr import IPNetwork, IPSet, IPRange, all_matching_cidrs
41
42 HTTP_Bad_Request = 400
43 HTTP_Unauthorized = 401
44 HTTP_Not_Found = 404
45 HTTP_Method_Not_Allowed = 405
46 HTTP_Request_Timeout = 408
47 HTTP_Conflict = 409
48 HTTP_Service_Unavailable = 503
49 HTTP_Internal_Server_Error = 500
50
51
52 class vim_db():
53 def __init__(self, vlan_range, logger_name= None, debug=None):
54 '''vlan_range must be a tuple (vlan_ini, vlan_end) with available vlan values for networks
55 every dataplane network contain a unique value, regardless of it is used or not
56 '''
57 #initialization
58 self.net_vlan_range = vlan_range
59 self.net_vlan_usedlist = None
60 self.net_vlan_lastused = self.net_vlan_range[0] -1
61 self.debug=debug
62 if logger_name:
63 self.logger_name = logger_name
64 else:
65 self.logger_name = 'openvim.db'
66 self.logger = logging.getLogger(self.logger_name)
67 if debug:
68 self.logger.setLevel( getattr(logging, debug) )
69
70
71 def connect(self, host=None, user=None, passwd=None, database=None):
72 '''Connect to the concrete data base.
73 The first time a valid host, user, passwd and database must be provided,
74 Following calls can skip this parameters
75 '''
76 try:
77 if host is not None: self.host = host
78 if user is not None: self.user = user
79 if passwd is not None: self.passwd = passwd
80 if database is not None: self.database = database
81
82 self.con = mdb.connect(self.host, self.user, self.passwd, self.database)
83 self.logger.debug("connected to DB %s at %s@%s", self.database,self.user, self.host)
84 return 0
85 except mdb.Error as e:
86 self.logger.error("Cannot connect to DB %s at %s@%s Error %d: %s", self.database, self.user, self.host, e.args[0], e.args[1])
87 return -1
88
89 def get_db_version(self):
90 ''' Obtain the database schema version.
91 Return: (negative, text) if error or version 0.0 where schema_version table is missing
92 (version_int, version_text) if ok
93 '''
94 cmd = "SELECT version_int,version,openvim_ver FROM schema_version"
95 for retry_ in range(0,2):
96 try:
97 with self.con:
98 self.cur = self.con.cursor()
99 self.logger.debug(cmd)
100 self.cur.execute(cmd)
101 rows = self.cur.fetchall()
102 highest_version_int=0
103 highest_version=""
104 #print rows
105 for row in rows: #look for the latest version
106 if row[0]>highest_version_int:
107 highest_version_int, highest_version = row[0:2]
108 return highest_version_int, highest_version
109 except (mdb.Error, AttributeError) as e:
110 self.logger.error("get_db_version DB Exception %d: %s. Command %s",e.args[0], e.args[1], cmd)
111 r,c = self.format_error(e)
112 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
113
114 def disconnect(self):
115 '''disconnect from the data base'''
116 try:
117 self.con.close()
118 del self.con
119 except mdb.Error as e:
120 self.logger.error("while disconnecting from DB: Error %d: %s",e.args[0], e.args[1])
121 return -1
122 except AttributeError as e: #self.con not defined
123 if e[0][-5:] == "'con'": return -1, "Database internal error, no connection."
124 else: raise
125
126 def format_error(self, e, func, cmd, command=None, extra=None):
127 '''Creates a text error base on the produced exception
128 Params:
129 e: mdb exception
130 func: name of the function that makes the call, for logging purposes
131 cmd: database command that produce the exception
132 command: if the intention is update or delete
133 extra: extra information to add to some commands
134 Return
135 HTTP error in negative, formatted error text
136 '''
137
138 self.logger.error("%s DB Exception %s. Command %s",func, str(e), cmd)
139 if type(e[0]) is str:
140 if e[0][-5:] == "'con'": return -HTTP_Internal_Server_Error, "DB Exception, no connection."
141 else: raise
142 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
143 #reconnect
144 self.connect()
145 return -HTTP_Request_Timeout,"Database reconnection. Try Again"
146 fk=e.args[1].find("foreign key constraint fails")
147 if fk>=0:
148 if command=="update": return -HTTP_Bad_Request, "tenant_id %s not found." % extra
149 elif command=="delete": return -HTTP_Bad_Request, "Resource is not free. There are %s that prevent its deletion." % extra
150 de = e.args[1].find("Duplicate entry")
151 fk = e.args[1].find("for key")
152 uk = e.args[1].find("Unknown column")
153 wc = e.args[1].find("in 'where clause'")
154 fl = e.args[1].find("in 'field list'")
155 #print de, fk, uk, wc,fl
156 if de>=0:
157 if fk>=0: #error 1062
158 return -HTTP_Conflict, "Value %s already in use for %s" % (e.args[1][de+15:fk], e.args[1][fk+7:])
159 if uk>=0:
160 if wc>=0:
161 return -HTTP_Bad_Request, "Field %s cannot be used for filtering" % e.args[1][uk+14:wc]
162 if fl>=0:
163 return -HTTP_Bad_Request, "Field %s does not exist" % e.args[1][uk+14:wc]
164 return -HTTP_Internal_Server_Error, "Database internal Error %d: %s" % (e.args[0], e.args[1])
165
166 def __data2db_format(self, data):
167 '''convert data to database format. If data is None it return the 'Null' text,
168 otherwise it return the text surrounded by quotes ensuring internal quotes are escaped'''
169 if data==None:
170 return 'Null'
171 out=str(data)
172 if "'" not in out:
173 return "'" + out + "'"
174 elif '"' not in out:
175 return '"' + out + '"'
176 else:
177 return json.dumps(out)
178
179 def __get_used_net_vlan(self):
180 #get used from database if needed
181 try:
182 cmd = "SELECT vlan FROM nets WHERE vlan>='%s' ORDER BY vlan LIMIT 25" % self.net_vlan_lastused
183 with self.con:
184 self.cur = self.con.cursor()
185 self.logger.debug(cmd)
186 self.cur.execute(cmd)
187 vlan_tuple = self.cur.fetchall()
188 #convert a tuple of tuples in a list of numbers
189 self.net_vlan_usedlist = []
190 for k in vlan_tuple:
191 self.net_vlan_usedlist.append(k[0])
192 return 0
193 except (mdb.Error, AttributeError) as e:
194 return self.format_error(e, "get_free_net_vlan", cmd)
195
196 def get_free_net_vlan(self):
197 '''obtain a vlan not used in any net'''
198
199 while True:
200 self.logger.debug("net_vlan_lastused:%d net_vlan_range:%d-%d net_vlan_usedlist:%s",
201 self.net_vlan_lastused, self.net_vlan_range[0], self.net_vlan_range[1], str(self.net_vlan_usedlist))
202 self.net_vlan_lastused += 1
203 if self.net_vlan_lastused == self.net_vlan_range[1]:
204 #start from the begining
205 self.net_vlan_lastused = self.net_vlan_range[0]
206 self.net_vlan_usedlist = None
207 if self.net_vlan_usedlist is None \
208 or (len(self.net_vlan_usedlist)>0 and self.net_vlan_lastused >= self.net_vlan_usedlist[-1] and len(self.net_vlan_usedlist)==25):
209 r = self.__get_used_net_vlan()
210 if r<0: return r
211 self.logger.debug("new net_vlan_usedlist %s", str(self.net_vlan_usedlist))
212 if self.net_vlan_lastused in self.net_vlan_usedlist:
213 continue
214 else:
215 return self.net_vlan_lastused
216
217 def get_table(self, **sql_dict):
218 ''' Obtain rows from a table.
219 Atribure sql_dir: dictionary with the following key: value
220 'SELECT': [list of fields to retrieve] (by default all)
221 'FROM': string of table name (Mandatory)
222 'WHERE': dict of key:values, translated to key=value AND ... (Optional)
223 'WHERE_NOT': dict of key:values, translated to key!=value AND ... (Optional)
224 'WHERE_OR': dict of key:values, translated to key=value OR ... (Optional)
225 'WHERE_AND_OR: str 'AND' or 'OR'(by default) mark the priority to 'WHERE AND (WHERE_OR)' or (WHERE) OR WHERE_OR' (Optional)
226 'LIMIT': limit of number of rows (Optional)
227 'DISTINCT': make a select distinct to remove repeated elements
228 Return: a list with dictionarys at each row
229 '''
230 #print sql_dict
231 select_ = "SELECT "
232 if sql_dict.get("DISTINCT"):
233 select_ += "DISTINCT "
234 select_ += ("*" if not sql_dict.get('SELECT') else ",".join(map(str,sql_dict['SELECT'])) )
235 #print 'select_', select_
236 from_ = "FROM " + str(sql_dict['FROM'])
237 #print 'from_', from_
238
239 where_and = None
240 where_or = None
241 w = sql_dict.get('WHERE')
242 if w:
243 where_and = " AND ".join(map( lambda x: str(x) + (" is Null" if w[x] is None else "='"+str(w[x])+"'"), w.keys()) )
244 w = sql_dict.get('WHERE_NOT')
245 if w:
246 where_and_not = " AND ".join(map( lambda x: str(x) + (" is not Null" if w[x] is None else "!='"+str(w[x])+"'"), w.keys()) )
247 if where_and:
248 where_and += " AND " + where_and_not
249 else:
250 where_and = where_and_not
251 w = sql_dict.get('WHERE_OR')
252 if w:
253 where_or = " OR ".join(map( lambda x: str(x) + (" is Null" if w[x] is None else "='"+str(w[x])+"'"), w.keys()) )
254
255 if where_and!=None and where_or!=None:
256 if sql_dict.get("WHERE_AND_OR") == "AND":
257 where_ = "WHERE " + where_and + " AND (" + where_or + ")"
258 else:
259 where_ = "WHERE (" + where_and + ") OR " + where_or
260 elif where_and!=None and where_or==None:
261 where_ = "WHERE " + where_and
262 elif where_and==None and where_or!=None:
263 where_ = "WHERE " + where_or
264 else:
265 where_ = ""
266 #print 'where_', where_
267 limit_ = "LIMIT " + str(sql_dict['LIMIT']) if sql_dict.get("LIMIT") else ""
268 #print 'limit_', limit_
269 cmd = " ".join( (select_, from_, where_, limit_) )
270 for retry_ in range(0,2):
271 try:
272 with self.con:
273 self.cur = self.con.cursor(mdb.cursors.DictCursor)
274 self.logger.debug(cmd)
275 self.cur.execute(cmd)
276 rows = self.cur.fetchall()
277 return self.cur.rowcount, rows
278 except (mdb.Error, AttributeError) as e:
279 r,c = self.format_error(e, "get_table", cmd)
280 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
281
282 def new_tenant(self, tenant_dict):
283 ''' Add one row into a table.
284 Attribure
285 tenant_dict: dictionary with the key: value to insert
286 It checks presence of uuid and add one automatically otherwise
287 Return: (result, uuid) where result can be 0 if error, or 1 if ok
288 '''
289 for retry_ in range(0,2):
290 cmd=""
291 inserted=-1
292 try:
293 #create uuid if not provided
294 if 'uuid' not in tenant_dict:
295 uuid = tenant_dict['uuid'] = str(myUuid.uuid1()) # create_uuid
296 else:
297 uuid = str(tenant_dict['uuid'])
298 #obtain tenant_id for logs
299 tenant_id = uuid
300 with self.con:
301 self.cur = self.con.cursor()
302 #inserting new uuid
303 cmd = "INSERT INTO uuids (uuid, used_at) VALUES ('%s','tenants')" % uuid
304 self.logger.debug(cmd)
305 self.cur.execute(cmd)
306 #insert tenant
307 cmd= "INSERT INTO tenants (" + \
308 ",".join(map(str, tenant_dict.keys() )) + ") VALUES(" + \
309 ",".join(map(lambda x: "Null" if x is None else "'"+str(x)+"'",tenant_dict.values() )) + ")"
310 self.logger.debug(cmd)
311 self.cur.execute(cmd)
312 inserted = self.cur.rowcount
313 ##inserting new log
314 #del tenant_dict['uuid'] # not interested for the log
315 #cmd = "INSERT INTO logs (related,level,tenant_id,uuid,description) VALUES ('tenants','debug','%s','%s',\"new tenant %s\")" % (uuid, tenant_id, str(tenant_dict))
316 #self.logger.debug(cmd)
317 #self.cur.execute(cmd)
318 #commit transaction
319 self.cur.close()
320 if inserted == 0: return 0, uuid
321 with self.con:
322 self.cur = self.con.cursor()
323 #adding public flavors
324 cmd = "INSERT INTO tenants_flavors(flavor_id,tenant_id) SELECT uuid as flavor_id,'"+ tenant_id + "' FROM flavors WHERE public = 'yes'"
325 self.logger.debug(cmd)
326 self.cur.execute(cmd)
327 self.logger.debug("attached public flavors: %s", str(self.cur.rowcount))
328 #rows = self.cur.fetchall()
329 #for row in rows:
330 # cmd = "INSERT INTO tenants_flavors(flavor_id,tenant_id) VALUES('%s','%s')" % (row[0], tenant_id)
331 # self.cur.execute(cmd )
332 #adding public images
333 cmd = "INSERT INTO tenants_images(image_id,tenant_id) SELECT uuid as image_id,'"+ tenant_id + "' FROM images WHERE public = 'yes'"
334 self.logger.debug(cmd)
335 self.cur.execute(cmd)
336 self.logger.debug("attached public images: %s", str(self.cur.rowcount))
337 return 1, uuid
338 except (mdb.Error, AttributeError) as e:
339 if inserted==1:
340 self.logger.warning("new_tenant DB Exception %d: %s. Command %s",e.args[0], e.args[1], cmd)
341 return 1, uuid
342 else:
343 r,c = self.format_error(e, "new_tenant", cmd)
344 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
345
346 def new_row(self, table, INSERT, add_uuid=False, log=False):
347 ''' Add one row into a table.
348 Atribure
349 INSERT: dictionary with the key: value to insert
350 table: table where to insert
351 add_uuid: if True, it will crated an uuid key entry at INSERT if not provided
352 It checks presence of uuid and add one automatically otherwise
353 Return: (result, uuid) where result can be 0 if error, or 1 if ok
354 '''
355 for retry_ in range(0,2):
356 cmd=""
357 try:
358 if add_uuid:
359 #create uuid if not provided
360 if 'uuid' not in INSERT:
361 uuid = INSERT['uuid'] = str(myUuid.uuid1()) # create_uuid
362 else:
363 uuid = str(INSERT['uuid'])
364 else:
365 uuid=None
366 with self.con:
367 self.cur = self.con.cursor()
368 if add_uuid:
369 #inserting new uuid
370 cmd = "INSERT INTO uuids (uuid, used_at) VALUES ('%s','%s')" % (uuid, table)
371 self.logger.debug(cmd)
372 self.cur.execute(cmd)
373 #insertion
374 cmd= "INSERT INTO " + table +" (" + \
375 ",".join(map(str, INSERT.keys() )) + ") VALUES(" + \
376 ",".join(map(lambda x: 'Null' if x is None else "'"+str(x)+"'", INSERT.values() )) + ")"
377 self.logger.debug(cmd)
378 self.cur.execute(cmd)
379 nb_rows = self.cur.rowcount
380 #inserting new log
381 #if nb_rows > 0 and log:
382 # if add_uuid: del INSERT['uuid']
383 # #obtain tenant_id for logs
384 # if 'tenant_id' in INSERT:
385 # tenant_id = INSERT['tenant_id']
386 # del INSERT['tenant_id']
387 # elif table == 'tenants':
388 # tenant_id = uuid
389 # else:
390 # tenant_id = None
391 # if uuid is None: uuid_k = uuid_v = ""
392 # else: uuid_k=",uuid"; uuid_v=",'" + str(uuid) + "'"
393 # if tenant_id is None: tenant_k = tenant_v = ""
394 # else: tenant_k=",tenant_id"; tenant_v=",'" + str(tenant_id) + "'"
395 # cmd = "INSERT INTO logs (related,level%s%s,description) VALUES ('%s','debug'%s%s,\"new %s %s\")" \
396 # % (uuid_k, tenant_k, table, uuid_v, tenant_v, table[:-1], str(INSERT))
397 # self.logger.debug(cmd)
398 # self.cur.execute(cmd)
399 return nb_rows, uuid
400
401 except (mdb.Error, AttributeError) as e:
402 r,c = self.format_error(e, "new_row", cmd)
403 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
404
405 def __remove_quotes(self, data):
406 '''remove single quotes ' of any string content of data dictionary'''
407 for k,v in data.items():
408 if type(v) == str:
409 if "'" in v:
410 data[k] = data[k].replace("'","_")
411
412 def _update_rows_internal(self, table, UPDATE, WHERE={}):
413 cmd= "UPDATE " + table +" SET " + \
414 ",".join(map(lambda x: str(x)+'='+ self.__data2db_format(UPDATE[x]), UPDATE.keys() ));
415 if WHERE:
416 cmd += " WHERE " + " and ".join(map(lambda x: str(x)+ (' is Null' if WHERE[x] is None else"='"+str(WHERE[x])+"'" ), WHERE.keys() ))
417 self.logger.debug(cmd)
418 self.cur.execute(cmd)
419 nb_rows = self.cur.rowcount
420 return nb_rows, None
421
422 def update_rows(self, table, UPDATE, WHERE={}, log=False):
423 ''' Update one or several rows into a table.
424 Atributes
425 UPDATE: dictionary with the key-new_value pairs to change
426 table: table to be modified
427 WHERE: dictionary to filter target rows, key-value
428 log: if true, a log entry is added at logs table
429 Return: (result, None) where result indicates the number of updated files
430 '''
431 for retry_ in range(0,2):
432 cmd=""
433 try:
434 #gettting uuid
435 uuid = WHERE.get('uuid')
436
437 with self.con:
438 self.cur = self.con.cursor()
439 cmd= "UPDATE " + table +" SET " + \
440 ",".join(map(lambda x: str(x)+'='+ self.__data2db_format(UPDATE[x]), UPDATE.keys() ));
441 if WHERE:
442 cmd += " WHERE " + " and ".join(map(lambda x: str(x)+ (' is Null' if WHERE[x] is None else"='"+str(WHERE[x])+"'" ), WHERE.keys() ))
443 self.logger.debug(cmd)
444 self.cur.execute(cmd)
445 nb_rows = self.cur.rowcount
446 #if nb_rows > 0 and log:
447 # #inserting new log
448 # if uuid is None: uuid_k = uuid_v = ""
449 # else: uuid_k=",uuid"; uuid_v=",'" + str(uuid) + "'"
450 # cmd = "INSERT INTO logs (related,level%s,description) VALUES ('%s','debug'%s,\"updating %d entry %s\")" \
451 # % (uuid_k, table, uuid_v, nb_rows, (str(UPDATE)).replace('"','-') )
452 # self.logger.debug(cmd)
453 # self.cur.execute(cmd)
454 return nb_rows, uuid
455 except (mdb.Error, AttributeError) as e:
456 r,c = self.format_error(e, "update_rows", cmd)
457 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
458
459 def get_host(self, host_id):
460 if af.check_valid_uuid(host_id):
461 where_filter="uuid='" + host_id + "'"
462 else:
463 where_filter="name='" + host_id + "'"
464 for retry_ in range(0,2):
465 cmd=""
466 try:
467 with self.con:
468 self.cur = self.con.cursor(mdb.cursors.DictCursor)
469 #get HOST
470 cmd = "SELECT uuid, user, name, ip_name, description, ranking, admin_state_up, DATE_FORMAT(created_at,'%Y-%m-%dT%H:%i:%s') as created_at \
471 FROM hosts WHERE " + where_filter
472 self.logger.debug(cmd)
473 self.cur.execute(cmd)
474 if self.cur.rowcount == 0 :
475 return 0, "host '" + str(host_id) +"'not found."
476 elif self.cur.rowcount > 1 :
477 return 0, "host '" + str(host_id) +"' matches more than one result."
478 host = self.cur.fetchone()
479 host_id = host['uuid']
480 #get numa
481 cmd = "SELECT id, numa_socket, hugepages, memory, admin_state_up FROM numas WHERE host_id = '" + str(host_id) + "'"
482 self.logger.debug(cmd)
483 self.cur.execute(cmd)
484 host['numas'] = self.cur.fetchall()
485 for numa in host['numas']:
486 #print "SELECT core_id, instance_id, status, thread_id, v_thread_id FROM resources_core WHERE numa_id = '" + str(numa['id']) + "'"
487 #get cores
488 cmd = "SELECT core_id, instance_id, status, thread_id, v_thread_id FROM resources_core WHERE numa_id = '" + str(numa['id']) + "'"
489 self.logger.debug(cmd)
490 self.cur.execute(cmd)
491 numa['cores'] = self.cur.fetchall()
492 for core in numa['cores']:
493 if core['instance_id'] == None: del core['instance_id'], core['v_thread_id']
494 if core['status'] == 'ok': del core['status']
495 #get used memory
496 cmd = "SELECT sum(consumed) as hugepages_consumed FROM resources_mem WHERE numa_id = '" + str(numa['id']) + "' GROUP BY numa_id"
497 self.logger.debug(cmd)
498 self.cur.execute(cmd)
499 used = self.cur.fetchone()
500 used_= int(used['hugepages_consumed']) if used != None else 0
501 numa['hugepages_consumed'] = used_
502 #get ports
503 #cmd = "CALL GetPortsFromNuma(%s)'" % str(numa['id'])
504 #self.cur.callproc('GetPortsFromNuma', (numa['id'],) )
505 #every time a Procedure is launched you need to close and open the cursor
506 #under Error 2014: Commands out of sync; you can't run this command now
507 #self.cur.close()
508 #self.cur = self.con.cursor(mdb.cursors.DictCursor)
509 cmd="SELECT Mbps, pci, status, Mbps_used, instance_id, if(id=root_id,'PF','VF') as type_,\
510 switch_port, switch_dpid, mac, source_name\
511 FROM resources_port WHERE numa_id=%d ORDER BY root_id, type_ DESC" % (numa['id'])
512 self.logger.debug(cmd)
513 self.cur.execute(cmd)
514 ifaces = self.cur.fetchall()
515 #The SQL query will ensure to have SRIOV interfaces from a port first
516 sriovs=[]
517 Mpbs_consumed = 0
518 numa['interfaces'] = []
519 for iface in ifaces:
520 if not iface["instance_id"]:
521 del iface["instance_id"]
522 if iface['status'] == 'ok':
523 del iface['status']
524 Mpbs_consumed += int(iface["Mbps_used"])
525 del iface["Mbps_used"]
526 if iface["type_"]=='PF':
527 if not iface["switch_dpid"]:
528 del iface["switch_dpid"]
529 if not iface["switch_port"]:
530 del iface["switch_port"]
531 if sriovs:
532 iface["sriovs"] = sriovs
533 if Mpbs_consumed:
534 iface["Mpbs_consumed"] = Mpbs_consumed
535 del iface["type_"]
536 numa['interfaces'].append(iface)
537 sriovs=[]
538 Mpbs_consumed = 0
539 else: #VF, SRIOV
540 del iface["switch_port"]
541 del iface["switch_dpid"]
542 del iface["type_"]
543 del iface["Mbps"]
544 sriovs.append(iface)
545
546 #delete internal field
547 del numa['id']
548 return 1, host
549 except (mdb.Error, AttributeError) as e:
550 r,c = self.format_error(e, "get_host", cmd)
551 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
552
553 def new_uuid(self):
554 max_retries=10
555 while max_retries>0:
556 uuid = str( myUuid.uuid1() )
557 if self.check_uuid(uuid)[0] == 0:
558 return uuid
559 max_retries-=1
560 return uuid
561
562 def check_uuid(self, uuid):
563 '''check in the database if this uuid is already present'''
564 try:
565 cmd = "SELECT * FROM uuids where uuid='" + str(uuid) + "'"
566 with self.con:
567 self.cur = self.con.cursor(mdb.cursors.DictCursor)
568 self.logger.debug(cmd)
569 self.cur.execute(cmd)
570 rows = self.cur.fetchall()
571 return self.cur.rowcount, rows
572 except (mdb.Error, AttributeError) as e:
573 return self.format_error(e, "check_uuid", cmd)
574
575 def __get_next_ids(self):
576 '''get next auto increment index of all table in the database'''
577 self.cur.execute("SELECT table_name,AUTO_INCREMENT FROM information_schema.tables WHERE AUTO_INCREMENT IS NOT NULL AND table_schema = DATABASE()")
578 rows = self.cur.fetchall()
579 return self.cur.rowcount, dict(rows)
580
581 def edit_host(self, host_id, host_dict):
582 #get next port index
583 for retry_ in range(0,2):
584 cmd=""
585 try:
586 with self.con:
587 self.cur = self.con.cursor()
588
589 #update table host
590 numa_list = host_dict.pop('numas', () )
591 if host_dict:
592 self._update_rows_internal("hosts", host_dict, {"uuid": host_id})
593
594 where = {"host_id": host_id}
595 for numa_dict in numa_list:
596 where["numa_socket"] = str(numa_dict.pop('numa_socket'))
597 interface_list = numa_dict.pop('interfaces', () )
598 if numa_dict:
599 self._update_rows_internal("numas", numa_dict, where)
600 for interface in interface_list:
601 source_name = str(interface.pop("source_name") )
602 if interface:
603 #get interface id from resources_port
604 cmd= "SELECT rp.id as id FROM resources_port as rp join numas as n on n.id=rp.numa_id join hosts as h on h.uuid=n.host_id " +\
605 "WHERE host_id='%s' and rp.source_name='%s'" %(host_id, source_name)
606 self.logger.debug(cmd)
607 self.cur.execute(cmd)
608 row = self.cur.fetchone()
609 if self.cur.rowcount<=0:
610 return -HTTP_Bad_Request, "Interface source_name='%s' from numa_socket='%s' not found" % (source_name, str(where["numa_socket"]))
611 interface_id = row[0]
612 self._update_rows_internal("resources_port", interface, {"root_id": interface_id})
613 return self.get_host(host_id)
614 except (mdb.Error, AttributeError) as e:
615 r,c = self.format_error(e, "edit_host", cmd)
616 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
617
618 def new_host(self, host_dict):
619 #get next port index
620 for retry_ in range(0,2):
621 cmd=""
622 try:
623 with self.con:
624 self.cur = self.con.cursor()
625
626 result, next_ids = self.__get_next_ids()
627 #print "next_ids: " + str(next_ids)
628 if result <= 0: return result, "Internal DataBase error getting next id of tables"
629
630 #create uuid if not provided
631 if 'uuid' not in host_dict:
632 uuid = host_dict['uuid'] = str(myUuid.uuid1()) # create_uuid
633 else: #check uuid is valid
634 uuid = str(host_dict['uuid'])
635 # result, data = self.check_uuid(uuid)
636 # if (result == 1):
637 # return -1, "UUID '%s' already in use" % uuid
638
639 #inserting new uuid
640 cmd = "INSERT INTO uuids (uuid, used_at) VALUES ('%s','hosts')" % uuid
641 self.logger.debug(cmd)
642 result = self.cur.execute(cmd)
643
644 #insert in table host
645 numa_list = host_dict.pop('numas', [])
646 #get nonhupages and nonisolated cpus
647 host_dict['RAM']=0
648 host_dict['cpus']=0
649 for numa in numa_list:
650 mem_numa = numa.get('memory', 0) - numa.get('hugepages',0)
651 if mem_numa>0:
652 host_dict['RAM'] += mem_numa
653 for core in numa.get("cores", []):
654 if "status" in core and core["status"]=="noteligible":
655 host_dict['cpus']+=1
656 host_dict['RAM']*=1024 # from GB to MB
657
658 keys = ",".join(host_dict.keys())
659 values = ",".join( map(lambda x: "Null" if x is None else "'"+str(x)+"'", host_dict.values() ) )
660 cmd = "INSERT INTO hosts (" + keys + ") VALUES (" + values + ")"
661 self.logger.debug(cmd)
662 result = self.cur.execute(cmd)
663 #if result != 1: return -1, "Database Error while inserting at hosts table"
664
665 #insert numas
666 nb_numas = nb_cores = nb_ifaces = 0
667 for numa_dict in numa_list:
668 nb_numas += 1
669 interface_list = numa_dict.pop('interfaces', [])
670 core_list = numa_dict.pop('cores', [])
671 numa_dict['id'] = next_ids['numas']; next_ids['numas'] += 1
672 numa_dict['host_id'] = uuid
673 keys = ",".join(numa_dict.keys())
674 values = ",".join( map(lambda x: "Null" if x is None else "'"+str(x)+"'", numa_dict.values() ) )
675 cmd = "INSERT INTO numas (" + keys + ") VALUES (" + values + ")"
676 self.logger.debug(cmd)
677 result = self.cur.execute(cmd)
678
679 #insert cores
680 for core_dict in core_list:
681 nb_cores += 1
682 core_dict['numa_id'] = numa_dict['id']
683 keys = ",".join(core_dict.keys())
684 values = ",".join( map(lambda x: "Null" if x is None else "'"+str(x)+"'", core_dict.values() ) )
685 cmd = "INSERT INTO resources_core (" + keys + ") VALUES (" + values + ")"
686 self.logger.debug(cmd)
687 result = self.cur.execute(cmd)
688
689 #insert ports
690 for port_dict in interface_list:
691 nb_ifaces += 1
692 sriov_list = port_dict.pop('sriovs', [])
693 port_dict['numa_id'] = numa_dict['id']
694 port_dict['id'] = port_dict['root_id'] = next_ids['resources_port']
695 next_ids['resources_port'] += 1
696 switch_port = port_dict.get('switch_port', None)
697 switch_dpid = port_dict.get('switch_dpid', None)
698 keys = ",".join(port_dict.keys())
699 values = ",".join( map(lambda x: "Null" if x is None else "'"+str(x)+"'", port_dict.values() ) )
700 cmd = "INSERT INTO resources_port (" + keys + ") VALUES (" + values + ")"
701 self.logger.debug(cmd)
702 result = self.cur.execute(cmd)
703
704 #insert sriovs into port table
705 for sriov_dict in sriov_list:
706 sriov_dict['switch_port'] = switch_port
707 sriov_dict['switch_dpid'] = switch_dpid
708 sriov_dict['numa_id'] = port_dict['numa_id']
709 sriov_dict['Mbps'] = port_dict['Mbps']
710 sriov_dict['root_id'] = port_dict['id']
711 sriov_dict['id'] = next_ids['resources_port']
712 if "vlan" in sriov_dict:
713 del sriov_dict["vlan"]
714 next_ids['resources_port'] += 1
715 keys = ",".join(sriov_dict.keys())
716 values = ",".join( map(lambda x: "Null" if x is None else "'"+str(x)+"'", sriov_dict.values() ) )
717 cmd = "INSERT INTO resources_port (" + keys + ") VALUES (" + values + ")"
718 self.logger.debug(cmd)
719 result = self.cur.execute(cmd)
720
721 #inserting new log
722 #cmd = "INSERT INTO logs (related,level,uuid,description) VALUES ('hosts','debug','%s','new host: %d numas, %d theads, %d ifaces')" % (uuid, nb_numas, nb_cores, nb_ifaces)
723 #self.logger.debug(cmd)
724 #result = self.cur.execute(cmd)
725
726 #inseted ok
727 with self.con:
728 self.cur = self.con.cursor()
729 self.logger.debug("callproc('UpdateSwitchPort', () )")
730 self.cur.callproc('UpdateSwitchPort', () )
731
732 self.logger.debug("getting host '%s'",str(host_dict['uuid']))
733 return self.get_host(host_dict['uuid'])
734 except (mdb.Error, AttributeError) as e:
735 r,c = self.format_error(e, "new_host", cmd)
736 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
737
738 def new_flavor(self, flavor_dict, tenant_id ):
739 '''Add new flavor into the database. Create uuid if not provided
740 Atributes
741 flavor_dict: flavor dictionary with the key: value to insert. Must be valid flavors columns
742 tenant_id: if not 'any', it matches this flavor/tenant inserting at tenants_flavors table
743 Return: (result, data) where result can be
744 negative: error at inserting. data contain text
745 1, inserted, data contain inserted uuid flavor
746 '''
747 for retry_ in range(0,2):
748 cmd=""
749 try:
750 with self.con:
751 self.cur = self.con.cursor()
752
753 #create uuid if not provided
754 if 'uuid' not in flavor_dict:
755 uuid = flavor_dict['uuid'] = str(myUuid.uuid1()) # create_uuid
756 else: #check uuid is valid
757 uuid = str(flavor_dict['uuid'])
758 # result, data = self.check_uuid(uuid)
759 # if (result == 1):
760 # return -1, "UUID '%s' already in use" % uuid
761
762 #inserting new uuid
763 cmd = "INSERT INTO uuids (uuid, used_at) VALUES ('%s','flavors')" % uuid
764 self.logger.debug(cmd)
765 self.cur.execute(cmd)
766
767 #insert in table flavor
768 keys = ",".join(flavor_dict.keys())
769 values = ",".join( map(lambda x: "Null" if x is None else "'"+str(x)+"'", flavor_dict.values() ) )
770 cmd = "INSERT INTO flavors (" + keys + ") VALUES (" + values + ")"
771 self.logger.debug(cmd)
772 self.cur.execute(cmd)
773 #if result != 1: return -1, "Database Error while inserting at flavors table"
774
775 #insert tenants_flavors
776 if tenant_id != 'any':
777 cmd = "INSERT INTO tenants_flavors (tenant_id,flavor_id) VALUES ('%s','%s')" % (tenant_id, uuid)
778 self.logger.debug(cmd)
779 self.cur.execute(cmd)
780
781 #inserting new log
782 #del flavor_dict['uuid']
783 #if 'extended' in flavor_dict: del flavor_dict['extended'] #remove two many information
784 #cmd = "INSERT INTO logs (related,level,uuid, tenant_id, description) VALUES ('flavors','debug','%s','%s',\"new flavor: %s\")" \
785 # % (uuid, tenant_id, str(flavor_dict))
786 #self.logger.debug(cmd)
787 #self.cur.execute(cmd)
788
789 #inseted ok
790 return 1, uuid
791 except (mdb.Error, AttributeError) as e:
792 r,c = self.format_error(e, "new_flavor", cmd, "update", tenant_id)
793 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
794
795 def new_image(self, image_dict, tenant_id):
796 '''Add new image into the database. Create uuid if not provided
797 Atributes
798 image_dict: image dictionary with the key: value to insert. Must be valid images columns
799 tenant_id: if not 'any', it matches this image/tenant inserting at tenants_images table
800 Return: (result, data) where result can be
801 negative: error at inserting. data contain text
802 1, inserted, data contain inserted uuid image
803 '''
804 for retry_ in range(0,2):
805 cmd=""
806 try:
807 with self.con:
808 self.cur = self.con.cursor()
809
810 #create uuid if not provided
811 if 'uuid' not in image_dict:
812 uuid = image_dict['uuid'] = str(myUuid.uuid1()) # create_uuid
813 else: #check uuid is valid
814 uuid = str(image_dict['uuid'])
815 # result, data = self.check_uuid(uuid)
816 # if (result == 1):
817 # return -1, "UUID '%s' already in use" % uuid
818
819 #inserting new uuid
820 cmd = "INSERT INTO uuids (uuid, used_at) VALUES ('%s','images')" % uuid
821 self.logger.debug(cmd)
822 self.cur.execute(cmd)
823
824 #insert in table image
825 keys = ",".join(image_dict.keys())
826 values = ",".join( map(lambda x: "Null" if x is None else "'"+str(x)+"'", image_dict.values() ) )
827 cmd = "INSERT INTO images (" + keys + ") VALUES (" + values + ")"
828 self.logger.debug(cmd)
829 self.cur.execute(cmd)
830 #if result != 1: return -1, "Database Error while inserting at images table"
831
832 #insert tenants_images
833 if tenant_id != 'any':
834 cmd = "INSERT INTO tenants_images (tenant_id,image_id) VALUES ('%s','%s')" % (tenant_id, uuid)
835 self.logger.debug(cmd)
836 self.cur.execute(cmd)
837
838 ##inserting new log
839 #cmd = "INSERT INTO logs (related,level,uuid, tenant_id, description) VALUES ('images','debug','%s','%s',\"new image: %s path: %s\")" % (uuid, tenant_id, image_dict['name'], image_dict['path'])
840 #self.logger.debug(cmd)
841 #self.cur.execute(cmd)
842
843 #inseted ok
844 return 1, uuid
845 except (mdb.Error, AttributeError) as e:
846 r,c = self.format_error(e, "new_image", cmd, "update", tenant_id)
847 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
848
849 def delete_image_flavor(self, item_type, item_id, tenant_id):
850 '''deletes an image or flavor from database
851 item_type must be a 'image' or 'flavor'
852 item_id is the uuid
853 tenant_id is the asociated tenant, can be 'any' with means all
854 If tenan_id is not any, it deletes from tenants_images/flavors,
855 which means this image/flavor is used by this tenant, and if success,
856 it tries to delete from images/flavors in case this is not public,
857 that only will success if image is private and not used by other tenants
858 If tenant_id is any, it tries to delete from both tables at the same transaction
859 so that image/flavor is completely deleted from all tenants or nothing
860 '''
861 for retry_ in range(0,2):
862 deleted = -1
863 deleted_item = -1
864 result = (-HTTP_Internal_Server_Error, "internal error")
865 cmd=""
866 try:
867 with self.con:
868 self.cur = self.con.cursor()
869 cmd = "DELETE FROM tenants_%ss WHERE %s_id = '%s'" % (item_type, item_type, item_id)
870 if tenant_id != 'any':
871 cmd += " AND tenant_id = '%s'" % tenant_id
872 self.logger.debug(cmd)
873 self.cur.execute(cmd)
874 deleted = self.cur.rowcount
875 if tenant_id == 'any': #delete from images/flavors in the SAME transaction
876 cmd = "DELETE FROM %ss WHERE uuid = '%s'" % (item_type, item_id)
877 self.logger.debug(cmd)
878 self.cur.execute(cmd)
879 deleted = self.cur.rowcount
880 if deleted>=1:
881 #delete uuid
882 cmd = "DELETE FROM uuids WHERE uuid = '%s'" % item_id
883 self.logger.debug(cmd)
884 self.cur.execute(cmd)
885 ##inserting new log
886 #cmd = "INSERT INTO logs (related,level,uuid,tenant_id,description) \
887 # VALUES ('%ss','debug','%s','%s','delete %s completely')" % \
888 # (item_type, item_id, tenant_id, item_type)
889 #self.logger.debug(cmd)
890 #self.cur.execute(cmd)
891 return deleted, "%s '%s' completely deleted" % (item_type, item_id)
892 return 0, "%s '%s' not found" % (item_type, item_id)
893
894 if deleted == 1:
895 ##inserting new log
896 #cmd = "INSERT INTO logs (related,level,uuid,tenant_id,description) \
897 # VALUES ('%ss','debug','%s','%s','delete %s reference for this tenant')" % \
898 # (item_type, item_id, tenant_id, item_type)
899 #self.logger.debug(cmd)
900 #self.cur.execute(cmd)
901
902 #commit transaction
903 self.cur.close()
904 #if tenant!=any delete from images/flavors in OTHER transaction. If fails is because dependencies so that not return error
905 if deleted==1:
906 with self.con:
907 self.cur = self.con.cursor()
908
909 #delete image/flavor if not public
910 cmd = "DELETE FROM %ss WHERE uuid = '%s' AND public = 'no'" % (item_type, item_id)
911 self.logger.debug(cmd)
912 self.cur.execute(cmd)
913 deleted_item = self.cur.rowcount
914 if deleted_item == 1:
915 #delete uuid
916 cmd = "DELETE FROM uuids WHERE uuid = '%s'" % item_id
917 self.logger.debug(cmd)
918 self.cur.execute(cmd)
919 ##inserting new log
920 #cmd = "INSERT INTO logs (related,level,uuid,tenant_id,description) \
921 # VALUES ('%ss','debug','%s','%s','delete %s completely')" % \
922 # (item_type, item_id, tenant_id, item_type)
923 #self.logger.debug(cmd)
924 #self.cur.execute(cmd)
925 except (mdb.Error, AttributeError) as e:
926 #print "delete_%s DB Exception %d: %s" % (item_type, e.args[0], e.args[1])
927 if deleted <0:
928 result = self.format_error(e, "delete_"+item_type, cmd, "delete", "servers")
929 finally:
930 if deleted==1:
931 return 1, "%s '%s' from tenant '%s' %sdeleted" % \
932 (item_type, item_id, tenant_id, "completely " if deleted_item==1 else "")
933 elif deleted==0:
934 return 0, "%s '%s' from tenant '%s' not found" % (item_type, item_id, tenant_id)
935 else:
936 if result[0]!=-HTTP_Request_Timeout or retry_==1: return result
937
938 def delete_row(self, table, uuid):
939 for retry_ in range(0,2):
940 cmd=""
941 try:
942 with self.con:
943 #delete host
944 self.cur = self.con.cursor()
945 cmd = "DELETE FROM %s WHERE uuid = '%s'" % (table, uuid)
946 self.logger.debug(cmd)
947 self.cur.execute(cmd)
948 deleted = self.cur.rowcount
949 if deleted == 1:
950 #delete uuid
951 if table == 'tenants': tenant_str=uuid
952 else: tenant_str='Null'
953 self.cur = self.con.cursor()
954 cmd = "DELETE FROM uuids WHERE uuid = '%s'" % uuid
955 self.logger.debug(cmd)
956 self.cur.execute(cmd)
957 ##inserting new log
958 #cmd = "INSERT INTO logs (related,level,uuid,tenant_id,description) VALUES ('%s','debug','%s','%s','delete %s')" % (table, uuid, tenant_str, table[:-1])
959 #self.logger.debug(cmd)
960 #self.cur.execute(cmd)
961 return deleted, table[:-1] + " '%s' %s" %(uuid, "deleted" if deleted==1 else "not found")
962 except (mdb.Error, AttributeError) as e:
963 r,c = self.format_error(e, "delete_row", cmd, "delete", 'instances' if table=='hosts' or table=='tenants' else 'dependencies')
964 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
965
966 def delete_row_by_key(self, table, key, value):
967 for retry_ in range(0,2):
968 cmd=""
969 try:
970 with self.con:
971 #delete host
972 self.cur = self.con.cursor()
973 cmd = "DELETE FROM %s" % (table)
974 if key!=None:
975 if value!=None:
976 cmd += " WHERE %s = '%s'" % (key, value)
977 else:
978 cmd += " WHERE %s is null" % (key)
979 else: #delete all
980 pass
981 self.logger.debug(cmd)
982 self.cur.execute(cmd)
983 deleted = self.cur.rowcount
984 if deleted < 1:
985 return -1, 'Not found'
986 #delete uuid
987 return 0, deleted
988 except (mdb.Error, AttributeError) as e:
989 r,c = self.format_error(e, "delete_row_by_key", cmd, "delete", 'instances' if table=='hosts' or table=='tenants' else 'dependencies')
990 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
991
992 def delete_row_by_dict(self, **sql_dict):
993 ''' Deletes rows from a table.
994 Attribute sql_dir: dictionary with the following key: value
995 'FROM': string of table name (Mandatory)
996 'WHERE': dict of key:values, translated to key=value AND ... (Optional)
997 'WHERE_NOT': dict of key:values, translated to key<>value AND ... (Optional)
998 'WHERE_NOTNULL': (list or tuple of items that must not be null in a where ... (Optional)
999 'LIMIT': limit of number of rows (Optional)
1000 Return: the (number of items deleted, descriptive test) if ok; (negative, descriptive text) if error
1001 '''
1002 #print sql_dict
1003 from_ = "FROM " + str(sql_dict['FROM'])
1004 #print 'from_', from_
1005 if 'WHERE' in sql_dict and len(sql_dict['WHERE']) > 0:
1006 w=sql_dict['WHERE']
1007 where_ = "WHERE " + " AND ".join(map( lambda x: str(x) + (" is Null" if w[x] is None else "='"+str(w[x])+"'"), w.keys()) )
1008 else: where_ = ""
1009 if 'WHERE_NOT' in sql_dict and len(sql_dict['WHERE_NOT']) > 0:
1010 w=sql_dict['WHERE_NOT']
1011 where_2 = " AND ".join(map( lambda x: str(x) + (" is not Null" if w[x] is None else "<>'"+str(w[x])+"'"), w.keys()) )
1012 if len(where_)==0: where_ = "WHERE " + where_2
1013 else: where_ = where_ + " AND " + where_2
1014 if 'WHERE_NOTNULL' in sql_dict and len(sql_dict['WHERE_NOTNULL']) > 0:
1015 w=sql_dict['WHERE_NOTNULL']
1016 where_2 = " AND ".join(map( lambda x: str(x) + " is not Null", w) )
1017 if len(where_)==0: where_ = "WHERE " + where_2
1018 else: where_ = where_ + " AND " + where_2
1019 #print 'where_', where_
1020 limit_ = "LIMIT " + str(sql_dict['LIMIT']) if 'LIMIT' in sql_dict else ""
1021 #print 'limit_', limit_
1022 cmd = " ".join( ("DELETE", from_, where_, limit_) )
1023 self.logger.debug(cmd)
1024 for retry_ in range(0,2):
1025 try:
1026 with self.con:
1027 #delete host
1028 self.cur = self.con.cursor()
1029 self.cur.execute(cmd)
1030 deleted = self.cur.rowcount
1031 return deleted, "%d deleted from %s" % (deleted, sql_dict['FROM'][:-1] )
1032 except (mdb.Error, AttributeError) as e:
1033 r,c = self.format_error(e, "delete_row_by_dict", cmd, "delete", 'dependencies')
1034 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
1035
1036
1037 def get_instance(self, instance_id):
1038 for retry_ in range(0,2):
1039 cmd=""
1040 try:
1041 with self.con:
1042 self.cur = self.con.cursor(mdb.cursors.DictCursor)
1043 #get INSTANCE
1044 cmd = "SELECT uuid, name, description, progress, host_id, flavor_id, image_id, status, last_error, "\
1045 "tenant_id, ram, vcpus, created_at FROM instances WHERE uuid='{}'".format(instance_id)
1046 self.logger.debug(cmd)
1047 self.cur.execute(cmd)
1048 if self.cur.rowcount == 0 : return 0, "instance '" + str(instance_id) +"'not found."
1049 instance = self.cur.fetchone()
1050 #get networks
1051 cmd = "SELECT uuid as iface_id, net_id, mac as mac_address, ip_address, name, Mbps as bandwidth, "\
1052 "vpci, model FROM ports WHERE (type='instance:bridge' or type='instance:ovs') AND "\
1053 "instance_id= '{}'".format(instance_id)
1054 self.logger.debug(cmd)
1055 self.cur.execute(cmd)
1056 if self.cur.rowcount > 0 :
1057 instance['networks'] = self.cur.fetchall()
1058
1059 #get extended
1060 extended = {}
1061 #get devices
1062 cmd = "SELECT type, vpci, image_id, xml,dev FROM instance_devices WHERE instance_id = '%s' " % str(instance_id)
1063 self.logger.debug(cmd)
1064 self.cur.execute(cmd)
1065 if self.cur.rowcount > 0 :
1066 extended['devices'] = self.cur.fetchall()
1067 #get numas
1068 numas = []
1069 cmd = "SELECT id, numa_socket as source FROM numas WHERE host_id = '" + str(instance['host_id']) + "'"
1070 self.logger.debug(cmd)
1071 self.cur.execute(cmd)
1072 host_numas = self.cur.fetchall()
1073 #print 'host_numas', host_numas
1074 for k in host_numas:
1075 numa_id = str(k['id'])
1076 numa_dict ={}
1077 #get memory
1078 cmd = "SELECT consumed FROM resources_mem WHERE instance_id = '%s' AND numa_id = '%s'" % ( instance_id, numa_id)
1079 self.logger.debug(cmd)
1080 self.cur.execute(cmd)
1081 if self.cur.rowcount > 0:
1082 mem_dict = self.cur.fetchone()
1083 numa_dict['memory'] = mem_dict['consumed']
1084 #get full cores
1085 cursor2 = self.con.cursor()
1086 cmd = "SELECT core_id, paired, MIN(v_thread_id) as v1, MAX(v_thread_id) as v2, COUNT(instance_id) as nb, MIN(thread_id) as t1, MAX(thread_id) as t2 FROM resources_core WHERE instance_id = '%s' AND numa_id = '%s' GROUP BY core_id,paired" % ( str(instance_id), numa_id)
1087 self.logger.debug(cmd)
1088 cursor2.execute(cmd)
1089 core_list = []; core_source = []
1090 paired_list = []; paired_source = []
1091 thread_list = []; thread_source = []
1092 if cursor2.rowcount > 0:
1093 cores = cursor2.fetchall()
1094 for core in cores:
1095 if core[4] == 2: #number of used threads from core
1096 if core[3] == core[2]: #only one thread asigned to VM, so completely core
1097 core_list.append(core[2])
1098 core_source.append(core[5])
1099 elif core[1] == 'Y':
1100 paired_list.append(core[2:4])
1101 paired_source.append(core[5:7])
1102 else:
1103 thread_list.extend(core[2:4])
1104 thread_source.extend(core[5:7])
1105
1106 else:
1107 thread_list.append(core[2])
1108 thread_source.append(core[5])
1109 if len(core_list) > 0:
1110 numa_dict['cores'] = len(core_list)
1111 numa_dict['cores-id'] = core_list
1112 numa_dict['cores-source'] = core_source
1113 if len(paired_list) > 0:
1114 numa_dict['paired-threads'] = len(paired_list)
1115 numa_dict['paired-threads-id'] = paired_list
1116 numa_dict['paired-threads-source'] = paired_source
1117 if len(thread_list) > 0:
1118 numa_dict['threads'] = len(thread_list)
1119 numa_dict['threads-id'] = thread_list
1120 numa_dict['threads-source'] = thread_source
1121
1122 #get dedicated ports and SRIOV
1123 cmd = "SELECT port_id as iface_id, p.vlan as vlan, p.mac as mac_address, net_id, if(model='PF',\
1124 'yes',if(model='VF','no','yes:sriov')) as dedicated, rp.Mbps as bandwidth, name, vpci, \
1125 pci as source \
1126 FROM resources_port as rp join ports as p on port_id=uuid WHERE p.instance_id = '%s' AND numa_id = '%s' and p.type='instance:data'" % (instance_id, numa_id)
1127 self.logger.debug(cmd)
1128 self.cur.execute(cmd)
1129 if self.cur.rowcount > 0:
1130 numa_dict['interfaces'] = self.cur.fetchall()
1131 #print 'interfaces', numa_dict
1132
1133 if len(numa_dict) > 0 :
1134 numa_dict['source'] = k['source'] #numa socket
1135 numas.append(numa_dict)
1136
1137 if len(numas) > 0 : extended['numas'] = numas
1138 if len(extended) > 0 : instance['extended'] = extended
1139 af.DeleteNone(instance)
1140 return 1, instance
1141 except (mdb.Error, AttributeError) as e:
1142 r,c = self.format_error(e, "get_instance", cmd)
1143 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
1144
1145 def get_numas(self, requirements, prefered_host_id=None, only_of_ports=True):
1146 '''Obtain a valid NUMA/HOST for deployment a VM
1147 requirements: contain requirement regarding:
1148 requirements['ram']: Non huge page memory in MB; 0 to skip
1149 requirements['vcpus']: Non isolated cpus; 0 to skip
1150 requirements['numa']: Requiremets to be fixed in ONE Numa node
1151 requirements['numa']['memory']: Huge page memory in GB at ; 0 for any
1152 requirements['numa']['proc_req_type']: Type of processor, cores or threads
1153 requirements['numa']['proc_req_nb']: Number of isolated cpus
1154 requirements['numa']['port_list']: Physical NIC ports list ; [] for any
1155 requirements['numa']['sriov_list']: Virtual function NIC ports list ; [] for any
1156 prefered_host_id: if not None return this host if it match
1157 only_of_ports: if True only those ports conected to the openflow (of) are valid,
1158 that is, with switch_port information filled; if False, all NIC ports are valid.
1159 Return a valid numa and host
1160 '''
1161
1162 for retry_ in range(0,2):
1163 cmd=""
1164 try:
1165 with self.con:
1166 # #Find numas of prefered host
1167 # prefered_numas = ()
1168 # if prefered_host_id != None:
1169 # self.cur = self.con.cursor()
1170 # self.cur.execute("SELECT id FROM numas WHERE host_id='%s'" + prefered_host_id)
1171 # prefered_numas = self.cur.fetchall()
1172 # self.cur.close()
1173
1174 #Find valid host for the ram and vcpus
1175 self.cur = self.con.cursor(mdb.cursors.DictCursor)
1176 cmd = "CALL GetHostByMemCpu(%s, %s)" % (str(requirements['ram']), str(requirements['vcpus']))
1177 self.logger.debug(cmd)
1178 self.cur.callproc('GetHostByMemCpu', (str(requirements['ram']), str(requirements['vcpus'])) )
1179 valid_hosts = self.cur.fetchall()
1180 self.cur.close()
1181 self.cur = self.con.cursor()
1182 match_found = False
1183 if len(valid_hosts)<=0:
1184 error_text = 'No room at data center. Cannot find a host with %s MB memory and %s cpus available' % (str(requirements['ram']), str(requirements['vcpus']))
1185 #self.logger.debug(error_text)
1186 return -1, error_text
1187
1188 #elif req_numa != None:
1189 #Find valid numa nodes for memory requirements
1190 self.cur = self.con.cursor(mdb.cursors.DictCursor)
1191 cmd = "CALL GetNumaByMemory(%s)" % str(requirements['numa']['memory'])
1192 self.logger.debug(cmd)
1193 self.cur.callproc('GetNumaByMemory', (requirements['numa']['memory'],) )
1194 valid_for_memory = self.cur.fetchall()
1195 self.cur.close()
1196 self.cur = self.con.cursor()
1197 if len(valid_for_memory)<=0:
1198 error_text = 'No room at data center. Cannot find a host with %s GB Hugepages memory available' % str(requirements['numa']['memory'])
1199 #self.logger.debug(error_text)
1200 return -1, error_text
1201
1202 #Find valid numa nodes for processor requirements
1203 self.cur = self.con.cursor(mdb.cursors.DictCursor)
1204 if requirements['numa']['proc_req_type'] == 'threads':
1205 cpu_requirement_text='cpu-threads'
1206 cmd = "CALL GetNumaByThread(%s)" % str(requirements['numa']['proc_req_nb'])
1207 self.logger.debug(cmd)
1208 self.cur.callproc('GetNumaByThread', (requirements['numa']['proc_req_nb'],) )
1209 else:
1210 cpu_requirement_text='cpu-cores'
1211 cmd = "CALL GetNumaByCore(%s)" % str(requirements['numa']['proc_req_nb'])
1212 self.logger.debug(cmd)
1213 self.cur.callproc('GetNumaByCore', (requirements['numa']['proc_req_nb'],) )
1214 valid_for_processor = self.cur.fetchall()
1215 self.cur.close()
1216 self.cur = self.con.cursor()
1217 if len(valid_for_processor)<=0:
1218 error_text = 'No room at data center. Cannot find a host with %s %s available' % (str(requirements['numa']['proc_req_nb']),cpu_requirement_text)
1219 #self.logger.debug(error_text)
1220 return -1, error_text
1221
1222 #Find the numa nodes that comply for memory and processor requirements
1223 #sorting from less to more memory capacity
1224 valid_numas = []
1225 for m_numa in valid_for_memory:
1226 numa_valid_for_processor = False
1227 for p_numa in valid_for_processor:
1228 if m_numa['numa_id'] == p_numa['numa_id']:
1229 numa_valid_for_processor = True
1230 break
1231 numa_valid_for_host = False
1232 prefered_numa = False
1233 for p_host in valid_hosts:
1234 if m_numa['host_id'] == p_host['uuid']:
1235 numa_valid_for_host = True
1236 if p_host['uuid'] == prefered_host_id:
1237 prefered_numa = True
1238 break
1239 if numa_valid_for_host and numa_valid_for_processor:
1240 if prefered_numa:
1241 valid_numas.insert(0, m_numa['numa_id'])
1242 else:
1243 valid_numas.append(m_numa['numa_id'])
1244 if len(valid_numas)<=0:
1245 error_text = 'No room at data center. Cannot find a host with %s MB hugepages memory and %s %s available in the same numa' %\
1246 (requirements['numa']['memory'], str(requirements['numa']['proc_req_nb']),cpu_requirement_text)
1247 #self.logger.debug(error_text)
1248 return -1, error_text
1249
1250 # print 'Valid numas list: '+str(valid_numas)
1251
1252 #Find valid numa nodes for interfaces requirements
1253 #For each valid numa we will obtain the number of available ports and check if these are valid
1254 match_found = False
1255 for numa_id in valid_numas:
1256 # print 'Checking '+str(numa_id)
1257 match_found = False
1258 self.cur = self.con.cursor(mdb.cursors.DictCursor)
1259 if only_of_ports:
1260 cmd="CALL GetAvailablePorts(%s)" % str(numa_id)
1261 self.logger.debug(cmd)
1262 self.cur.callproc('GetAvailablePorts', (numa_id,) )
1263 else:
1264 cmd="CALL GetAllAvailablePorts(%s)" % str(numa_id)
1265 self.logger.debug(cmd)
1266 self.cur.callproc('GetAllAvailablePorts', (numa_id,) )
1267 available_ports = self.cur.fetchall()
1268 self.cur.close()
1269 self.cur = self.con.cursor()
1270
1271 #Set/reset reservations
1272 for port in available_ports:
1273 port['Mbps_reserved'] = 0
1274 port['SRIOV_reserved'] = 0
1275
1276 #Try to allocate physical ports
1277 physical_ports_found = True
1278 for iface in requirements['numa']['port_list']:
1279 # print '\t\tchecking iface: '+str(iface)
1280 portFound = False
1281 for port in available_ports:
1282 # print '\t\t\tfor port: '+str(port)
1283 #If the port is not empty continue
1284 if port['Mbps_free'] != port['Mbps'] or port['Mbps_reserved'] != 0:
1285 # print '\t\t\t\t Not empty port'
1286 continue;
1287 #If the port speed is not enough continue
1288 if port['Mbps'] < iface['bandwidth']:
1289 # print '\t\t\t\t Not enough speed'
1290 continue;
1291
1292 #Otherwise this is a valid port
1293 port['Mbps_reserved'] = port['Mbps']
1294 port['SRIOV_reserved'] = 0
1295 iface['port_id'] = port['port_id']
1296 iface['vlan'] = None
1297 iface['mac'] = port['mac']
1298 iface['switch_port'] = port['switch_port']
1299 # print '\t\t\t\t Dedicated port found '+str(port['port_id'])
1300 portFound = True
1301 break;
1302
1303 #if all ports have been checked and no match has been found
1304 #this is not a valid numa
1305 if not portFound:
1306 # print '\t\t\t\t\tAll ports have been checked and no match has been found for numa '+str(numa_id)+'\n\n'
1307 physical_ports_found = False
1308 break
1309
1310 #if there is no match continue checking the following numa
1311 if not physical_ports_found:
1312 continue
1313
1314 #Try to allocate SR-IOVs
1315 sriov_ports_found = True
1316 for iface in requirements['numa']['sriov_list']:
1317 # print '\t\tchecking iface: '+str(iface)
1318 portFound = False
1319 for port in available_ports:
1320 # print '\t\t\tfor port: '+str(port)
1321 #If there are not available SR-IOVs continue
1322 if port['availableSRIOV'] - port['SRIOV_reserved'] <= 0:
1323 # print '\t\t\t\t Not enough SR-IOV'
1324 continue;
1325 #If the port free speed is not enough continue
1326 if port['Mbps_free'] - port['Mbps_reserved'] < iface['bandwidth']:
1327 # print '\t\t\t\t Not enough speed'
1328 continue;
1329
1330 #Otherwise this is a valid port
1331 port['Mbps_reserved'] += iface['bandwidth']
1332 port['SRIOV_reserved'] += 1
1333 # print '\t\t\t\t SR-IOV found '+str(port['port_id'])
1334 iface['port_id'] = port['port_id']
1335 iface['vlan'] = None
1336 iface['mac'] = port['mac']
1337 iface['switch_port'] = port['switch_port']
1338 portFound = True
1339 break;
1340
1341 #if all ports have been checked and no match has been found
1342 #this is not a valid numa
1343 if not portFound:
1344 # print '\t\t\t\t\tAll ports have been checked and no match has been found for numa '+str(numa_id)+'\n\n'
1345 sriov_ports_found = False
1346 break
1347
1348 #if there is no match continue checking the following numa
1349 if not sriov_ports_found:
1350 continue
1351
1352
1353 if sriov_ports_found and physical_ports_found:
1354 match_found = True
1355 break
1356
1357 if not match_found:
1358 error_text = 'No room at data center. Cannot find a host with the required hugepages, vcpus and interfaces'
1359 #self.logger.debug(error_text)
1360 return -1, error_text
1361
1362 #self.logger.debug('Full match found in numa %s', str(numa_id))
1363
1364 for numa in valid_for_processor:
1365 if numa_id==numa['numa_id']:
1366 host_id=numa['host_id']
1367 break
1368 return 0, {'numa_id':numa_id, 'host_id': host_id, }
1369 except (mdb.Error, AttributeError) as e:
1370 r,c = self.format_error(e, "get_numas", cmd)
1371 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
1372
1373 def new_instance(self, instance_dict, nets, ports_to_free):
1374 for retry_ in range(0,2):
1375 cmd=""
1376 try:
1377 with self.con:
1378 self.cur = self.con.cursor()
1379
1380 #create uuid if not provided
1381 if 'uuid' not in instance_dict:
1382 uuid = instance_dict['uuid'] = str(myUuid.uuid1()) # create_uuid
1383 else: #check uuid is valid
1384 uuid = str(instance_dict['uuid'])
1385
1386
1387 #inserting new uuid
1388 cmd = "INSERT INTO uuids (uuid, root_uuid, used_at) VALUES ('%s','%s', 'instances')" % (uuid, uuid)
1389 self.logger.debug(cmd)
1390 self.cur.execute(cmd)
1391
1392 #insert in table instance
1393 extended = instance_dict.pop('extended', None);
1394 bridgedifaces = instance_dict.pop('bridged-ifaces', () );
1395
1396 keys = ",".join(instance_dict.keys())
1397 values = ",".join( map(lambda x: "Null" if x is None else "'"+str(x)+"'", instance_dict.values() ) )
1398 cmd = "INSERT INTO instances (" + keys + ") VALUES (" + values + ")"
1399 self.logger.debug(cmd)
1400 self.cur.execute(cmd)
1401 #if result != 1: return -1, "Database Error while inserting at instances table"
1402
1403 #insert resources
1404 nb_bridge_ifaces = nb_cores = nb_ifaces = nb_numas = 0
1405 #insert bridged_ifaces
1406
1407 for iface in bridgedifaces:
1408 #generate and insert a iface uuid
1409 if 'enable_dhcp' in iface and iface['enable_dhcp']:
1410 dhcp_first_ip = iface["dhcp_first_ip"]
1411 del iface["dhcp_first_ip"]
1412 dhcp_last_ip = iface["dhcp_last_ip"]
1413 del iface["dhcp_last_ip"]
1414 dhcp_cidr = iface["cidr"]
1415 del iface["cidr"]
1416 del iface["enable_dhcp"]
1417 used_dhcp_ips = self._get_dhcp_ip_used_list(iface["net_id"])
1418 iface["ip_address"] = self.get_free_ip_from_range(dhcp_first_ip, dhcp_last_ip,
1419 dhcp_cidr, used_dhcp_ips)
1420
1421 iface['uuid'] = str(myUuid.uuid1()) # create_uuid
1422 cmd = "INSERT INTO uuids (uuid, root_uuid, used_at) VALUES ('%s','%s', 'ports')" % (iface['uuid'], uuid)
1423 self.logger.debug(cmd)
1424 self.cur.execute(cmd)
1425 #insert iface
1426 iface['instance_id'] = uuid
1427 # iface['type'] = 'instance:bridge'
1428 if 'name' not in iface: iface['name']="br"+str(nb_bridge_ifaces)
1429 iface['Mbps']=iface.pop('bandwidth', None)
1430 if 'mac_address' not in iface:
1431 iface['mac'] = af.gen_random_mac()
1432 else:
1433 iface['mac'] = iface['mac_address']
1434 del iface['mac_address']
1435 #iface['mac']=iface.pop('mac_address', None) #for leaving mac generation to libvirt
1436 keys = ",".join(iface.keys())
1437 values = ",".join( map(lambda x: "Null" if x is None else "'"+str(x)+"'", iface.values() ) )
1438 cmd = "INSERT INTO ports (" + keys + ") VALUES (" + values + ")"
1439 self.logger.debug(cmd)
1440 self.cur.execute(cmd)
1441 nb_bridge_ifaces += 1
1442
1443 if extended is not None:
1444 if 'numas' not in extended or extended['numas'] is None: extended['numas'] = ()
1445 for numa in extended['numas']:
1446 nb_numas += 1
1447 #cores
1448 if 'cores' not in numa or numa['cores'] is None: numa['cores'] = ()
1449 for core in numa['cores']:
1450 nb_cores += 1
1451 cmd = "UPDATE resources_core SET instance_id='%s'%s%s WHERE id='%s'" \
1452 % (uuid, \
1453 (",v_thread_id='" + str(core['vthread']) + "'") if 'vthread' in core else '', \
1454 (",paired='" + core['paired'] + "'") if 'paired' in core else '', \
1455 core['id'] )
1456 self.logger.debug(cmd)
1457 self.cur.execute(cmd)
1458 #interfaces
1459 if 'interfaces' not in numa or numa['interfaces'] is None: numa['interfaces'] = ()
1460 for iface in numa['interfaces']:
1461 #generate and insert an uuid; iface[id]=iface_uuid; iface[uuid]= net_id
1462 iface['id'] = str(myUuid.uuid1()) # create_uuid
1463 cmd = "INSERT INTO uuids (uuid, root_uuid, used_at) VALUES ('%s','%s', 'ports')" % (iface['id'], uuid)
1464 self.logger.debug(cmd)
1465 self.cur.execute(cmd)
1466 nb_ifaces += 1
1467 mbps_=("'"+str(iface['Mbps_used'])+"'") if 'Mbps_used' in iface and iface['Mbps_used'] is not None else "Mbps"
1468 if iface["dedicated"]=="yes":
1469 iface_model="PF"
1470 elif iface["dedicated"]=="yes:sriov":
1471 iface_model="VFnotShared"
1472 elif iface["dedicated"]=="no":
1473 iface_model="VF"
1474 #else error
1475 INSERT=(iface['mac_address'], iface['switch_port'], iface.get('vlan',None), 'instance:data', iface['Mbps_used'], iface['id'],
1476 uuid, instance_dict['tenant_id'], iface.get('name',None), iface.get('vpci',None), iface.get('uuid',None), iface_model )
1477 cmd = "INSERT INTO ports (mac,switch_port,vlan,type,Mbps,uuid,instance_id,tenant_id,name,vpci,net_id, model) " + \
1478 " VALUES (" + ",".join(map(lambda x: 'Null' if x is None else "'"+str(x)+"'", INSERT )) + ")"
1479 self.logger.debug(cmd)
1480 self.cur.execute(cmd)
1481 if 'uuid' in iface:
1482 nets.append(iface['uuid'])
1483
1484 #discover if this port is not used by anyone
1485 cmd = "SELECT source_name, mac FROM ( SELECT root_id, count(instance_id) as used FROM resources_port" \
1486 " WHERE root_id=(SELECT root_id from resources_port WHERE id='%s')"\
1487 " GROUP BY root_id ) AS A JOIN resources_port as B ON A.root_id=B.id AND A.used=0" % iface['port_id']
1488 self.logger.debug(cmd)
1489 self.cur.execute(cmd)
1490 ports_to_free += self.cur.fetchall()
1491
1492 cmd = "UPDATE resources_port SET instance_id='%s', port_id='%s',Mbps_used=%s WHERE id='%s'" \
1493 % (uuid, iface['id'], mbps_, iface['port_id'])
1494 #if Mbps_used not suply, set the same value of 'Mpbs', that is the total
1495 self.logger.debug(cmd)
1496 self.cur.execute(cmd)
1497 #memory
1498 if 'memory' in numa and numa['memory'] is not None and numa['memory']>0:
1499 cmd = "INSERT INTO resources_mem (numa_id, instance_id, consumed) VALUES ('%s','%s','%s')" % (numa['numa_id'], uuid, numa['memory'])
1500 self.logger.debug(cmd)
1501 self.cur.execute(cmd)
1502 if 'devices' not in extended or extended['devices'] is None: extended['devices'] = ()
1503 for device in extended['devices']:
1504 if 'vpci' in device: vpci = "'" + device['vpci'] + "'"
1505 else: vpci = 'Null'
1506 if 'image_id' in device: image_id = "'" + device['image_id'] + "'"
1507 else: image_id = 'Null'
1508 if 'xml' in device: xml = "'" + device['xml'] + "'"
1509 else: xml = 'Null'
1510 if 'dev' in device: dev = "'" + device['dev'] + "'"
1511 else: dev = 'Null'
1512 cmd = "INSERT INTO instance_devices (type, instance_id, image_id, vpci, xml, dev) VALUES ('%s','%s', %s, %s, %s, %s)" % \
1513 (device['type'], uuid, image_id, vpci, xml, dev)
1514 self.logger.debug(cmd)
1515 self.cur.execute(cmd)
1516 ##inserting new log
1517 #cmd = "INSERT INTO logs (related,level,uuid,description) VALUES ('instances','debug','%s','new instance: %d numas, %d theads, %d ifaces %d bridge_ifaces')" % (uuid, nb_numas, nb_cores, nb_ifaces, nb_bridge_ifaces)
1518 #self.logger.debug(cmd)
1519 #self.cur.execute(cmd)
1520
1521 #inseted ok
1522 return 1, uuid
1523 except (mdb.Error, AttributeError) as e:
1524 r,c = self.format_error(e, "new_instance", cmd)
1525 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
1526
1527 def get_free_ip_from_range(self, first_ip, last_ip, cidr, ip_used_list):
1528 """
1529 Calculate a free IP from a range given
1530 :param first_ip: First dhcp ip range
1531 :param last_ip: Last dhcp ip range
1532 :param cidr: net cidr
1533 :param ip_used_list: contain all used ips to avoid ip collisions
1534 :return:
1535 """
1536
1537 ip_tools = IPNetwork(cidr)
1538 cidr_len = ip_tools.prefixlen
1539 ips = IPNetwork(first_ip + '/' + str(cidr_len))
1540 ip_used_list.append(str(ips[0])) # first ip
1541 ip_used_list.append(str(ips[1])) # gw ip
1542 ip_used_list.append(str(ips[-1])) # broadcast ip
1543 for vm_ip in ips:
1544 if str(vm_ip) not in ip_used_list:
1545 return vm_ip
1546
1547 return None
1548
1549 def _get_dhcp_ip_used_list(self, net_id):
1550 """
1551 REtreive from DB all ips already used by the dhcp server for a given net
1552 :param net_id:
1553 :return:
1554 """
1555 WHERE={'type': 'instance:ovs', 'net_id': net_id}
1556 for retry_ in range(0, 2):
1557 cmd = ""
1558 self.cur = self.con.cursor(mdb.cursors.DictCursor)
1559 select_ = "SELECT uuid, ip_address FROM ports "
1560
1561 if WHERE is None or len(WHERE) == 0:
1562 where_ = ""
1563 else:
1564 where_ = "WHERE " + " AND ".join(
1565 map(lambda x: str(x) + (" is Null" if WHERE[x] is None else "='" + str(WHERE[x]) + "'"),
1566 WHERE.keys()))
1567 limit_ = "LIMIT 100"
1568 cmd = " ".join((select_, where_, limit_))
1569 self.logger.debug(cmd)
1570 self.cur.execute(cmd)
1571 ports = self.cur.fetchall()
1572 ip_address_list = []
1573 for port in ports:
1574 ip_address_list.append(port['ip_address'])
1575
1576 return ip_address_list
1577
1578
1579 def delete_instance(self, instance_id, tenant_id, net_dataplane_list, ports_to_free, net_ovs_list, logcause="requested by http"):
1580 for retry_ in range(0,2):
1581 cmd=""
1582 try:
1583 with self.con:
1584 self.cur = self.con.cursor()
1585 #get INSTANCE
1586 cmd = "SELECT uuid FROM instances WHERE uuid='%s' AND tenant_id='%s'" % (instance_id, tenant_id)
1587 self.logger.debug(cmd)
1588 self.cur.execute(cmd)
1589 if self.cur.rowcount == 0 : return 0, "instance %s not found in tenant %s" % (instance_id, tenant_id)
1590
1591 #delete bridged ifaces, instace_devices, resources_mem; done by database: it is automatic by Database; FOREIGN KEY DELETE CASCADE
1592
1593 #get nets afected
1594 cmd = "SELECT DISTINCT net_id from ports WHERE instance_id = '%s' AND net_id is not Null AND type='instance:data'" % instance_id
1595 self.logger.debug(cmd)
1596 self.cur.execute(cmd)
1597 net_list__ = self.cur.fetchall()
1598 for net in net_list__:
1599 net_dataplane_list.append(net[0])
1600
1601 # get ovs manangement nets
1602 cmd = "SELECT DISTINCT net_id, vlan, ip_address, mac FROM ports WHERE instance_id='{}' AND net_id is not Null AND "\
1603 "type='instance:ovs'".format(instance_id)
1604 self.logger.debug(cmd)
1605 self.cur.execute(cmd)
1606 net_ovs_list += self.cur.fetchall()
1607
1608 #get dataplane interfaces releases by this VM; both PF and VF with no other VF
1609 cmd="SELECT source_name, mac FROM (SELECT root_id, count(instance_id) as used FROM resources_port WHERE instance_id='%s' GROUP BY root_id ) AS A" % instance_id \
1610 + " JOIN (SELECT root_id, count(instance_id) as used FROM resources_port GROUP BY root_id) AS B ON A.root_id=B.root_id AND A.used=B.used"\
1611 + " JOIN resources_port as C ON A.root_id=C.id"
1612 # cmd = "SELECT DISTINCT root_id FROM resources_port WHERE instance_id = '%s'" % instance_id
1613 self.logger.debug(cmd)
1614 self.cur.execute(cmd)
1615 ports_to_free += self.cur.fetchall()
1616
1617 #update resources port
1618 cmd = "UPDATE resources_port SET instance_id=Null, port_id=Null, Mbps_used='0' WHERE instance_id = '%s'" % instance_id
1619 self.logger.debug(cmd)
1620 self.cur.execute(cmd)
1621
1622 # #filter dataplane ports used by this VM that now are free
1623 # for port in ports_list__:
1624 # cmd = "SELECT mac, count(instance_id) FROM resources_port WHERE root_id = '%s'" % port[0]
1625 # self.logger.debug(cmd)
1626 # self.cur.execute(cmd)
1627 # mac_list__ = self.cur.fetchone()
1628 # if mac_list__ and mac_list__[1]==0:
1629 # ports_to_free.append(mac_list__[0])
1630
1631
1632 #update resources core
1633 cmd = "UPDATE resources_core SET instance_id=Null, v_thread_id=Null, paired='N' WHERE instance_id = '%s'" % instance_id
1634 self.logger.debug(cmd)
1635 self.cur.execute(cmd)
1636
1637 #delete all related uuids
1638 cmd = "DELETE FROM uuids WHERE root_uuid='%s'" % instance_id
1639 self.logger.debug(cmd)
1640 self.cur.execute(cmd)
1641
1642 ##insert log
1643 #cmd = "INSERT INTO logs (related,level,uuid,description) VALUES ('instances','debug','%s','delete instance %s')" % (instance_id, logcause)
1644 #self.logger.debug(cmd)
1645 #self.cur.execute(cmd)
1646
1647 #delete instance
1648 cmd = "DELETE FROM instances WHERE uuid='%s' AND tenant_id='%s'" % (instance_id, tenant_id)
1649 self.cur.execute(cmd)
1650 return 1, "instance %s from tenant %s DELETED" % (instance_id, tenant_id)
1651
1652 except (mdb.Error, AttributeError) as e:
1653 r,c = self.format_error(e, "delete_instance", cmd)
1654 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
1655
1656 def get_ports(self, WHERE):
1657 ''' Obtain ports using the WHERE filtering.
1658 Attributes:
1659 'where_': dict of key:values, translated to key=value AND ... (Optional)
1660 Return: a list with dictionarys at each row
1661 '''
1662 for retry_ in range(0,2):
1663 cmd=""
1664 try:
1665 with self.con:
1666
1667 self.cur = self.con.cursor(mdb.cursors.DictCursor)
1668 select_ = "SELECT uuid,'ACTIVE' as status,admin_state_up,name,net_id,\
1669 tenant_id,type,mac,vlan,switch_port,instance_id,Mbps FROM ports "
1670
1671 if WHERE is None or len(WHERE) == 0: where_ = ""
1672 else:
1673 where_ = "WHERE " + " AND ".join(map( lambda x: str(x) + (" is Null" if WHERE[x] is None else "='"+str(WHERE[x])+"'"), WHERE.keys()) )
1674 limit_ = "LIMIT 100"
1675 cmd = " ".join( (select_, where_, limit_) )
1676 # print "SELECT multiple de instance_ifaces, iface_uuid, external_ports" #print cmd
1677 self.logger.debug(cmd)
1678 self.cur.execute(cmd)
1679 ports = self.cur.fetchall()
1680 if self.cur.rowcount>0: af.DeleteNone(ports)
1681 return self.cur.rowcount, ports
1682 # return self.get_table(FROM=from_, SELECT=select_,WHERE=where_,LIMIT=100)
1683 except (mdb.Error, AttributeError) as e:
1684 r,c = self.format_error(e, "get_ports", cmd)
1685 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
1686
1687 def check_target_net(self, net_id, tenant_id, port_type):
1688 '''check if valid attachement of a port into a target net
1689 Attributes:
1690 net_id: target net uuid
1691 tenant_id: client where tenant belongs. Not used in this version
1692 port_type: string with the option 'instance:bridge', 'instance:data', 'external'
1693 Return:
1694 (0,net_dict) if ok, where net_dict contain 'uuid','type','vlan', ...
1695 (negative,string-error) if error
1696 '''
1697 for retry_ in range(0,2):
1698 cmd=""
1699 try:
1700 with self.con:
1701 self.cur = self.con.cursor(mdb.cursors.DictCursor)
1702 cmd = "SELECT * FROM nets WHERE uuid='%s'" % net_id
1703 self.logger.debug(cmd)
1704 self.cur.execute(cmd)
1705 if self.cur.rowcount == 0 : return -1, "network_id %s does not match any net" % net_id
1706 net = self.cur.fetchone()
1707 break
1708
1709 except (mdb.Error, AttributeError) as e:
1710 r,c = self.format_error(e, "check_target_net", cmd)
1711 if r!=-HTTP_Request_Timeout or retry_==1: return r,c
1712 #check permissions
1713 if tenant_id is not None and tenant_id is not "admin":
1714 if net['tenant_id']==tenant_id and net['shared']=='false':
1715 return -1, "needed admin privileges to attach to the net %s" % net_id
1716 #check types
1717 if (net['type'] in ('ptp','data') and port_type not in ('instance:data','external')) or \
1718 (net['type'] in ('bridge_data','bridge_man') and port_type not in ('instance:bridge', 'instance:ovs')):
1719 return -1, "Cannot attach a port of type %s into a net of type %s" % (port_type, net['type'])
1720 if net['type'] == 'ptp':
1721 #look how many
1722 nb_ports, data = self.get_ports( {'net_id':net_id} )
1723 if nb_ports<0:
1724 return -1, data
1725 else:
1726 if net['provider']:
1727 nb_ports +=1
1728 if nb_ports >=2:
1729 return -1, "net of type p2p already contain two ports attached. No room for another"
1730
1731 return 0, net
1732
1733 if __name__ == "__main__":
1734 print "Hello World"