Module OSCPluginUtils
[hide private]
[frames] | no frames]

Source Code for Module OSCPluginUtils

  1  #!/usr/bin/env python 
  2   
  3  """ 
  4  @copyright: Copyright (C) 2008, 2010 Oracle and/or its affiliates. All rights reserved. 
  5   
  6  @license:   See the file COPYING for redistribution information. 
  7   
  8  @summary:   Miscellaneous utility functions that is available for use by any 
  9              Storage Connect plug-in. 
 10  """ 
 11   
 12  import os 
 13  import os.path 
 14  import re 
 15  import fcntl 
 16  import socket 
 17  import struct 
 18  import gettext 
 19  import fileinput 
 20  import subprocess 
 21   
 22  from glob import glob 
 23   
 24   
 25  _ = gettext.gettext 
 26   
27 -def checkRequired(*required_commands):
28 """ 29 Check if the command (full path to the command) is available on the system, 30 if not it will raise the standardized Storage Connect exception. 31 32 @param required_commands: Commands to check if available. 33 @type required_commands: C{str} or C{list} 34 35 @raise CommandMissingEx: Raised if a command cannot be found. 36 """ 37 for required_command in required_commands: 38 if type(required_command) == list: 39 checkRequired(required_command) 40 elif type(required_command) == str: 41 if not os.path.exists(required_command): 42 raise CommandMissingEx(_("Missing command: %s" % 43 required_command))
44
45 -def destroyMPDev(mp_dev):
46 """ 47 This will destroy a multipath device as well as all of its children 48 (partitions and block devices). 49 50 @param mp_dev: The multipath device (dm device) that need to teared down. 51 @type mp_dev: C{str} 52 """ 53 from OSCPlugin import IPlugin 54 55 if not os.path.exists(IPlugin.dev_path_prefix): 56 IPlugin.logger.info("destroyMPDev() - " 57 "Device prefix dir (%s) does not exist" % 58 IPlugin.dev_path_prefix) 59 return 60 61 dev_names = os.listdir(IPlugin.dev_path_prefix) 62 p_lst = [] 63 d_lst = [d_lst for d_lst in dev_names 64 if re.match("%sp\d+" % os.path.basename(mp_dev), d_lst)] 65 if len(d_lst) > 0: 66 p_lst = p_lst + d_lst[:] 67 68 for part in p_lst: 69 if os.path.exists(os.path.join(IPlugin.dev_path_prefix, part)): 70 IPlugin.logger.info("destroyMPDev() - Removing DM device %s" % part) 71 dms_p = subprocess.Popen(["dmsetup", 72 "remove", 73 part], 74 stdout = subprocess.PIPE, 75 stderr = subprocess.PIPE) 76 (p_out, p_err) = dms_p.communicate() 77 78 if dms_p.returncode != 0: 79 IPlugin.logger.error("destroyMPDev() - Unable to tear down " 80 "multipath device: %s" % p_err) 81 raise OperationFailedEx(_("Unable to tear down multipath" 82 "device: %s" % p_err)) 83 84 if os.path.exists(mp_dev): 85 mpl_p = subprocess.Popen(["multipath", 86 "-l", 87 os.path.basename(mp_dev)], 88 stdout = subprocess.PIPE, 89 stderr = subprocess.PIPE) 90 (p_out, p_err) = mpl_p.communicate() 91 92 if mpl_p.returncode != 0: 93 IPlugin.logger.error("destroyMPDev() - Unable to obtain " 94 "multipath info for %s: %s", 95 mp_dev, p_err) 96 raise OperationFailedEx(_("Unable to obtain multipath info for " 97 "%s: %s" % (mp_dev, p_err))) 98 99 slaves = [] 100 for mpl_line in p_out.splitlines(): #pylint: disable=E1103 101 mpl_fields = mpl_line.split() 102 for index in range(len(mpl_fields)): 103 if (mpl_fields[index] == "|-" or mpl_fields[index] == "`-"): 104 slaves.append(mpl_fields[index + 2]) 105 106 IPlugin.logger.info("destroyMPDev() - Removing DM device %s" % mp_dev) 107 dms_p = subprocess.Popen(["dmsetup", 108 "remove", 109 os.path.basename(mp_dev)], 110 stdout = subprocess.PIPE, 111 stderr = subprocess.PIPE) 112 (p_out, p_err) = dms_p.communicate() 113 114 if dms_p.returncode != 0: 115 IPlugin.logger.error("Unable to tear down multipath device %s: %s" % 116 (mp_dev, p_err)) 117 raise OperationFailedEx(_("Unable to tear down multipath " 118 "device %s: %s" % (mp_dev, p_err))) 119 120 for slave in slaves: 121 devdel_name = os.path.join(os.path.join("/sys/block", slave), 122 "device/delete") 123 124 if os.path.exists(devdel_name): 125 IPlugin.logger.debug("destroyMPDev() - Deleting block " 126 "device %s" % slave) 127 try: 128 devdel_f = open(devdel_name, "a") 129 devdel_f.write("1") 130 devdel_f.close() 131 132 except IOError, ioe: 133 IPlugin.logger.error("IOError deleting device %s: %s", 134 slave, ioe.strerror)
135
136 -def getPage83FromMPDev(mp_dev):
137 """ 138 This will return the page83_id from the supplied multipath device. 139 140 @param mp_dev: The multi path device (dm device) to interrogate. 141 @type mp_dev: C{str} 142 """ 143 144 from OSCPlugin import IPlugin 145 146 if not os.path.exists(mp_dev): 147 IPlugin.logger.info("getPage83FromMPDev() - Device (%s) does not exist" % mp_dev) 148 return 149 150 dms_p = subprocess.Popen(["dmsetup", 151 "info", 152 "--columns", 153 "--noheadings", 154 "--options=UUID", 155 os.path.basename(mp_dev)], 156 stdout = subprocess.PIPE, 157 stderr = subprocess.PIPE) 158 (p_out, p_err) = dms_p.communicate() 159 160 if dms_p.returncode != 0: 161 IPlugin.logger.error("Unable to tear down multipath device %s: %s" % 162 (mp_dev, p_err)) 163 raise OperationFailedEx(_("Unable to tear down multipath device %s: %s" % 164 (mp_dev, p_err))) 165 166 return p_out.rstrip("\n").replace("mpath-", "") #pylint: disable=E1103
167
168 -def containsFile(fs_record, file_path):
169 """ 170 Check if the file is contained on the file system identified by the 171 File System record. 172 173 @param fs_record: L{File System record<OSCPlugin.IPlugin.__FSRecord__>} 174 @type fs_record: C{dict} 175 176 @param file_path: File path to check. 177 @type file_path: C{str} 178 179 @return: True if the file is contained in the file system. 180 @rtype: C{bool} 181 182 @raise IPluginException: on failure 183 """ 184 if file_path.startswith('/'): 185 if file_path[:len(fs_record["mount_path"])] == fs_record["mount_path"]: 186 return True 187 else: 188 return False 189 190 else: 191 return True
192
193 -def getFileRecord(fs_record, file_path):
194 """ 195 Generate a file record for the file identified by the file path for 196 the specific File System record. 197 198 @param fs_record: L{File System record<OSCPlugin.IPlugin.__FSRecord__>} 199 @type fs_record: C{dict} 200 201 @param file_path: File path to create the file record for. 202 @type file_path: C{str} 203 204 @return: L{File record<OSCPlugin.IPlugin.__FileRecord__>} 205 @rtype: C{dict} 206 207 @raise IPluginException: on failure 208 """ 209 if file_path.startswith('/'): 210 if file_path[:len(fs_record["mount_path"])] != fs_record["mount_path"]: 211 raise ValueFormatEx(_("File (%s) is not contained on %s file " 212 "system" % (file_path, fs_record["name"]))) 213 214 else: 215 if ".snap" in file_path: 216 fr_type = IFileSystemPlugin.SnapType 217 218 else: 219 fr_type = IFileSystemPlugin.FileType 220 221 return {"fr_type": fr_type, 222 "file_path": file_path} 223 224 else: 225 full_path = os.path.join(fs_record["mount_path"], file_path) 226 if ".snap" in full_path: 227 fr_type = IFileSystemPlugin.SnapType 228 229 else: 230 fr_type = IFileSystemPlugin.FileType 231 232 return {"fr_type": fr_type, 233 "file_path": file_path}
234
235 -def iSCSIDiscoveryAuth(ss_record):
236 """ 237 Generate a file record for the file identified by the file path for 238 the specific File System record. 239 240 @param ss_record: L{Storage Server record<OSCPlugin.IPlugin.__SSRecord__>} 241 @type ss_record: C{dict} 242 243 @raise IPluginException: on failure 244 """ 245 found_username = False 246 found_passwd = False 247 found_chap = False 248 username = ss_record.get("username", None) 249 passwd = ss_record.get("passwd", None) 250 chap = ss_record.get("chap", False) 251 try: 252 cnf_file = fileinput.input("/etc/iscsi/iscsid.conf", 253 inplace=1, 254 backup=".bak") 255 for cnf_line in cnf_file: 256 if username and re.match("discovery.sendtargets.auth.username.*", cnf_line): 257 cnf_line = "discovery.sendtargets.auth.username = %s\n" % username 258 found_username = True 259 260 if passwd and re.match("discovery.sendtargets.auth.password.*", cnf_line): 261 cnf_line = "discovery.sendtargets.auth.password = %s\n" % passwd 262 found_passwd = True 263 264 if chap and re.match("discovery.sendtargets.auth.authmethod", cnf_line): 265 cnf_line = "discovery.sendtargets.auth.authmethod = CHAP\n" 266 found_chap = True 267 268 print cnf_line, 269 270 cnf_file = open("/etc/iscsi/iscsid.conf", "a") 271 if username and not found_username: 272 print >> cnf_file, "discovery.sendtargets.auth.username = %s" % username 273 274 if passwd and not found_passwd: 275 print >> cnf_file, "discovery.sendtargets.auth.password = %s" % passwd 276 277 if chap and not found_chap: 278 print >> cnf_file, "discovery.sendtargets.auth.authmethod = CHAP" 279 280 except Exception, exc: 281 cnf_file.close() 282 if os.path.exists("/etc/iscsi/iscsid.conf.bak"): 283 os.remove("/etc/iscsi/iscsid.conf") 284 os.rename("/etc/iscsi/iscsid.conf.bak", 285 "/etc/iscsi/iscsid.conf") 286 287 raise TargetDiscoveryEx(_(exc)) 288 289 cnf_file.close()
290
291 -def getNetDevInfo(netdev):
292 """ 293 Obtain network device information. 294 295 @param netdev: Network device name 296 @type netdev: C{str} 297 298 @return: Network device information 299 @rtype: C{dict} 300 """ 301 netdev_info = {"netdev": netdev} 302 temp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 303 sock_fd = temp_sock.fileno() 304 305 try: 306 netdev_info["addr"] = __netdev_ioctl__(sock_fd, 0x8915, netdev) 307 netdev_info["mask"] = __netdev_ioctl__(sock_fd, 0x891b, netdev) 308 netdev_info["bcast"] = __netdev_ioctl__(sock_fd, 0x8919, netdev) 309 netdev_info["hwaddr"] = __netdev_ioctl__(sock_fd, 0x8927, netdev) 310 311 except: 312 pass 313 314 temp_sock.close() 315 return netdev_info
316
317 -def __netdev_ioctl__(sock_fd, ioctl_num, dev_name): #pylint: disable=C0103
318 """ 319 Execute specific ioctl op for network devices. 320 321 @param sock_fd: File descriptor 322 @type sock_fd: C{int} 323 324 @param ioctl_num: ioctl operation 325 @type ioctl_num: C{int} 326 327 @param dev_name: Device name to query 328 @type dev_name: C{str} 329 330 @return: ioctl system call return value 331 @rtype: C{str} 332 """ 333 if_struct = struct.pack("256s", dev_name[:15]) 334 net_val = fcntl.ioctl(sock_fd, ioctl_num, if_struct) 335 if ioctl_num == 0x8927: 336 hwaddr = "" 337 for hw_char in net_val[18:24]: 338 hex_num = "00" + hex(ord(hw_char))[2:] 339 hwaddr = hwaddr + hex_num[-2:] + ":" 340 hwaddr = hwaddr[:len(hwaddr) - 1] 341 return hwaddr 342 343 else: 344 return socket.inet_ntoa(net_val[20:24]) 345
346 -def _read_port_name(filename):
347 f = file(filename, 'r') 348 port_wwn = f.readline().rstrip('\n') 349 f.close() 350 return port_wwn
351
352 -def getPortNames():
353 port_names = [] 354 for filename in glob('/sys/class/fc_host/*/port_name'): 355 port_wwn = _read_port_name(filename) 356 port_names.append(port_wwn) 357 return port_names
358
359 -def getInitiatorNames():
360 initiators = [] 361 if os.path.exists('/etc/iscsi/initiatorname.iscsi'): 362 f = file('/etc/iscsi/initiatorname.iscsi', 'r') 363 while True: 364 line = f.readline() 365 if line == '': 366 break 367 line = line.strip() 368 if (line == '') or line.startswith('#') or (line.find('=') == -1): 369 continue 370 (key, value) = line.split('=', 1) 371 if (key == 'InitiatorName') or (key == 'InitiatorAlias'): 372 initiators.append(value) 373 f.close() 374 return initiators
375 376
377 -def makeMPPage83FromRawPage83(page_83, vendor = None, model = None):
378 """ 379 This will return the multipath uuid from the supplied raw page 83 id. 380 381 @param page_83: The raw page 83 to convert. 382 @type page_83: C{int} 383 384 @param vendor: The lun vendor. 385 @type vendor: C{str} 386 387 @param model: The lun model. 388 @type model: C{str} 389 """ 390 391 MAX_SERIAL_LEN = 256 392 VENDOR_LENGTH = 8 393 MODEL_LENGTH = 16 394 PAGE_83 = 0x83 395 396 # id type values of id descriptors. These are assumed to fit in 4 bits. 397 398 SCSI_ID_VENDOR_SPECIFIC = 0 399 SCSI_ID_T10_VENDOR = 1 400 SCSI_ID_EUI_64 = 2 401 SCSI_ID_NAA = 3 402 403 # Supported NAA values. These fit in 4 bits, so the "don't care" value 404 # cannot conflict with real values. 405 406 SCSI_ID_NAA_DONT_CARE = 0xff 407 SCSI_ID_NAA_IEEE_REG = 5 408 SCSI_ID_NAA_IEEE_REG_EXTENDED = 6 409 410 # Supported Code Set values. 411 412 SCSI_ID_BINARY = 1 413 SCSI_ID_ASCII = 2 414 415 id_search_list = [ 416 { 'id_type':SCSI_ID_NAA, 'naa_type':SCSI_ID_NAA_IEEE_REG_EXTENDED, 'code_set':SCSI_ID_BINARY }, 417 { 'id_type':SCSI_ID_NAA, 'naa_type':SCSI_ID_NAA_IEEE_REG_EXTENDED, 'code_set':SCSI_ID_ASCII }, 418 { 'id_type':SCSI_ID_NAA, 'naa_type':SCSI_ID_NAA_IEEE_REG, 'code_set':SCSI_ID_BINARY }, 419 { 'id_type':SCSI_ID_NAA, 'naa_type':SCSI_ID_NAA_IEEE_REG, 'code_set':SCSI_ID_ASCII }, 420 { 'id_type':SCSI_ID_NAA, 'naa_type':SCSI_ID_NAA_DONT_CARE, 'code_set':SCSI_ID_BINARY }, 421 { 'id_type':SCSI_ID_NAA, 'naa_type':SCSI_ID_NAA_DONT_CARE, 'code_set':SCSI_ID_ASCII }, 422 { 'id_type':SCSI_ID_EUI_64, 'naa_type':SCSI_ID_NAA_DONT_CARE, 'code_set':SCSI_ID_BINARY }, 423 { 'id_type':SCSI_ID_EUI_64, 'naa_type':SCSI_ID_NAA_DONT_CARE, 'code_set':SCSI_ID_ASCII }, 424 { 'id_type':SCSI_ID_T10_VENDOR, 'naa_type':SCSI_ID_NAA_DONT_CARE, 'code_set':SCSI_ID_BINARY }, 425 { 'id_type':SCSI_ID_T10_VENDOR, 'naa_type':SCSI_ID_NAA_DONT_CARE, 'code_set':SCSI_ID_ASCII }, 426 { 'id_type':SCSI_ID_VENDOR_SPECIFIC, 'naa_type':SCSI_ID_NAA_DONT_CARE, 'code_set':SCSI_ID_BINARY }, 427 { 'id_type':SCSI_ID_VENDOR_SPECIFIC, 'naa_type':SCSI_ID_NAA_DONT_CARE, 'code_set':SCSI_ID_ASCII }] 428 429 serial = '' 430 431 if page_83[1] != PAGE_83: 432 return None 433 434 if vendor and len(vendor) > VENDOR_LENGTH - 1: 435 vendor = vendors[:VENDOR_LENGTH - 1] 436 if model and len(model) > MODEL_LENGTH - 1: 437 model = model[:MODEL_LENGTH - 1] 438 439 if page_83[6] != 0: # Extract the raw binary from VPD 0x83 pre-SPC devices 440 serial += "%x" % SCSI_ID_NAA 441 for i in range(page_83[3]): 442 serial += "%x" % ((page_83[i+4] & 0xf0) >> 4) 443 serial += serial + "%x" % (page_83[i+4] & 0x0f) 444 return serial 445 446 j = 0 447 448 for entry in id_search_list: 449 450 # Examine each descriptor returned. There is normally only 451 #one or a small number of descriptors. 452 453 for j in range(4, page_83[3] + 4, page_83[j + 3] + 4): 454 455 # ASSOCIATION must be with the device (value 0) 456 457 if page_83[j + 1] & 0x30 != 0: 458 continue 459 460 if (page_83[j + 1] & 0x0f) != entry['id_type']: 461 continue 462 463 # Possibly check NAA sub-type. 464 465 if entry['naa_type'] != SCSI_ID_NAA_DONT_CARE \ 466 and entry['naa_type'] != ((page_83[j + 4] & 0xf0) >> 4): 467 continue 468 469 #Check for matching code set - ASCII or BINARY. 470 471 if (page_83[j] & 0x0f) != entry['code_set']: 472 continue 473 474 # page_83[3]: page length 475 476 length = page_83[j + 3] 477 478 if (page_83[j] & 0x0f) != SCSI_ID_ASCII: 479 length *= 2 # If not ASCII, use two bytes for each binary value. 480 481 # Add one byte for the NUL termination, and one for the id_type. 482 483 length += 2 484 485 if entry['id_type'] == SCSI_ID_VENDOR_SPECIFIC: 486 length += VENDOR_LENGTH + MODEL_LENGTH 487 488 if MAX_SERIAL_LEN < length: 489 continue 490 491 492 # For SCSI_ID_VENDOR_SPECIFIC prepend the vendor and model before 493 # the id since it is not unique across all vendors and models, 494 # this differs from SCSI_ID_T10_VENDOR, where the vendor is 495 # included in the identifier. 496 497 if entry['id_type'] == SCSI_ID_VENDOR_SPECIFIC: 498 serial = vendor + '_' + model + serial 499 500 i = j + 4 # offset to the start of the identifier 501 502 if (page_83[j] & 0x0f) == SCSI_ID_ASCII: 503 # ASCII descriptor. 504 temp = '' 505 for descriptor_len in range(page_83[j + 3]): 506 temp += chr(page_83[i]) 507 i += 1 508 509 if temp and temp.startswith(''): 510 temp = temp.lstrip() 511 temp = '_' + temp 512 serial += temp 513 else: 514 # Binary descriptor, convert to ASCII, using two bytes of 515 # ASCII for each byte in the page_83. 516 serial += "%x" % (entry['id_type']) 517 for descriptor_len in range(page_83[j + 3]): 518 serial += "%x" % ((page_83[i] & 0xf0) >> 4) 519 serial += "%x" % (page_83[i] & 0x0f) 520 i += 1 521 522 serial = serial.replace(' ', '_') 523 serial = serial.rstrip() 524 return serial 525 526 return None
527