Hello Ayal Baron, Bala.FA, Saggi Mizrahi, Dan Kenigsberg,
I'd like you to do a code review. Please visit
http://gerrit.ovirt.org/18355
to review the following change.
Change subject: gluster: Setup and verify ssl connection between nodes.
......................................................................
gluster: Setup and verify ssl connection between nodes.
This will be used in geo-replication session creation.
Because, there should be password-less ssh access between
at least one node of master volume and one node of slave
volume before creating geo-replication session.
Below new verbs are added
*glusterValidateSshConnection
*glusterSetupSshConnection
Change-Id: Ia6f040b1343998de4f8e28419c63e380240368db
Signed-off-by: Bala.FA <barumuga(a)redhat.com>
Signed-off-by: Timothy Asir <tjeyasin(a)redhat.com>
---
M client/vdsClientGluster.py
M vdsm.spec.in
M vdsm/gluster/api.py
M vdsm/gluster/exception.py
4 files changed, 191 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/55/18355/1
diff --git a/client/vdsClientGluster.py b/client/vdsClientGluster.py
index 90af83e..0ae7ec7 100644
--- a/client/vdsClientGluster.py
+++ b/client/vdsClientGluster.py
@@ -424,6 +424,30 @@
pp.pprint(status)
return status['status']['code'],
status['status']['message']
+ def do_glusterValidateSshConnection(self, args):
+ params = self._eqSplit(args)
+ host = params.get('host', '')
+ fingerprint = params.get('fingerprint', '')
+ username = params.get('username', '')
+
+ status = self.s.glusterValidateSshConnection(host,
+ fingerprint,
+ username)
+ return status['status']['code'],
status['status']['message']
+
+ def do_glusterSetupSshConnection(self, args):
+ params = self._eqSplit(args)
+ host = params.get('host', '')
+ fingerprint = params.get('fingerprint', '')
+ username = params.get('username', '')
+ password = params.get('password', '')
+
+ status = self.s.glusterSetupSshConnection(host,
+ fingerprint,
+ username,
+ password)
+ return status['status']['code'],
status['status']['message']
+
def getGlusterCmdDict(serv):
return \
@@ -705,4 +729,15 @@
'not set'
'(swift, glusterd, smb, memcached)'
)),
+ 'glusterValidateSshConnection': (
+ serv.do_glusterValidateSshConnection,
+ ('host=<host> fingerprint=<fingerprint>
username=<username>',
+ 'validate passwordless ssh connection'
+ )),
+ 'glusterSetupSshConnection': (
+ serv.do_glusterSetupSshConnection,
+ ('host=<host> fingerprint=<fingerprint>
username=<username> '
+ 'password=<password>',
+ 'setup passwordless ssh connection'
+ )),
}
diff --git a/vdsm.spec.in b/vdsm.spec.in
index e2307e0..81d8f9f 100644
--- a/vdsm.spec.in
+++ b/vdsm.spec.in
@@ -524,6 +524,7 @@
Requires: glusterfs-fuse
Requires: glusterfs-rdma
Requires: python-magic
+Requires: python-paramiko
%description gluster
Gluster plugin enables VDSM to serve Gluster functionalities.
diff --git a/vdsm/gluster/api.py b/vdsm/gluster/api.py
index 4bd8308..1d93150 100644
--- a/vdsm/gluster/api.py
+++ b/vdsm/gluster/api.py
@@ -19,11 +19,28 @@
#
from functools import wraps
+import socket
+import paramiko
+import logging
+import os
+import re
from vdsm.define import doneCode
import supervdsm as svdsm
+from vdsm.config import config
+from vdsm import utils
+import exception as ge
_SUCCESS = {'status': doneCode}
+_KEYFILE = config.get('vars', 'trust_store_path') +
'/keys/vdsmkey.pem'
+_sshKeyGenCommandPath = utils.CommandPath("ssh-keygen",
+ "/usr/bin/ssh-keygen",
+ )
+_SSH_COPY_ID_CMD = 'umask 077 && mkdir -p ~/.ssh && ' \
+ 'cat >> ~/.ssh/authorized_keys && if test -x /sbin/restorecon;
' \
+ 'then /sbin/restorecon ~/.ssh ~/.ssh/authorized_keys >/dev/null 2>&1;
' \
+ 'else true; fi'
+paramiko.util.get_logger('paramiko').setLevel(logging.ERROR)
GLUSTER_RPM_PACKAGES = (
('glusterfs', 'glusterfs'),
@@ -59,6 +76,57 @@
wrapper.exportAsVerb = True
return wrapper
+
+
+class VolumeStatus():
+ ONLINE = 'ONLINE'
+ OFFLINE = 'OFFLINE'
+
+
+class HostKeyMatchException(paramiko.SSHException):
+ def __init__(self, hostname, fingerprint, expected_fingerprint):
+ self.err = 'Fingerprint %s of host %s does not match with %s' % \
+ (fingerprint, hostname, expected_fingerprint)
+ paramiko.SSHException.__init__(self, self.err)
+ self.hostname = hostname
+ self.fingerprint = fingerprint
+ self.expected_fingerprint = expected_fingerprint
+
+
+class HostKeyMatchPolicy(paramiko.AutoAddPolicy):
+ def __init__(self, expected_fingerprint):
+ self.expected_fingerprint = expected_fingerprint
+
+ def missing_host_key(self, client, hostname, key):
+ s = paramiko.util.hexlify(key.get_fingerprint())
+ fingerprint = ':'.join(re.findall('..', s))
+ if fingerprint.upper() == self.expected_fingerprint.upper():
+ paramiko.AutoAddPolicy.missing_host_key(self, client, hostname,
+ key)
+ else:
+ raise HostKeyMatchException(hostname, fingerprint,
+ self.expected_fingerprint)
+
+
+class GlusterSsh(paramiko.SSHClient):
+ def __init__(self, hostname, fingerprint, port=22, username=None,
+ password=None, pkey=None, key_filenames=[], timeout=None,
+ allow_agent=True, look_for_keys=True, compress=False):
+ paramiko.SSHClient.__init__(self)
+ key_file_list = []
+ if os.path.exists(_KEYFILE):
+ key_file_list.append(_KEYFILE)
+ key_file_list.append(key_filenames)
+ self.set_missing_host_key_policy(HostKeyMatchPolicy(fingerprint))
+ try:
+ paramiko.SSHClient.connect(self, hostname, port, username,
+ password, pkey, key_file_list, timeout,
+ allow_agent, look_for_keys, compress)
+ except socket.error, e:
+ err = ['%s: %s' % (hostname, e)]
+ raise ge.GlusterSshConnectionFailedException(err=err)
+ except HostKeyMatchException, e:
+ raise ge.GlusterSshHostKeyMismatchException(err=[e.err])
class GlusterApi(object):
@@ -287,6 +355,57 @@
status = self.svdsmProxy.glusterServicesGet(serviceNames)
return {'services': status}
+ def _validateSshConnection(self, hostname, fingerprint, username):
+ try:
+ ssh = GlusterSsh(hostname,
+ fingerprint,
+ username=username)
+ ssh.close()
+ return True
+ except paramiko.SSHException, e:
+ raise ge.GlusterSshHostKeyAuthException(err=[str(e)])
+
+ @exportAsVerb
+ def validateSshConnection(self, hostname, fingerprint, username,
+ options=None):
+ self._validateSshConnection(hostname, fingerprint, username)
+
+ @exportAsVerb
+ def setupSshConnection(self, hostname, fingerprint, username, password,
+ options=None):
+ rc, out, err = utils.execCmd([_sshKeyGenCommandPath.cmd, '-y',
'-f',
+ _KEYFILE])
+ if rc != 0:
+ raise ge.GlusterSshPubKeyGenerationFailedException(rc=rc, err=err)
+
+ try:
+ ssh = GlusterSsh(hostname,
+ fingerprint,
+ username=username,
+ password=password)
+ c = ssh.get_transport().open_session()
+ c.exec_command(_SSH_COPY_ID_CMD)
+ stdin = c.makefile('wb')
+ stdout = c.makefile('rb')
+ stderr = c.makefile_stderr('rb')
+ stdin.write('\n'.join(out) + '\n')
+ stdin.flush()
+ stdin.close()
+ c.shutdown_write()
+ rc = c.recv_exit_status()
+ out = stdout.read().splitlines()
+ err = stderr.read().splitlines()
+ c.close()
+ ssh.close()
+ if rc != 0:
+ raise ge.GlusterSshSetupExecFailedException(rc=rc,
+ out=out,
+ err=err)
+ except paramiko.AuthenticationException, e:
+ raise ge.GlusterSshHostAuthException(err=[str(e)])
+
+ self._validateSshConnection(hostname, fingerprint, username)
+
def getGlusterMethods(gluster):
l = []
diff --git a/vdsm/gluster/exception.py b/vdsm/gluster/exception.py
index c569a9e..c9a0548 100644
--- a/vdsm/gluster/exception.py
+++ b/vdsm/gluster/exception.py
@@ -484,3 +484,39 @@
prefix = "%s: " % (action)
self.message = prefix + "Service action is not supported"
self.err = [self.message]
+
+
+# Ssh
+class GlusterSshException(GlusterException):
+ code = 4500
+ message = "Gluster ssh exception"
+
+
+class GlusterSshConnectionFailedException(GlusterSshException):
+ code = 4501
+ message = "SSH connection failed"
+
+
+class GlusterSshHostKeyMismatchException(GlusterSshException):
+ code = 4502
+ message = "Host key match failed"
+
+
+class GlusterSshHostKeyAuthException(GlusterSshException):
+ code = 4503
+ message = "SSH host key authentication failed"
+
+
+class GlusterSshHostAuthException(GlusterSshException):
+ code = 4504
+ message = "SSH host authentication failed"
+
+
+class GlusterSshPubKeyGenerationFailedException(GlusterSshException):
+ code = 4505
+ message = "SSH public key generation failed"
+
+
+class GlusterSshSetupExecFailedException(GlusterSshException):
+ code = 4506
+ message = "SSH key setup execution failed"
--
To view, visit
http://gerrit.ovirt.org/18355
To unsubscribe, visit
http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ia6f040b1343998de4f8e28419c63e380240368db
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Timothy Asir <tjeyasin(a)redhat.com>
Gerrit-Reviewer: Ayal Baron <abaron(a)redhat.com>
Gerrit-Reviewer: Bala.FA <barumuga(a)redhat.com>
Gerrit-Reviewer: Dan Kenigsberg <danken(a)redhat.com>
Gerrit-Reviewer: Saggi Mizrahi <smizrahi(a)redhat.com>