Hello Ayal Baron, Timothy Asir, Saggi Mizrahi, Federico Simoncelli, Dan 
Kenigsberg,

I'd like you to do a code review.  Please visit

    http://gerrit.ovirt.org/8375

to review the following change.

Change subject: [WIP] Added gluster geo-replication support
......................................................................

[WIP] Added gluster geo-replication support

Below new verbs are added

glusterValidateSshConnection
glusterSetupSshConnection

Change-Id: Ic783abd5f1b63bc5116ce4ff2a3c7be92001a387
Signed-off-by: Bala.FA <[email protected]>
---
M vdsm.spec.in
M vdsm/gluster/api.py
M vdsm/gluster/exception.py
3 files changed, 137 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/75/8375/1

diff --git a/vdsm.spec.in b/vdsm.spec.in
index 22e48fb..020344f 100644
--- a/vdsm.spec.in
+++ b/vdsm.spec.in
@@ -352,6 +352,7 @@
 
 Requires: %{name} = %{version}-%{release}
 Requires: glusterfs glusterfs-server glusterfs-fuse
+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 a825de2..338cb06 100644
--- a/vdsm/gluster/api.py
+++ b/vdsm/gluster/api.py
@@ -19,11 +19,24 @@
 #
 
 from functools import wraps
+import socket
+import paramiko
+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; test -d ~/.ssh || mkdir ~/.ssh ; " \
+    "cat >> ~/.ssh/authorized_keys && (test -x /sbin/restorecon && " \
+    "/sbin/restorecon ~/.ssh ~/.ssh/authorized_keys >/dev/null 2>&1 || true)"
 
 
 def exportAsVerb(func):
@@ -43,6 +56,52 @@
 class VolumeStatus():
     ONLINE = 'ONLINE'
     OFFLINE = 'OFFLINE'
+
+
+class HostKeyMatchException(paramiko.SSHException):
+    def __init__(self, hostname, fingerprint, expected_fingerprint):
+        err = 'Fingerprint of Host key ' \
+            '%s for server %s does not match with %s' % \
+            (fingerprint, hostname, expected_fingerprint)
+        paramiko.SSHException.__init__(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 == 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_filename=None, timeout=None,
+                 allow_agent=True, look_for_keys=True, compress=False):
+        paramiko.SSHClient.__init__(self)
+        key_file_list = [_KEYFILE]
+        if key_filename:
+            key_file_list.append(list(key_filename))
+        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):
@@ -236,6 +295,47 @@
     def volumeProfileStop(self, volumeName, options=None):
         self.svdsmProxy.glusterVolumeProfileStop(volumeName)
 
+    def _validateSshConnection(self, hostname, fingerprint, username):
+        try:
+            ssh = GlusterSsh(hostname,
+                             fingerprint,
+                             username=username)
+            ssh.close()
+            return True
+        except paramiko.AuthenticationException, e:
+            raise ge.GlusterSshHostKeyAuthException(err=[str(e)])
+
+    @exportAsVerb
+    def validateSshConnection(self, hostname, fingerprint, username):
+        return self._validateSshConnection(hostname, fingerprint, username)
+
+    @exportAsVerb
+    def setupSshConnection(self, hostname, fingerprint, username, password):
+        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()
+            stdin, stdout, stderr = c.exec_command(_SSH_COPY_ID_CMD)
+            stdin.write('\n'.join(out) + '\n')
+            stdin.flush()
+            stdin.close()
+            rc = c.recv_exit_status()
+            ssh.close()
+            if rc != 0:
+                err = stderr.read().splitlines()
+                raise ge.GlusterSshSetupExecFailedException(rc=rc, err=err)
+        except paramiko.AuthenticationException, e:
+            raise ge.GlusterSshHostAuthException(err=[str(e)])
+
+        return self._validateSshConnection(hostname, fingerprint, username)
+
 
 def getGlusterMethods(gluster):
     l = []
diff --git a/vdsm/gluster/exception.py b/vdsm/gluster/exception.py
index 6d94ae3..0143a5e 100644
--- a/vdsm/gluster/exception.py
+++ b/vdsm/gluster/exception.py
@@ -377,3 +377,39 @@
 class GlusterHostsListFailedException(GlusterHostException):
     code = 4407
     message = "Hosts list failed"
+
+
+# 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/8375
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic783abd5f1b63bc5116ce4ff2a3c7be92001a387
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Bala.FA <[email protected]>
Gerrit-Reviewer: Ayal Baron <[email protected]>
Gerrit-Reviewer: Dan Kenigsberg <[email protected]>
Gerrit-Reviewer: Federico Simoncelli <[email protected]>
Gerrit-Reviewer: Saggi Mizrahi <[email protected]>
Gerrit-Reviewer: Timothy Asir <[email protected]>
_______________________________________________
vdsm-patches mailing list
[email protected]
https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches

Reply via email to