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