Blob Blame History Raw
/*
 * iSCSI Administration library
 *
 * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
 * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
 * maintained by open-iscsi@googlegroups.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * See the file COPYING included with this distribution for more details.
 */

#include <Python.h>
#include "libiscsi.h"

#if PY_MAJOR_VERSION >= 3
#define IS_PY3K
#define MODINITERROR return NULL
#define PYNUM_FROMLONG PyLong_FromLong
#define PYSTR_FROMSTRING PyUnicode_FromString
#else
#define MODINITERROR return
#define PYNUM_FROMLONG PyInt_FromLong
#define PYSTR_FROMSTRING PyString_FromString
#endif

#define RET_TRUE_ELSE_FALSE { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; }
#define CMP_TO_RICHCMP(cmpfunc) \
	int comp_res = cmpfunc(self, other); \
	switch (op) { \
	    case Py_LT: \
		if (comp_res < 0) RET_TRUE_ELSE_FALSE \
	    case Py_LE: \
		if (comp_res <= 0) RET_TRUE_ELSE_FALSE \
	    case Py_EQ: \
		if (comp_res == 0) RET_TRUE_ELSE_FALSE \
	    case Py_NE: \
		if (comp_res != 0) RET_TRUE_ELSE_FALSE \
	    case Py_GT: \
		if (comp_res > 0) RET_TRUE_ELSE_FALSE \
	    default: \
		if (comp_res >= 0) RET_TRUE_ELSE_FALSE \
	}

static struct libiscsi_context *context = NULL;

/****************************** helpers ***********************************/
static int check_string(const char *string)
{
	if (strlen(string) >= LIBISCSI_VALUE_MAXLEN) {
		PyErr_SetString(PyExc_ValueError, "string too long");
		return -1;
	}
	return 0;
}

/********************** PyIscsiChapAuthInfo ***************************/

typedef struct {
	PyObject_HEAD

	struct libiscsi_auth_info info;
} PyIscsiChapAuthInfo;

static int PyIscsiChapAuthInfo_init(PyObject *self, PyObject *args,
				    PyObject *kwds)
{
	int i;
	PyIscsiChapAuthInfo *chap = (PyIscsiChapAuthInfo *)self;
	char *kwlist[] = {"username", "password", "reverse_username",
				"reverse_password", NULL};
	const char *string[4] = { NULL, NULL, NULL, NULL };

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					"zz|zz:chapAuthInfo.__init__",
					kwlist, &string[0], &string[1],
					&string[2], &string[3]))
		return -1;

	for (i = 0; i < 4; i++)
		if (string[i] && check_string(string[i]))
			return -1;

	memset (&chap->info, 0, sizeof(chap->info));
	chap->info.method = libiscsi_auth_chap;
	if (string[0])
		strcpy(chap->info.chap.username, string[0]);
	if (string[1])
		strcpy(chap->info.chap.password, string[1]);
	if (string[2])
		strcpy(chap->info.chap.reverse_username, string[2]);
	if (string[3])
		strcpy(chap->info.chap.reverse_password, string[3]);

	if (libiscsi_verify_auth_info(context, &chap->info)) {
		PyErr_SetString(PyExc_ValueError,
				libiscsi_get_error_string(context));
		return -1;
	}
	return 0;
}

static PyObject *PyIscsiChapAuthInfo_get(PyObject *self, void *data)
{
	PyIscsiChapAuthInfo *chap = (PyIscsiChapAuthInfo *)self;
	const char *attr = (const char *)data;

	if (!strcmp(attr, "username")) {
		return PYSTR_FROMSTRING(chap->info.chap.username);
	} else if (!strcmp(attr, "password")) {
		return PYSTR_FROMSTRING(chap->info.chap.password);
	} else if (!strcmp(attr, "reverse_username")) {
		return PYSTR_FROMSTRING(chap->info.chap.reverse_username);
	} else if (!strcmp(attr, "reverse_password")) {
		return PYSTR_FROMSTRING(chap->info.chap.reverse_password);
	}
	return NULL;
}

