fix typo in GetInterfaceName
[racktables-contribs] / rtapi / __init__.py
1 #!/usr/bin/python
2 #
3 # RTAPI
4 # Racktables API is simple python module providing some methods
5 # for monipulation with racktables objects.
6 #
7 # This utility is released under GPL v2
8 #
9 # Server Audit utility for Racktables Datacenter management project.
10 # Copyright (C) 2012 Robert Vojcik (robert@vojcik.net)
11 #
12 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; either version 2
15 # of the License, or (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #
26
27 '''PyRacktables
28 Simple python Class for manipulation with objects in racktables database.
29
30 For proper function, some methods need ipaddr module (https://pypi.python.org/pypi/ipaddr)
31 '''
32 __author__ = "Robert Vojcik (robert@vojcik.net)"
33 __version__ = "0.1.2"
34 __copyright__ = "OpenSource"
35 __license__ = "GPLv2"
36
37 __all__ = ["RTObject"]
38
39
40 import re
41 import ipaddr
42
43
44 class RTObject:
45 '''Ractables object. Require database object as argument. '''
46
47 # Init method
48 def __init__(self, dbobject):
49 '''Initialize Object'''
50 # Open configuration file
51 self.db = dbobject
52 self.dbresult = self.db.cursor()
53
54 # DATABASE methods
55 def db_query_one(self, sql):
56 '''SQL query function, return one row. Require sql query as parameter'''
57 self.dbresult.execute(sql)
58 return self.dbresult.fetchone()
59
60 def db_query_all(self, sql):
61 '''SQL query function, return all rows. Require sql query as parameter'''
62 self.dbresult.execute(sql)
63 return self.dbresult.fetchall()
64
65 def db_insert(self, sql):
66 '''SQL insert/update function. Require sql query as parameter'''
67 self.dbresult.execute(sql)
68 self.db.commit()
69
70 def db_fetch_lastid(self):
71 '''SQL function which return ID of last inserted row.'''
72 return self.dbresult.lastrowid
73
74 def ListObjects(self):
75 '''List all objects'''
76 sql = 'SELECT name FROM Object'
77 return "Found " + str(len(self.db_query_all(sql))) +" objects in database"
78
79 # Object methotds
80 def ObjectExistST(self,service_tag):
81 '''Check if object exist in database based on asset_no'''
82 sql = 'SELECT name FROM Object WHERE asset_no = \''+service_tag+'\''
83 if self.db_query_one(sql) == None:
84 return False
85 else:
86 return True
87
88 def ObjectExistName(self,name):
89 '''Check if object exist in database based on name'''
90 sql = 'select id from Object where name = \''+name+'\''
91 if self.db_query_one(sql) == None:
92 return False
93 else:
94 return True
95
96 def ObjectExistSTName(self,name,asset_no):
97 '''Check if object exist in database based on name'''
98 sql = "SELECT id FROM Object WHERE name = '%s' AND asset_no = '%s'" % (name,asset_no)
99 if self.db_query_one(sql) == None:
100 return False
101 else:
102 return True
103
104 def AddObject(self,name,server_type_id,asset_no,label):
105 '''Add new object to racktables'''
106 sql = "INSERT INTO Object (name,objtype_id,asset_no,label) VALUES ('%s',%d,'%s','%s')" % (name,server_type_id,asset_no,label)
107 self.db_insert(sql)
108
109 def UpdateObjectLabel(self,object_id,label):
110 '''Update label on object'''
111 sql = "UPDATE Object SET label = '%s' where id = %d" % (label, object_id)
112 self.db_insert(sql)
113
114 def UpdateObjectComment(self,object_id,comment):
115 '''Update comment on object'''
116 sql = "UPDATE Object SET comment = '%s' where id = %d" % (comment, object_id)
117 self.db_insert(sql)
118
119 def UpdateObjectName(self,object_id,name):
120 '''Update name on object'''
121 sql = "UPDATE Object SET name = '%s' where id = %d" % (name, object_id)
122 self.db_insert(sql)
123
124 def GetObjectName(self,object_id):
125 '''Translate Object ID to Object Name'''
126 #Get interface id
127 sql = "SELECT name FROM Object WHERE id = %d" % (object_id)
128 result = self.db_query_one(sql)
129 if result != None:
130 object_name = result[0]
131 else:
132 object_name = None
133
134 return object_name
135
136 def GetObjectLabel(self,object_id):
137 '''Get object label'''
138 #Get interface id
139 sql = "SELECT label FROM Object WHERE id = %d" % (object_id)
140 result = self.db_query_one(sql)
141 if result != None:
142 object_label = result[0]
143 else:
144 object_label = None
145
146 return object_label
147
148 def GetObjectComment(self,object_id):
149 '''Get object comment'''
150 #Get interface id
151 sql = "SELECT comment FROM Object WHERE id = %d" % (object_id)
152 result = self.db_query_one(sql)
153 if result != None:
154 object_comment = result[0]
155 else:
156 object_comment = None
157
158 return object_comment
159
160 def GetObjectId(self,name):
161 '''Translate Object name to object id'''
162 #Get interface id
163 sql = "SELECT id FROM Object WHERE name = '%s'" % (name)
164 result = self.db_query_one(sql)
165 if result != None:
166 object_id = result[0]
167 else:
168 object_id = None
169
170 return object_id
171
172 # Logging
173 def InsertLog(self,object_id,message):
174 '''Attach log message to specific object'''
175 sql = "INSERT INTO ObjectLog (object_id,user,date,content) VALUES (%d,'script',now(),'%s')" % (object_id, message)
176 self.db_insert(sql)
177
178 # Attrubute methods
179 def InsertAttribute(self,object_id,object_tid,attr_id,string_value,uint_value,name):
180 '''Add or Update object attribute.
181 Require 6 arguments: object_id, object_tid, attr_id, string_value, uint_value, name'''
182
183 # Check if attribute exist
184 sql = "SELECT string_value,uint_value FROM AttributeValue WHERE object_id = %d AND object_tid = %d AND attr_id = %d" % (object_id, object_tid, attr_id)
185 result = self.db_query_one(sql)
186
187 if result != None:
188 # Check if attribute value is same and determine attribute type
189 old_string_value = result[0]
190 old_uint_value = result[1]
191 same_flag = "no"
192 attribute_type = "None"
193
194 if old_string_value != None:
195 attribute_type = "string"
196 old_value = old_string_value
197 if old_string_value == string_value:
198 same_flag = "yes"
199 elif old_uint_value != None:
200 attribute_type = "uint"
201 old_value = old_uint_value
202 if old_uint_value == uint_value:
203 same_flag = "yes"
204
205 # If exist, update value
206 new_value = ''
207 if same_flag == "no":
208 if attribute_type == "string":
209 sql = "UPDATE AttributeValue SET string_value = '%s' WHERE object_id = %d AND attr_id = %d AND object_tid = %d" % (string_value, object_id, attr_id, object_tid)
210 new_value = string_value
211 if attribute_type == "uint":
212 sql = "UPDATE AttributeValue SET uint_value = %d WHERE object_id = %d AND attr_id = %d AND object_tid = %d" % (uint_value, object_id, attr_id, object_tid)
213 new_value = uint_value
214
215 self.db_insert(sql)
216
217 else:
218 # Attribute not exist, insert new
219 if string_value == "NULL":
220 sql = "INSERT INTO AttributeValue (object_id,object_tid,attr_id,uint_value) VALUES (%d,%d,%d,%d)" % (object_id,object_tid,attr_id,uint_value)
221 else:
222 sql = "INSERT INTO AttributeValue (object_id,object_tid,attr_id,string_value) VALUES (%d,%d,%d,'%s')" % (object_id,object_tid,attr_id,string_value)
223 self.db_insert(sql)
224
225 def GetAttributeId(self,searchstring):
226 '''Search racktables database and get attribud id based on search string as argument'''
227 sql = "SELECT id FROM Attribute WHERE name LIKE '%"+searchstring+"%'"
228
229 result = self.db_query_one(sql)
230
231 if result != None:
232 getted_id = result[0]
233 else:
234 getted_id = None
235
236 return getted_id
237
238 # Interfaces methods
239 def GetInterfaceName(self,object_id,interface_id):
240 '''Find name of specified interface. Required object_id and interface_id argument'''
241 #Get interface id
242 sql = "SELECT name FROM Port WHERE object_id = %d AND id = %d" % (object_id, interface_id)
243 result = self.db_query_one(sql)
244 if result != None:
245 port_name = result[0]
246 else:
247 port_name = None
248
249 return port_name
250
251 def GetInterfaceId(self,object_id,interface):
252 '''Find id of specified interface'''
253 #Get interface id
254 sql = "SELECT id,name FROM Port WHERE object_id = %d AND name = '%s'" % (object_id, interface)
255 result = self.db_query_one(sql)
256 if result != None:
257 port_id = result[0]
258 else:
259 port_id = None
260
261 return port_id
262
263 def UpdateNetworkInterface(self,object_id,interface):
264 '''Add network interfece to object if not exist'''
265
266 sql = "SELECT id,name FROM Port WHERE object_id = %d AND name = '%s'" % (object_id, interface)
267
268 result = self.db_query_one(sql)
269 if result == None:
270
271 sql = "INSERT INTO Port (object_id,name,iif_id,type) VALUES (%d,'%s',1,24)" % (object_id,interface)
272 self.db_insert(sql)
273 port_id = self.db_fetch_lastid()
274
275 else:
276 port_id = result[0]
277
278
279 return port_id
280
281 def LinkNetworkInterface(self,object_id,interface,switch_name,interface_switch):
282 '''Link two devices togetger'''
283 #Get interface id
284 port_id = self.GetInterfaceId(object_id,interface)
285 if port_id != None:
286 #Get switch object ID
287 switch_object_id = self.GetObjectId(switch_name)
288 if switch_object_id != None:
289 switch_port_id = self.GetInterfaceId(switch_object_id,interface_switch)
290 if switch_port_id != None:
291 if switch_port_id > port_id:
292 select_object = 'portb'
293 else:
294 select_object = 'porta'
295 sql = "SELECT %s FROM Link WHERE porta = %d OR portb = %d" % (select_object, port_id, port_id)
296 result = self.db_query_one(sql)
297 if result == None:
298 #Insert new connection
299 sql = "INSERT INTO Link (porta,portb) VALUES (%d,%d)" % (port_id, switch_port_id)
300 self.db_insert(sql)
301 resolution = True
302 else:
303 #Update old connection
304 old_switch_port_id = result[0]
305 if old_switch_port_id != switch_port_id:
306 sql = "UPDATE Link set portb = %d, porta = %d WHERE porta = %d OR portb = %d" % (switch_port_id,port_id, port_id, port_id)
307 self.db_insert(sql)
308 sql = "SELECT Port.name as port_name, Object.name as obj_name FROM Port INNER JOIN Object ON Port.object_id = Object.id WHERE Port.id = %d" % old_switch_port_id
309 result = self.db_query_one(sql)
310 old_switch_port, old_device_link = result
311
312 text = "Changed link from %s -> %s" % (old_device_link,old_switch_port)
313 self.InsertLog(object_id,text)
314 resolution = True
315 resolution = None
316
317 else:
318 resolution = None
319 else:
320 resolution = None
321
322 else:
323 resolution = None
324
325 return resolution
326
327 def InterfaceAddIpv4IP(self,object_id,device,ip):
328 '''Add/Update IPv4 IP on interface'''
329
330 sql = "SELECT INET_NTOA(ip) from IPv4Allocation WHERE object_id = %d AND name = '%s'" % (object_id,device)
331 result = self.db_query_all(sql)
332
333 if result != None:
334 old_ips = result
335
336 is_there = "no"
337
338 for old_ip in old_ips:
339 if old_ip[0] == ip:
340 is_there = "yes"
341
342 if is_there == "no":
343 sql = "INSERT INTO IPv4Allocation (object_id,ip,name) VALUES (%d,INET_ATON('%s'),'%s')" % (object_id,ip,device)
344 self.db_insert(sql)
345 text = "Added IP %s on %s" % (ip,device)
346 self.InsertLog(object_id,text)
347
348 def InterfaceAddIpv6IP(self,object_id,device,ip):
349 '''Add/Update IPv6 IP on interface'''
350 #Create address object using ipaddr
351 addr6 = ipaddr.IPAddress(ip)
352 #Create IPv6 format for Mysql
353 ip6 = "".join(str(x) for x in addr6.exploded.split(':'))
354
355 sql = "SELECT HEX(ip) FROM IPv6Allocation WHERE object_id = %d AND name = '%s'" % (object_id, device)
356 result = self.db_query_all(sql)
357
358 if result != None:
359 old_ips = result
360
361 is_there = "no"
362
363 for old_ip in old_ips:
364 if old_ip[0] != ip6:
365 is_there = "yes"
366
367 if is_there == "no":
368 sql = "INSERT INTO IPv6Allocation (object_id,ip,name) VALUES (%d,UNHEX('%s'),'%s')" % (object_id,ip6,device)
369 self.db_insert(sql)
370 text = "Added IPv6 IP %s on %s" % (ip,device)
371 self.InsertLog(object_id,text)
372
373
374
375 def GetDictionaryId(self,searchstring):
376 '''Search racktables dictionary using searchstring and return id of dictionary element'''
377 sql = "SELECT dict_key FROM Dictionary WHERE dict_value LIKE '%"+searchstring+"%'"
378
379 result = self.db_query_one(sql)
380 if result != None:
381 getted_id = result[0]
382 else:
383 getted_id = None
384
385 return getted_id
386
387 def CleanVirtuals(self,object_id,virtual_servers):
388 '''Clean dead virtuals from hypervisor. virtual_servers is list of active virtual servers on hypervisor (object_id)'''
389
390 sql = "SELECT child_entity_id FROM EntityLink WHERE parent_entity_id = %d" % object_id
391
392 result = self.db_query_all(sql)
393
394 if result != None:
395 old_virtuals_ids = result
396 delete_virtual_id = []
397 new_virtuals_ids = []
398 # Translate names into ids
399 for new_virt in virtual_servers:
400 new_id = self.GetObjectId(new_virt)
401 if new_id != None:
402 new_virtuals_ids.append(new_id)
403
404 for old_id in old_virtuals_ids:
405 try:
406 test = new_virtuals_ids.index(old_id[0])
407 except ValueError:
408 delete_virtual_id.append(old_id[0])
409 if len(delete_virtual_id) != 0:
410 for virt_id in delete_virtual_id:
411
412 sql = "DELETE FROM EntityLink WHERE parent_entity_id = %d AND child_entity_id = %d" % (object_id,virt_id)
413 self.db_insert(sql)
414 virt_name = self.GetObjectName(virt_id)
415 logstring = "Removed virtual %s" % virt_name
416 self.InsertLog(object_id,logstring)
417
418 def CleanIPAddresses(self,object_id,ip_addresses,device):
419 '''Clean unused ip from object. ip addresses is list of IP addresses configured on device (device) on host (object_id)'''
420
421 sql = "SELECT INET_NTOA(ip) FROM IPv4Allocation WHERE object_id = %d AND name = '%s'" % (object_id, device)
422
423 result = self.db_query_all(sql)
424
425 if result != None:
426 old_ips = result
427 delete_ips = []
428
429 for old_ip in old_ips:
430 try:
431 test = ip_addresses.index(old_ip[0])
432 except ValueError:
433 delete_ips.append(old_ip[0])
434 if len(delete_ips) != 0:
435 for ip in delete_ips:
436 sql = "DELETE FROM IPv4Allocation WHERE ip = INET_ATON('%s') AND object_id = %d AND name = '%s'" % (ip,object_id,device)
437 self.db_insert(sql)
438 logstring = "Removed IP %s from %s" % (ip,device)
439 self.InsertLog(object_id,logstring)
440
441 def CleanIPv6Addresses(self,object_id,ip_addresses,device):
442 '''Clean unused ipv6 from object. ip_addresses mus be list of active IP addresses on device (device) on host (object_id)'''
443
444 sql = "SELECT HEX(ip) FROM IPv6Allocation WHERE object_id = %d AND name = '%s'" % (object_id,device)
445 result = self.db_query_all(sql)
446
447 if result != None:
448 old_ips = result
449 delete_ips = []
450 new_ip6_ips = []
451
452 #We must prepare ipv6 addresses into same format for compare
453 for new_ip in ip_addresses:
454 converted = ipaddr.IPAddress(new_ip).exploded.lower()
455 new_ip6_ips.append(converted)
456
457
458 for old_ip_hex in old_ips:
459 try:
460 #First we must construct IP from HEX
461 tmp = re.sub("(.{4})","\\1:", old_ip_hex[0], re.DOTALL)
462 #Remove last : and lower string
463 old_ip = tmp[:len(tmp)-1].lower()
464
465 test = new_ip6_ips.index(old_ip)
466
467 except ValueError:
468 delete_ips.append(old_ip)
469
470 if len(delete_ips) != 0:
471 for ip in delete_ips:
472 db_ip6_format = "".join(str(x) for x in ip.split(':'))
473 sql = "DELETE FROM IPv6Allocation WHERE ip = UNHEX('%s') AND object_id = %d AND name = '%s'" % (db_ip6_format,object_id,device)
474 self.db_insert(sql)
475 logstring = "Removed IP %s from %s" % (ip,device)
476 self.InsertLog(object_id,logstring)
477
478 def LinkVirtualHypervisor(self,object_id,virtual_id):
479 '''Assign virtual server to correct hypervisor'''
480 sql = "SELECT child_entity_id FROM EntityLink WHERE parent_entity_id = %d AND child_entity_id = %d" % (object_id,virtual_id)
481 result = self.db_query_one(sql)
482
483 if result == None:
484 sql = "INSERT INTO EntityLink (parent_entity_type, parent_entity_id, child_entity_type, child_entity_id) VALUES ('object',%d,'object',%d)" % (object_id, virtual_id)
485 self.db_insert(sql)
486 text = "Linked virtual %s with hypervisor" % self.GetObjectName(virtual_id)
487 self.InsertLog(object_id,text)
488
489 def AssignChassisSlot(self,chassis_name,slot_number,server_name):
490 '''Assign server objects to server chassis'''
491 chassis_id = self.GetObjectId(chassis_name)
492 server_id = self.GetObjectId(server_name)
493 slot_attribute_id = self.GetAttributeId("Slot number")
494
495 # Assign slot number to server
496 sql = "INSERT INTO AttributeValue (object_id,object_tid,attr_id,string_value) VALUES ( %d, 4, %d, '%s')" % ( server_id, slot_attribute_id, slot_number)
497 try:
498 self.db_insert(sql)
499 except:
500 pass
501
502 # Assign server to chassis
503 # Check if it's connected
504 sql = "SELECT parent_entity_id FROM EntityLink WHERE child_entity_type = 'object' AND child_entity_id = %d" % (server_id)
505 result = self.db_query_one(sql)
506
507 if result != None:
508 # Object is connected to someone
509 if result[0] != chassis_id:
510 # Connected to differend chassis/chassis
511 sql = "UPDATE EntityLink SET parent_entity_id = %d WHERE child_entity_id = %d AND child_entity_type = 'object' AND parent_entity_id = %d" % (chassis_id, server_id, result[0])
512 self.db_insert(sql)
513
514 old_object_name = self.GetObjectName(result[0])
515 self.InsertLog(old_object_name, "Unlinked server %s" % (server_name))
516 self.InsertLog(server_id, "Unlinked from Blade Chassis %s" % (old_object_name))
517 self.InsertLog(chassis_id, "Linked with server %s" % (server_name))
518 self.InsertLog(server_id, "Linked with Blade Chassis %s" % (chassis_name))
519
520 else:
521 # Object is not connected
522 sql = "INSERT INTO EntityLink (parent_entity_type, parent_entity_id, child_entity_type, child_entity_id) VALUES ('object', %d, 'object', %d)" % (chassis_id, server_id)
523 self.db_insert(sql)
524 self.InsertLog(chassis_id, "Linked with server %s" % (server_name))
525 self.InsertLog(server_id, "Linked with Blade Chassis %s" % (chassis_name))
526
527
528
529 def GetAllServerChassisId(self):
530 '''Get list of all server chassis IDs'''
531 sql = "SELECT object_id FROM AttributeValue WHERE attr_id = 2 AND uint_value = 994"
532 return self.db_query_all(sql)