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