static int PyIscsiChapAuthInfo_set(PyObject *self, PyObject *value, void *data)
{
	PyIscsiChapAuthInfo *chap = (PyIscsiChapAuthInfo *)self;
	const char *attr = (const char *)data;
	const char *str;

	if (!PyArg_Parse(value, "s", &str) || check_string(str))
		return -1;

	if (!strcmp(attr, "username")) {
		strcpy(chap->info.chap.username, str);
	} else if (!strcmp(attr, "password")) {
		strcpy(chap->info.chap.password, str);
	} else if (!strcmp(attr, "reverse_username")) {
		strcpy(chap->info.chap.reverse_username, str);
	} else if (!strcmp(attr, "reverse_password")) {
		strcpy(chap->info.chap.reverse_password, str);
	}

	return 0;
}

static int PyIscsiChapAuthInfo_compare(PyIscsiChapAuthInfo *self,
				       PyIscsiChapAuthInfo *other)
{
	int r;

	r = strcmp(self->info.chap.username, other->info.chap.username);
	if (r)
		return r;

	r = strcmp(self->info.chap.password, other->info.chap.password);
	if (r)
		return r;

	r = strcmp(self->info.chap.reverse_username,
		   other->info.chap.reverse_username);
	if (r)
		return r;

	r = strcmp(self->info.chap.reverse_password,
		   other->info.chap.reverse_password);
	return r;
}

PyObject *PyIscsiChapAuthInfo_richcompare(PyIscsiChapAuthInfo *self,
	                                  PyIscsiChapAuthInfo *other,
					  int op)
{
	CMP_TO_RICHCMP(PyIscsiChapAuthInfo_compare)
}

static PyObject *PyIscsiChapAuthInfo_str(PyObject *self)
{
	PyIscsiChapAuthInfo *chap = (PyIscsiChapAuthInfo *)self;
	char s[1024], reverse[512] = "";

	if (chap->info.chap.reverse_username[0])
		snprintf(reverse, sizeof(reverse), ", %s:%s",
			 chap->info.chap.reverse_username,
			 chap->info.chap.reverse_password);

	snprintf(s, sizeof(s), "%s:%s%s", chap->info.chap.username,
		 chap->info.chap.password, reverse);

	return PYSTR_FROMSTRING(s);
}

static struct PyGetSetDef PyIscsiChapAuthInfo_getseters[] = {
	{"username", (getter)PyIscsiChapAuthInfo_get,
		(setter)PyIscsiChapAuthInfo_set,
		"username", "username"},
	{"password", (getter)PyIscsiChapAuthInfo_get,
		(setter)PyIscsiChapAuthInfo_set,
		"password", "password"},
	{"reverse_username", (getter)PyIscsiChapAuthInfo_get,
		(setter)PyIscsiChapAuthInfo_set,
		"reverse_username", "reverse_username"},
	{"reverse_password", (getter)PyIscsiChapAuthInfo_get,
		(setter)PyIscsiChapAuthInfo_set,
		"reverse_password", "reverse_password"},
	{NULL}
};

PyTypeObject PyIscsiChapAuthInfo_Type = {
	PyVarObject_HEAD_INIT(NULL, 0)
	.tp_name = "libiscsi.chapAuthInfo",
	.tp_basicsize = sizeof (PyIscsiChapAuthInfo),
	.tp_getset = PyIscsiChapAuthInfo_getseters,
	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
#ifndef IS_PY3K
	// Py_TPFLAGS_CHECKTYPES is only needed on Python 2
	|  Py_TPFLAGS_CHECKTYPES
#endif
	,
	.tp_richcompare = (richcmpfunc)PyIscsiChapAuthInfo_compare,
	.tp_init = PyIscsiChapAuthInfo_init,
	.tp_str = PyIscsiChapAuthInfo_str,
	.tp_new = PyType_GenericNew,
	.tp_doc = "iscsi chap authentication information.",
};

