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