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(a)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)
+
+
+@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)
+
+
+@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(a)redhat.com>
Nir Soffer has uploaded a new change for review.
Change subject: Experiment with delay after running tc
......................................................................
Experiment with delay after running tc
Change-Id: I37c7928d5e57c3555bd3467ec60293705bbc2ac5
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M tests/nettestlib.py
1 file changed, 6 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/75/44375/1
diff --git a/tests/nettestlib.py b/tests/nettestlib.py
index 51bafc4..8e62cab 100644
--- a/tests/nettestlib.py
+++ b/tests/nettestlib.py
@@ -24,6 +24,7 @@
import platform
import signal
import struct
+import time
from multiprocessing import Process
from nose.plugins.skip import SkipTest
@@ -176,6 +177,11 @@
except ExecError as e:
raise SkipTest("%r has failed: %s\nDo you have Traffic Control kernel "
"modules installed?" % (EXT_TC, e.err))
+ # FIXME: Deleting a bridge fails randomally becasue the bridge is up,
+ # altough we bring the interface down before deleting it. We probably
+ # need to wait for tc events, but monitoring 'tc' cause a segfault.
+ # Hopefully this delay will avoid the failures.
+ time.sleep(0.1)
finally:
dev.delDevice()
--
To view, visit https://gerrit.ovirt.org/44375
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I37c7928d5e57c3555bd3467ec60293705bbc2ac5
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
Nir Soffer has uploaded a new change for review.
Change subject: Test randomness when running under mock
......................................................................
Test randomness when running under mock
Add two tests that will always fail, showing random values generated by
random.randint() and os.urandom().
If we get different random values on each run, this it shows that we
don't need the randomness hack added in https://gerrit.ovirt.org/41079
Change-Id: I90fbc6cbc6ef9a9acbdc2964b92f1c4eb72df97d
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M tests/testlibTests.py
1 file changed, 9 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/56/44356/1
diff --git a/tests/testlibTests.py b/tests/testlibTests.py
index d631aaf..b2a6489 100644
--- a/tests/testlibTests.py
+++ b/tests/testlibTests.py
@@ -149,3 +149,12 @@
def test_expanded_attributes(self):
fn = getattr(self._Permutations, 'fn(False)')
self.assertNotIn(PERMUTATION_ATTR, dir(fn))
+
+
+class TestRandomness(VdsmTestCase):
+ def test_randint(self):
+ import random
+ self.assertEqual([0] * 10, [random.randint(0, 1000) for _ in range(10)])
+ def test_urandom(self):
+ import os
+ self.assertEqual("0" * 20, os.urandom(10).encode('hex'))
--
To view, visit https://gerrit.ovirt.org/44356
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I90fbc6cbc6ef9a9acbdc2964b92f1c4eb72df97d
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>