/***************************** PyIscsiNode  ********************************/

typedef struct {
	PyObject_HEAD

	struct libiscsi_node node;
} PyIscsiNode;

static int PyIscsiNode_init(PyObject *self, PyObject *args, PyObject *kwds)
{
	PyIscsiNode *node = (PyIscsiNode *)self;
	char *kwlist[] = {"name", "tpgt", "address", "port", "iface", NULL};
	const char *name = NULL, *address = NULL, *iface = NULL;
	int tpgt = -1, port = 3260;

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|isis:node.__init__",
					 kwlist, &name, &tpgt, &address,
					 &port, &iface))
		return -1;
	if (address == NULL) {
		PyErr_SetString(PyExc_ValueError, "address not set");
		return -1;
	}
	if (check_string(name) || check_string(address) || check_string(iface))
		return -1;

	strcpy(node->node.name, name);
	node->node.tpgt = tpgt;
	strcpy(node->node.address, address);
	node->node.port = port;
	strcpy(node->node.iface, iface);

	return 0;
}

static PyObject *PyIscsiNode_get(PyObject *self, void *data)
{
	PyIscsiNode *node = (PyIscsiNode *)self;
	const char *attr = (const char *)data;

	if (!strcmp(attr, "name")) {
		return PYSTR_FROMSTRING(node->node.name);
	} else if (!strcmp(attr, "tpgt")) {
		return PYNUM_FROMLONG(node->node.tpgt);
	} else if (!strcmp(attr, "address")) {
		return PYSTR_FROMSTRING(node->node.address);
	} else if (!strcmp(attr, "port")) {
		return PYNUM_FROMLONG(node->node.port);
	} else if (!strcmp(attr, "iface")) {
		return PYSTR_FROMSTRING(node->node.iface);
	}
	return NULL;
}

static int PyIscsiNode_set(PyObject *self, PyObject *value, void *data)
{
	PyIscsiNode *node = (PyIscsiNode *)self;
	const char *attr = (const char *)data;
	const char *str;
	int i;

	if (!strcmp(attr, "name")) {
		if (!PyArg_Parse(value, "s", &str) || check_string(str))
			return -1;
		strcpy(node->node.name, str);
	} else if (!strcmp(attr, "tpgt")) {
		if (!PyArg_Parse(value, "i", &i))
			return -1;
		node->node.tpgt = i;
	} else if (!strcmp(attr, "address")) {
		if (!PyArg_Parse(value, "s", &str) || check_string(str))
			return -1;
		strcpy(node->node.address, str);
	} else if (!strcmp(attr, "port")) {
		if (!PyArg_Parse(value, "i", &i))
			return -1;
		node->node.port = i;
	} else if (!strcmp(attr, "iface")) {
		if (!PyArg_Parse(value, "s", &str) || check_string(str))
			return -1;
		strcpy(node->node.iface, str);
	}

	return 0;
}

static int PyIscsiNode_compare(PyIscsiNode *self, PyIscsiNode *other)
{
	int res;

	res = strcmp(self->node.name, other->node.name);
	if (res)
		return res;

	if (self->node.tpgt < other->node.tpgt)
		return -1;
	if (self->node.tpgt > other->node.tpgt)
		return -1;

	res = strcmp(self->node.address, other->node.address);
	if (res)
		return res;

	if (self->node.port < other->node.port)
		return -1;
	if (self->node.port > other->node.port)
		return -1;

	res = strcmp(self->node.iface, other->node.iface);
	if (res)
		return res;

	return 0;
}

PyObject *PyIscsiNode_richcompare(PyIscsiNode *self, PyIscsiNode *other, int op)
{
    CMP_TO_RICHCMP(PyIscsiNode_compare)
}

static PyObject *PyIscsiNode_str(PyObject *self)
{
	PyIscsiNode *node = (PyIscsiNode *)self;
	char s[1024], tpgt[16] = "";

	if (node->node.tpgt != -1)
		sprintf(tpgt, ",%d", node->node.tpgt);

	snprintf(s, sizeof(s), "%s:%d%s %s", node->node.address,
		 node->node.port, tpgt, node->node.name);

	return PYSTR_FROMSTRING(s);
}

