Created
January 25, 2013 13:14
-
-
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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