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