static PyObject *PyIscsiNode_login(PyObject *self)
{
	PyIscsiNode *node = (PyIscsiNode *)self;
	int ret;

	Py_BEGIN_ALLOW_THREADS
	ret = libiscsi_node_login(context, &node->node);
	Py_END_ALLOW_THREADS

	if (ret) {
		PyErr_SetString(PyExc_IOError,
				libiscsi_get_error_string(context));
		return NULL;
	}
	Py_RETURN_NONE;
}

static PyObject *PyIscsiNode_logout(PyObject *self)
{
	PyIscsiNode *node = (PyIscsiNode *)self;

	if (libiscsi_node_logout(context, &node->node)) {
		PyErr_SetString(PyExc_IOError,
				libiscsi_get_error_string(context));
		return NULL;
	}
	Py_RETURN_NONE;
}

static PyObject *PyIscsiNode_setAuth(PyObject *self, PyObject *args,
				     PyObject *kwds)
{
	char *kwlist[] = {"authinfo", NULL};
	PyIscsiNode *node = (PyIscsiNode *)self;
	PyObject *arg;
	const struct libiscsi_auth_info *authinfo = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &arg))
		return NULL;

	if (arg == Py_None) {
		authinfo = NULL;
	} else if (PyObject_IsInstance(arg, (PyObject *)
				       &PyIscsiChapAuthInfo_Type)) {
		PyIscsiChapAuthInfo *pyauthinfo = (PyIscsiChapAuthInfo *)arg;
		authinfo = &pyauthinfo->info;
	} else {
		PyErr_SetString(PyExc_ValueError, "invalid authinfo type");
		return NULL;
	}

	if (libiscsi_node_set_auth(context, &node->node, authinfo)) {
		PyErr_SetString(PyExc_IOError,
				libiscsi_get_error_string(context));
		return NULL;
	}
	Py_RETURN_NONE;
}

static PyObject *PyIscsiNode_getAuth(PyObject *self)
{
	PyIscsiNode *node = (PyIscsiNode *)self;
	PyIscsiChapAuthInfo *pyauthinfo;
	struct libiscsi_auth_info authinfo;

	if (libiscsi_node_get_auth(context, &node->node, &authinfo)) {
		PyErr_SetString(PyExc_IOError,
				libiscsi_get_error_string(context));
		return NULL;
	}

	switch (authinfo.method) {
	case libiscsi_auth_chap:
		pyauthinfo = PyObject_New(PyIscsiChapAuthInfo,
					  &PyIscsiChapAuthInfo_Type);
		if (!pyauthinfo)
			return NULL;

		pyauthinfo->info = authinfo;

		return (PyObject *)pyauthinfo;

	case libiscsi_auth_none:
	default:
		Py_RETURN_NONE;
	}
}

static PyObject *PyIscsiNode_setParameter(PyObject *self, PyObject *args,
					  PyObject *kwds)
{
	char *kwlist[] = {"parameter", "value", NULL};
	PyIscsiNode *node = (PyIscsiNode *)self;
	const char *parameter, *value;

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
					 &parameter, &value))
		return NULL;
	if (check_string(parameter) || check_string(value))
		return NULL;

	if (libiscsi_node_set_parameter(context, &node->node, parameter,
				        value)) {
		PyErr_SetString(PyExc_IOError,
				libiscsi_get_error_string(context));
		return NULL;
	}
	Py_RETURN_NONE;
}

