Skip to content

Instantly share code, notes, and snippets.

@dankrause
Created January 25, 2013 13:14
Show Gist options
  • Save dankrause/4634345 to your computer and use it in GitHub Desktop.
Save dankrause/4634345 to your computer and use it in GitHub Desktop.
A drop in replacement for tgtadm. This was written to help troubleshoot some issues with an older version of tgt on CentOS (back when tgtd used the abstract socket namespace for its IPC socket).
#!/usr/bin/python
import socket
import struct
def enum(*args, **kwargs):
kwargs["__getitem__"] = lambda self, key: args[key]
return type('Enum', (object,), dict(zip(args, values), **kwargs))()
class IPCClient(object):
class Message(object):
_ops = enum('NEW', 'DELETE', 'SHOW', 'BIND', 'UNBIND', 'UPDATE')
_modes = enum('SYSTEM', 'TARGET', 'DEVICE', 'SESSION', 'CONNECTION', 'ACCOUNT')
_ac_types = enum('INCOMING', 'OUTGOING')
_dev_types = {"DISK": 0, "CD": 5, "CHANGER": 8, "OSD": 17, "SCC": 1, "PT": 255}
_valid_params = ('path', 'bstype', 'targetname', 'initiator-address', 'user', 'password')
_packfmt = '2I64sIi2Q5I4x'
def __init__(self, mode = -1, op = -1, lld = "iscsi", tid = -1, sid = 0, lun = 0, cid = 0, hostno = 0, dev_type = 0, ac_type = 0, params = {}, targetOps = {}):
self.mode = mode
self.op = op
self.lld = lld
self.tid = tid
self.sid = sid
self.lun = lun
self.cid = cid
self.hostno = hostno
self.dev_type = dev_type
self.ac_type = ac_type
self.params = {}
[self.addparam(key, val) for (key, val) in params.iteritems()]
self.targetOps = targetOps
def addparam(self, key, val):
if key not in self._valid_params: raise KeyError("Not a valid param", key)
self.params[key] = val
def packed(self):
params = []
if self.params != {}:
params = ["%s=%s" % (key, value) for key, value in self.params.iteritems()]
if self.targetOps != {}:
targetOps = ["%s=%s" % (key, value) for key, value in self.params.iteritems()]
params.append("targetOps %s," % ",".join(targetOps))
suffix = ",".join(params)
size = struct.calcsize(self._packfmt) + len(suffix)
packed = struct.pack(self._packfmt, self._mode, self._op, self.lld, size, self.tid, self.sid, self.lun, self.cid, self.hostno, self._dev_types[self._dev_type], self._ac_type, 0)
return "".join([packed, suffix])
def _set_op(self, op):
op = op.upper()
if op not in self._ops: raise KeyError("Not a valid operation", op)
self._op = getattr(self._ops, op.upper())
def _get_op(self):
return self._ops[self._op]
def _set_mode(self, mode):
mode = mode.upper()
if mode not in self._modes: raise KeyError("Not a valid mode", mode)
self._mode = getattr(self._modes, mode.upper())
def _get_mode(self):
return self._modes[self._mode]
def _set_ac_type(self, ac_type):
ac_type = ac_type.upper()
if ac_type not in self._ac_types: raise KeyError("Not a account type", ac_type)
self._ac_type = getattr(self._ac_types, ac_type.upper())
def _get_ac_type(self):
return self._ac_types[self._ac_type]
def _set_dev_type(self, dev_type):
dev_type = dev_type.upper()
if dev_type not in self._dev_types: raise KeyError("Not a valid device type", dev_type)
self._dev_type = dev_type
def _get_dev_type(self):
return self._dev_type
op = property(_get_op, _set_op)
mode = property(_get_mode, _set_mode)
ac_type = property(_get_ac_type, _set_ac_type)
dev_type = property(_get_dev_type, _set_dev_type)
class Response(object):
_packfmt = '2I'
errors = [
"",
"unknown error",
"out of memory",
"can't find the driver",
"can't find the target",
"can't find the logical unit",
"can't find the session",
"can't find the connection",
"this target already exists",
"this logical unit number already exists",
"this access control rule already exists",
"this account already exists",
"can't find the account",
"too many accounts",
"invalid request",
"this target already has an outgoing account",
"this target unit is still active",
"this logical unit is still active",
"this operation isn't supported",
"unknown parameter"]
def __init__(self, errno, text):
self.errno = errno
self.error = self.errors[self.errno]
self.text = text
def __init__(self, socketname = "\0TGT_IPC_ABSTRACT_NAMESPACE"):
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.socketname = socketname
if self.socketname[0] == "\0": self.socketname = self.socketname.ljust(108, "\0")
def connect(self):
self.sock.connect(self.socketname)
def send(self, msg):
text = ""
if self.sock.send(msg.packed()) == 0:
raise IOError("Not connected")
recvsize = struct.calcsize(IPCClient.Response._packfmt)
errno, size = struct.unpack(IPCClient.Response._packfmt, self.sock.recv(recvsize))
if size - recvsize > 0:
text = self.sock.recv(size - recvsize)
return IPCClient.Response(errno, text)
def main():
version = "%prog 1.0"
usage = "usage: %prog [options]"
description = "Linux SCSI Target Framework Administration Utility. (re-implemented in Python)"
parser = optparse.OptionParser(version=version, usage=usage, description=description)
parser.add_option("-o", "--op", default=-1, help="The operation to perform")
parser.add_option("-m", "--mode", default=None, help="The type of entity to operate on")
parser.add_option("-L", "--lld", default="iscsi", help="Low-Level Driver (defaults to \"iscsi\")")
parser.add_option("-t", "--tid", default=-1, type="int", help="Target ID")
parser.add_option("-s", "--sid", default=0, help="Session ID")
parser.add_option("-l", "--lun", default=0, type="int", help="Logical Unit Number")
parser.add_option("-c", "--cid", default=0, help="Connection ID")
parser.add_option("-H", "--host", default=0, dest="hostno", help="Host Number")
parser.add_option("-T", "--targetname", help="The target IQN")
parser.add_option("-b", "--backing-store", dest="path", help="The path to the block device to bind to")
parser.add_option("-I", "--initiator-address", help="The remote address that will connect to this target")
parser.add_option("-E", "--bstype", help="")
parser.add_option("-u", "--user", help="")
parser.add_option("-p", "--password", help="")
parser.add_option("-O", "--outgoing", action="store_true", help="For adding outgoing accounts")
parser.add_option("-Y", "--device-type", default="disk", dest="dev_type", help="")
parser.add_option("-P", "--params", dest="targetOps", help="A list of driver-specific options, in the form of a comma-separated list of key=value pairs")
(options, args) = parser.parse_args()
if options.mode is None:
print "You must specify a mode."
return 1
ac_type = options.outgoing and "outgoing" or "incoming"
client = IPCClient()
client.connect()
try:
params = {}
for key in IPCClient.Message._valid_params:
value = getattr(options, key.replace("-", "_"))
if value: params[key] = value
msg = IPCClient.Message(options.mode, options.op, options.lld, options.tid, options.sid, options.lun, options.cid, options.hostno, options.dev_type, ac_type, params, options.targetOps)
rsp = client.send(msg)
except KeyError, e:
print "Key Error: %s" % e
return 1
except IOError, e:
print "Socket error: %s" % e
return 1
if rsp.errno != 0:
print "TGTD Error (%d): %s" % (rsp.errno, rsp.error)
return 1
print rsp.text
return 0
if __name__ == "__main__":
import sys
import optparse
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment