1
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
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
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():
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
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-", "")
167
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
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
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
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
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
347 f = file(filename, 'r')
348 port_wwn = f.readline().rstrip('\n')
349 f.close()
350 return port_wwn
351
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
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
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
404
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
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:
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
451
452
453 for j in range(4, page_83[3] + 4, page_83[j + 3] + 4):
454
455
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
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
470
471 if (page_83[j] & 0x0f) != entry['code_set']:
472 continue
473
474
475
476 length = page_83[j + 3]
477
478 if (page_83[j] & 0x0f) != SCSI_ID_ASCII:
479 length *= 2
480
481
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
493
494
495
496
497 if entry['id_type'] == SCSI_ID_VENDOR_SPECIFIC:
498 serial = vendor + '_' + model + serial
499
500 i = j + 4
501
502 if (page_83[j] & 0x0f) == SCSI_ID_ASCII:
503
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
515
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