static PyObject *PyIscsiNode_getParameter(PyObject *self, PyObject *args,
					  PyObject *kwds)
{
	char *kwlist[] = {"parameter", NULL};
	PyIscsiNode *node = (PyIscsiNode *)self;
	const char *parameter;
	char value[LIBISCSI_VALUE_MAXLEN];

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &parameter))
		return NULL;
	if (check_string(parameter))
		return NULL;

	if (libiscsi_node_get_parameter(context, &node->node, parameter,
					value)) {
		PyErr_SetString(PyExc_IOError,
				libiscsi_get_error_string(context));
		return NULL;
	}
	return Py_BuildValue("s", value);
}

static struct PyGetSetDef PyIscsiNode_getseters[] = {
	{"name", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
		"name", "name"},
	{"tpgt", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
		"tpgt", "tpgt"},
	{"address", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
		"address", "address"},
	{"port", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
		"port", "port"},
	{"iface", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
		"iface", "iface"},
	{NULL}
};

static struct PyMethodDef  PyIscsiNode_methods[] = {
	{"login", (PyCFunction) PyIscsiNode_login, METH_NOARGS,
		"Log in to the node"},
	{"logout", (PyCFunction) PyIscsiNode_logout, METH_NOARGS,
		"Log out of the node"},
	{"setAuth", (PyCFunction) PyIscsiNode_setAuth,
		METH_VARARGS|METH_KEYWORDS,
		"Set authentication information"},
	{"getAuth", (PyCFunction) PyIscsiNode_getAuth, METH_NOARGS,
		"Get authentication information"},
	{"setParameter", (PyCFunction) PyIscsiNode_setParameter,
		METH_VARARGS|METH_KEYWORDS,
		"Set an iscsi node parameter"},
	{"getParameter", (PyCFunction) PyIscsiNode_getParameter,
		METH_VARARGS|METH_KEYWORDS,
		"Get an iscsi node parameter"},
	{NULL}
};

PyTypeObject PyIscsiNode_Type = {
	PyVarObject_HEAD_INIT(NULL, 0)
	.tp_name = "libiscsi.node",
	.tp_basicsize = sizeof (PyIscsiNode),
	.tp_getset = PyIscsiNode_getseters,
	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
#ifndef IS_PY3K
	| Py_TPFLAGS_CHECKTYPES
#endif
	,
	.tp_methods = PyIscsiNode_methods,
	.tp_richcompare = (richcmpfunc)PyIscsiNode_richcompare,
	.tp_init = PyIscsiNode_init,
	.tp_str = PyIscsiNode_str,
	.tp_new = PyType_GenericNew,
	.tp_doc = "The iscsi node contains iscsi node information.",
};

/***************************************************************************/

static PyObject *pylibiscsi_discover_sendtargets(PyObject *self,
						PyObject *args, PyObject *kwds)
{
	char *kwlist[] = {"address", "port", "authinfo", NULL};
	const char *address = NULL;
	int i, nr_found, port = 3260;
	PyObject *authinfo_arg = NULL;
	const struct libiscsi_auth_info *authinfo = NULL;
	struct libiscsi_node *found_nodes;
	PyObject* found_node_list;
	int ret;

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|iO",
					kwlist, &address, &port,
					&authinfo_arg))
		return NULL;

	if (authinfo_arg) {
		if (PyObject_IsInstance(authinfo_arg, (PyObject *)
					       &PyIscsiChapAuthInfo_Type)) {
			PyIscsiChapAuthInfo *pyauthinfo =
				(PyIscsiChapAuthInfo *)authinfo_arg;
			authinfo = &pyauthinfo->info;
		} else if (authinfo_arg != Py_None) {
			PyErr_SetString(PyExc_ValueError,
				"invalid authinfo type");
			return NULL;
		}
	}

	Py_BEGIN_ALLOW_THREADS
	ret = libiscsi_discover_sendtargets(context, address, port, authinfo,
					    &nr_found, &found_nodes);
	Py_END_ALLOW_THREADS

	if (ret) {
		PyErr_SetString(PyExc_IOError,
				libiscsi_get_error_string(context));
		return NULL;
	}

	if (nr_found == 0)
		Py_RETURN_NONE;

	found_node_list = PyList_New(nr_found);
	if (!found_node_list)
		return NULL;

	for(i = 0; i < nr_found; i++) {
		PyIscsiNode *pynode;
		
		pynode = PyObject_New(PyIscsiNode, &PyIscsiNode_Type);
		if (!pynode) {
			/* This will deref already added nodes for us */
			Py_DECREF(found_node_list);
			return NULL;
		}
		pynode->node = found_nodes[i];
		PyList_SET_ITEM(found_node_list, i, (PyObject *)pynode);
	}

	return found_node_list;	
}

