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