Change in vdsm[master]: iscsicred: Support per-host iscsi credentials database
nsoffer at redhat.com
nsoffer at redhat.com
Thu Jul 2 23:21:02 UTC 2015
Nir Soffer has uploaded a new change for review.
Change subject: iscsicred: Support per-host iscsi credentials database
......................................................................
iscsicred: Support per-host iscsi credentials database
Some users need unique host iSCSI credentials for each target, but
Engine supports only same credentials for all hosts. This series adds
support for simple per-host iSCSI credentials database.
Credentials are stored in /etc/vdsm/iscsi-cred/<target-name>
Credentials file format:
username = foo:bar
password = 12345678
This patch adds the iscsicred module, providing readonly access to
the iSCSI credentials database.
Change-Id: I8f6a838f6b8e132d6b0c1a8135f2c28ef1e7f847
Signed-off-by: Nir Soffer <nsoffer at redhat.com>
---
M debian/vdsm.install
M tests/Makefile.am
A tests/iscsicred_test.py
M vdsm.spec.in
M vdsm/storage/Makefile.am
A vdsm/storage/iscsicred.py
M vdsm/supervdsmServer
7 files changed, 207 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/79/43179/1
diff --git a/debian/vdsm.install b/debian/vdsm.install
index 35fcba7..c7cc90e 100644
--- a/debian/vdsm.install
+++ b/debian/vdsm.install
@@ -110,6 +110,7 @@
./usr/share/vdsm/storage/imageSharing.py
./usr/share/vdsm/storage/iscsi.py
./usr/share/vdsm/storage/iscsiadm.py
+./usr/share/vdsm/storage/iscsicred.py
./usr/share/vdsm/storage/localFsSD.py
./usr/share/vdsm/storage/lvm.env
./usr/share/vdsm/storage/lvm.py
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b9b8e72..30ee221 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -56,6 +56,7 @@
iproute2Tests.py \
ipwrapperTests.py \
iscsiTests.py \
+ iscsicred_test.py \
jsonRpcHelper.py \
jsonRpcTests.py \
libvirtconnectionTests.py \
diff --git a/tests/iscsicred_test.py b/tests/iscsicred_test.py
new file mode 100644
index 0000000..715dded
--- /dev/null
+++ b/tests/iscsicred_test.py
@@ -0,0 +1,121 @@
+#
+# Copyright 2015 Red Hat, Inc.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+
+from contextlib import contextmanager
+import os
+
+from testlib import VdsmTestCase
+from testlib import namedTemporaryDir
+from monkeypatch import MonkeyPatchScope, MonkeyClass
+
+from vdsm.password import ProtectedPassword
+from storage import iscsicred
+
+
+class FakeSupervdsm(object):
+
+ def getProxy(self):
+ return self
+
+ def readTargetCredfile(self, targetName):
+ return iscsicred._read_credfile(targetName)
+
+
+ at MonkeyClass(iscsicred, "supervdsm", FakeSupervdsm())
+class IscsicredTests(VdsmTestCase):
+
+ TARGET = "iqn.1994-05.com.redhat:target7"
+
+ def test_not_found(self):
+ self.assertRaises(iscsicred.NotFound, iscsicred.get_credentials,
+ self.TARGET)
+
+ def test_simple(self):
+ data = "".join(["username=foo:bar\n",
+ "password=12345678\n"])
+ with credfile(self.TARGET, data):
+ d = iscsicred.get_credentials(self.TARGET)
+ self.assertEqual(d, {"username": "foo:bar",
+ "password": ProtectedPassword("12345678")})
+
+ def test_whitespace(self):
+ data = "".join([" username = foo:bar \n",
+ " password = 12345678 \n"])
+ with credfile(self.TARGET, data):
+ d = iscsicred.get_credentials(self.TARGET)
+ self.assertEqual(d, {"username": "foo:bar",
+ "password": ProtectedPassword("12345678")})
+
+ def test_any_whitespace(self):
+ data = "".join(["\t\tusername\t\t=\t\tfoo:bar\t\t\n",
+ "\t\tpassword\t\t=\t\t12345678\t\t\n"])
+ with credfile(self.TARGET, data):
+ d = iscsicred.get_credentials(self.TARGET)
+ self.assertEqual(d, {"username": "foo:bar",
+ "password": ProtectedPassword("12345678")})
+
+ def test_empty_lines(self):
+ data = "".join(["\n",
+ "username=foo:bar\n",
+ "\n",
+ "password=12345678\n",
+ "\n"])
+ with credfile(self.TARGET, data):
+ d = iscsicred.get_credentials(self.TARGET)
+ self.assertEqual(d, {"username": "foo:bar",
+ "password": ProtectedPassword("12345678")})
+
+ def test_no_eol(self):
+ data = "".join(["username=foo:bar\n",
+ "password=12345678"])
+ with credfile(self.TARGET, data):
+ d = iscsicred.get_credentials(self.TARGET)
+ self.assertEqual(d, {"username": "foo:bar",
+ "password": ProtectedPassword("12345678")})
+
+ def test_extra_key(self):
+ data = "".join(["username=foo:bar\n",
+ "password=12345678\n"
+ "extra=value\n"])
+ with credfile(self.TARGET, data):
+ d = iscsicred.get_credentials(self.TARGET)
+ self.assertEqual(d, {"username": "foo:bar",
+ "password": ProtectedPassword("12345678"),
+ "extra": "value"})
+
+ def test_empty(self):
+ with credfile(self.TARGET, ""):
+ d = iscsicred.get_credentials(self.TARGET)
+ self.assertEqual(d, {})
+
+ def test_invalid(self):
+ with credfile(self.TARGET, "invalid line\n"):
+ with self.assertRaises(iscsicred.InvalidCredfile):
+ iscsicred.get_credentials(self.TARGET)
+
+
+ at contextmanager
+def credfile(target, data):
+ with namedTemporaryDir() as tmpdir:
+ with MonkeyPatchScope([(iscsicred, "DIR", tmpdir)]):
+ filename = os.path.join(tmpdir, target)
+ with open(filename, "wb") as f:
+ f.write(data)
+ yield
diff --git a/vdsm.spec.in b/vdsm.spec.in
index 58bf5ef..844a041 100644
--- a/vdsm.spec.in
+++ b/vdsm.spec.in
@@ -976,6 +976,7 @@
%{_datadir}/%{vdsm_name}/storage/image.py*
%{_datadir}/%{vdsm_name}/storage/imageSharing.py*
%{_datadir}/%{vdsm_name}/storage/iscsiadm.py*
+%{_datadir}/%{vdsm_name}/storage/iscsicred.py*
%{_datadir}/%{vdsm_name}/storage/iscsi.py*
%{_datadir}/%{vdsm_name}/storage/localFsSD.py*
%{_datadir}/%{vdsm_name}/storage/lvm.env
diff --git a/vdsm/storage/Makefile.am b/vdsm/storage/Makefile.am
index 4ebe0a2..5e64c44 100644
--- a/vdsm/storage/Makefile.am
+++ b/vdsm/storage/Makefile.am
@@ -42,6 +42,7 @@
image.py \
imageSharing.py \
iscsiadm.py \
+ iscsicred.py \
iscsi.py \
localFsSD.py \
lvm.py \
diff --git a/vdsm/storage/iscsicred.py b/vdsm/storage/iscsicred.py
new file mode 100644
index 0000000..7e6bfa3
--- /dev/null
+++ b/vdsm/storage/iscsicred.py
@@ -0,0 +1,76 @@
+#
+# Copyright 2015 Red Hat, Inc.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+"""
+iscsicred - access iscsi credetials database
+
+This module provides readonly access to per-host iscsi credentails. Each host
+in a cluster may be configured with unique credentatils for each iscsi target.
+"""
+
+import errno
+import os
+from vdsm import password
+import supervdsm
+
+DIR = "/etc/vdsm/iscsi-cred"
+
+
+class NotFound(Exception):
+ """ Raised when getting non-existent cred file """
+
+
+class InvalidCredfile(Exception):
+ """ Raised if cred file cannot be parsed """
+
+
+def get_credentials(target_name):
+ data = supervdsm.getProxy().readTargetCredfile(target_name)
+ return _parse_credfile(data)
+
+
+def _read_credfile(target_name):
+ """
+ Read /etc/vdsm/iscsi-cred/target_name.
+
+ Called from supervdsm since credfiles are root-readonly files.
+ """
+ filename = os.path.join(DIR, target_name)
+ try:
+ with open(filename, "rb") as f:
+ return f.read()
+ except IOError as e:
+ if e.errno != errno.ENOENT:
+ raise
+ raise NotFound(target_name)
+
+
+def _parse_credfile(data):
+ cred = {}
+ for line in data.splitlines():
+ line = line.rstrip()
+ if not line:
+ continue
+ if "=" not in line:
+ raise InvalidCredfile("Invalid line: %r" % line)
+ key, value = [s.strip() for s in line.split("=", 1)]
+ if key == "password":
+ value = password.ProtectedPassword(value)
+ cred[key] = value
+ return cred
diff --git a/vdsm/supervdsmServer b/vdsm/supervdsmServer
index 36c67b1..1d2dd41 100755
--- a/vdsm/supervdsmServer
+++ b/vdsm/supervdsmServer
@@ -69,6 +69,7 @@
from storage.iscsi import readSessionInfo as _readSessionInfo
from supervdsm import _SuperVdsmManager
from storage import hba
+from storage import iscsicred
from storage import multipath
from storage.fileUtils import chown, resolveGid, resolveUid
from storage.fileUtils import validateAccess as _validateAccess
@@ -167,6 +168,11 @@
return _readSessionInfo(sessionID)
@logDecorator
+ def readTargetCredfile(self, target):
+ data = iscsicred._read_credfile(target)
+ return password.ProtectedPassword(data)
+
+ @logDecorator
def getPathsStatus(self):
return _getPathsStatus()
--
To view, visit https://gerrit.ovirt.org/43179
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I8f6a838f6b8e132d6b0c1a8135f2c28ef1e7f847
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer at redhat.com>
More information about the vdsm-patches
mailing list