static PyObject *pylibiscsi_discover_firmware(PyObject *self)
{
	int i, nr_found;
	struct libiscsi_node *found_nodes;
	PyObject* found_node_list;

	if (libiscsi_discover_firmware(context, &nr_found, &found_nodes)) {
		PyErr_SetString(PyExc_IOError,
				libiscsi_get_error_string(context));
		return NULL;
	}

	if (nr_found == 0)
		Py_RETURN_NONE;

	found_node_list = PyList_New(nr_found);
	if (!found_node_list)
		return NULL;

	for(i = 0; i < nr_found; i++) {
		PyIscsiNode *pynode;
		
		pynode = PyObject_New(PyIscsiNode, &PyIscsiNode_Type);
		if (!pynode) {
			/* This will deref already added nodes for us */
			Py_DECREF(found_node_list);
			return NULL;
		}
		pynode->node = found_nodes[i];
		PyList_SET_ITEM(found_node_list, i, (PyObject *)pynode);
	}

	return found_node_list;	
}

static PyObject *pylibiscsi_get_firmware_initiator_name(PyObject *self)
{
	char initiatorname[LIBISCSI_VALUE_MAXLEN];

	if (libiscsi_get_firmware_initiator_name(initiatorname)) {
		PyErr_SetString(PyExc_IOError,
				libiscsi_get_error_string(context));
		return NULL;
	}

	return PYSTR_FROMSTRING(initiatorname);
}

static PyMethodDef pylibiscsi_functions[] = {
	{	"discover_sendtargets",
		(PyCFunction)pylibiscsi_discover_sendtargets,
		METH_VARARGS|METH_KEYWORDS,
		"Do sendtargets discovery and return a list of found nodes)"},
	{	"discover_firmware",
		(PyCFunction)pylibiscsi_discover_firmware, METH_NOARGS,
		"Do firmware discovery and return a list of found nodes)"},
	{	"get_firmware_initiator_name",
		(PyCFunction)pylibiscsi_get_firmware_initiator_name,
		METH_NOARGS,
		"Get initator name (iqn) from firmware"},
	{NULL, NULL}
};

#ifdef IS_PY3K
static struct PyModuleDef libiscsi_def = {
	PyModuleDef_HEAD_INIT,
	"libiscsi",
	NULL,
	-1,
	pylibiscsi_functions,
	NULL,
	NULL,
	NULL,
	NULL
};

PyMODINIT_FUNC PyInit_libiscsi(void)
#else
PyMODINIT_FUNC initlibiscsi(void)
#endif
{
	PyObject *m;

	if (!context) /* We may be called more then once */
		context = libiscsi_init();
	if (!context)
		MODINITERROR;

	if (PyType_Ready(&PyIscsiChapAuthInfo_Type) < 0)
		MODINITERROR;

	if (PyType_Ready(&PyIscsiNode_Type) < 0)
		MODINITERROR;

#ifdef IS_PY3K
	m = PyModule_Create(&libiscsi_def);
#else
	m = Py_InitModule("libiscsi", pylibiscsi_functions);
#endif
	Py_INCREF(&PyIscsiChapAuthInfo_Type);
	PyModule_AddObject(m, "chapAuthInfo", (PyObject *) &PyIscsiChapAuthInfo_Type);
	Py_INCREF(&PyIscsiNode_Type);
	PyModule_AddObject(m, "node", (PyObject *) &PyIscsiNode_Type);
#ifdef IS_PY3K
	return m;
#endif
}