]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Move SSHPool into ssh_utils.py
authorEric Harney <eharney@redhat.com>
Fri, 25 Jul 2014 19:59:40 +0000 (15:59 -0400)
committerEric Harney <eharney@redhat.com>
Wed, 30 Jul 2014 12:48:03 +0000 (08:48 -0400)
utils.py is loaded by almost everything, but SSH
code and imports (paramiko) are only needed by certain
drivers.

Split this into a separate file to reduce overhead (and
things that can break) for commands like cinder-manage.

Partial-Bug: #1348787

Change-Id: I46896f2bd1fd0de2aedde8e87e255398e5bc3171

cinder/ssh_utils.py [new file with mode: 0644]
cinder/tests/test_huawei_t_dorado.py
cinder/tests/test_utils.py
cinder/utils.py
cinder/volume/drivers/eqlx.py
cinder/volume/drivers/huawei/ssh_common.py
cinder/volume/drivers/san/san.py
cinder/zonemanager/drivers/brocade/brcd_fc_zone_client_cli.py

diff --git a/cinder/ssh_utils.py b/cinder/ssh_utils.py
new file mode 100644 (file)
index 0000000..bbeb030
--- /dev/null
@@ -0,0 +1,120 @@
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# Copyright 2011 Justin Santa Barbara
+# Copyright 2014 Red Hat, Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+"""Utilities related to SSH connection management."""
+
+import os.path
+
+from eventlet import pools
+import paramiko
+
+from cinder import exception
+from cinder.openstack.common.gettextutils import _
+from cinder.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+class SSHPool(pools.Pool):
+    """A simple eventlet pool to hold ssh connections."""
+
+    def __init__(self, ip, port, conn_timeout, login, password=None,
+                 privatekey=None, *args, **kwargs):
+        self.ip = ip
+        self.port = port
+        self.login = login
+        self.password = password
+        self.conn_timeout = conn_timeout if conn_timeout else None
+        self.privatekey = privatekey
+        if 'missing_key_policy' in kwargs.keys():
+            self.missing_key_policy = kwargs.pop('missing_key_policy')
+        else:
+            self.missing_key_policy = paramiko.AutoAddPolicy()
+        if 'hosts_key_file' in kwargs.keys():
+            self.hosts_key_file = kwargs.pop('hosts_key_file')
+        else:
+            self.hosts_key_file = None
+        super(SSHPool, self).__init__(*args, **kwargs)
+
+    def create(self):
+        try:
+            ssh = paramiko.SSHClient()
+            ssh.set_missing_host_key_policy(self.missing_key_policy)
+            if not self.hosts_key_file:
+                ssh.load_system_host_keys()
+            else:
+                ssh.load_host_keys(self.hosts_key_file)
+            if self.password:
+                ssh.connect(self.ip,
+                            port=self.port,
+                            username=self.login,
+                            password=self.password,
+                            timeout=self.conn_timeout)
+            elif self.privatekey:
+                pkfile = os.path.expanduser(self.privatekey)
+                privatekey = paramiko.RSAKey.from_private_key_file(pkfile)
+                ssh.connect(self.ip,
+                            port=self.port,
+                            username=self.login,
+                            pkey=privatekey,
+                            timeout=self.conn_timeout)
+            else:
+                msg = _("Specify a password or private_key")
+                raise exception.CinderException(msg)
+
+            # Paramiko by default sets the socket timeout to 0.1 seconds,
+            # ignoring what we set through the sshclient. This doesn't help for
+            # keeping long lived connections. Hence we have to bypass it, by
+            # overriding it after the transport is initialized. We are setting
+            # the sockettimeout to None and setting a keepalive packet so that,
+            # the server will keep the connection open. All that does is send
+            # a keepalive packet every ssh_conn_timeout seconds.
+            if self.conn_timeout:
+                transport = ssh.get_transport()
+                transport.sock.settimeout(None)
+                transport.set_keepalive(self.conn_timeout)
+            return ssh
+        except Exception as e:
+            msg = _("Error connecting via ssh: %s") % e
+            LOG.error(msg)
+            raise paramiko.SSHException(msg)
+
+    def get(self):
+        """Return an item from the pool, when one is available.
+
+        This may cause the calling greenthread to block. Check if a
+        connection is active before returning it.
+
+        For dead connections create and return a new connection.
+        """
+        conn = super(SSHPool, self).get()
+        if conn:
+            if conn.get_transport().is_active():
+                return conn
+            else:
+                conn.close()
+        return self.create()
+
+    def remove(self, ssh):
+        """Close an ssh client and remove it from free_items."""
+        ssh.close()
+        ssh = None
+        if ssh in self.free_items:
+            self.free_items.pop(ssh)
+        if self.current_size > 0:
+            self.current_size -= 1
index b9bdf175f97622047ae2c63356366f7715e101d8..6f25f006e642df1841eb0ec96428b70b3634703b 100644 (file)
@@ -30,8 +30,8 @@ from xml.etree import ElementTree as ET
 
 from cinder import context
 from cinder import exception
+from cinder import ssh_utils
 from cinder import test
-from cinder import utils
 from cinder.volume import configuration as conf
 from cinder.volume.drivers.huawei import huawei_utils
 from cinder.volume.drivers.huawei import HuaweiVolumeDriver
@@ -1079,7 +1079,7 @@ class HuaweiTISCSIDriverTestCase(test.TestCase):
         self.configuration.append_config_values(mox.IgnoreArg())
 
         self.stubs.Set(time, 'sleep', Fake_sleep)
-        self.stubs.Set(utils, 'SSHPool', FakeSSHPool)
+        self.stubs.Set(ssh_utils, 'SSHPool', FakeSSHPool)
         self.stubs.Set(ssh_common.TseriesCommon, '_change_file_mode',
                        Fake_change_file_mode)
         self._init_driver()
@@ -1473,7 +1473,7 @@ class HuaweiTFCDriverTestCase(test.TestCase):
         self.configuration.append_config_values(mox.IgnoreArg())
 
         self.stubs.Set(time, 'sleep', Fake_sleep)
-        self.stubs.Set(utils, 'SSHPool', FakeSSHPool)
+        self.stubs.Set(ssh_utils, 'SSHPool', FakeSSHPool)
         self.stubs.Set(ssh_common.TseriesCommon, '_change_file_mode',
                        Fake_change_file_mode)
         self._init_driver()
@@ -1705,7 +1705,7 @@ class SSHMethodTestCase(test.TestCase):
         self.configuration.append_config_values(mox.IgnoreArg())
 
         self.stubs.Set(time, 'sleep', Fake_sleep)
-        self.stubs.Set(utils, 'SSHPool', FakeSSHPool)
+        self.stubs.Set(ssh_utils, 'SSHPool', FakeSSHPool)
         self.stubs.Set(ssh_common.TseriesCommon, '_change_file_mode',
                        Fake_change_file_mode)
         Curr_test[0] = 'T'
index ff7b8c7ee07bbbb97e19a1f8cf63a1eda427a3bc..75a5248486660e82c0089952cd3cbc1b4c290f5f 100644 (file)
@@ -31,6 +31,7 @@ from cinder.brick.initiator import linuxfc
 from cinder import exception
 from cinder.openstack.common import processutils as putils
 from cinder.openstack.common import timeutils
+from cinder import ssh_utils
 from cinder import test
 from cinder import utils
 
@@ -870,24 +871,24 @@ class SSHPoolTestCase(test.TestCase):
         mock_sshclient.return_value = FakeSSHClient()
 
         # create with customized setting
-        sshpool = utils.SSHPool("127.0.0.1", 22, 10,
-                                "test",
-                                password="test",
-                                min_size=1,
-                                max_size=1,
-                                missing_key_policy=paramiko.RejectPolicy(),
-                                hosts_key_file='dummy_host_keyfile')
+        sshpool = ssh_utils.SSHPool("127.0.0.1", 22, 10,
+                                    "test",
+                                    password="test",
+                                    min_size=1,
+                                    max_size=1,
+                                    missing_key_policy=paramiko.RejectPolicy(),
+                                    hosts_key_file='dummy_host_keyfile')
         with sshpool.item() as ssh:
             self.assertTrue(isinstance(ssh.get_policy(),
                                        paramiko.RejectPolicy))
             self.assertEqual(ssh.hosts_key_file, 'dummy_host_keyfile')
 
         # create with default setting
-        sshpool = utils.SSHPool("127.0.0.1", 22, 10,
-                                "test",
-                                password="test",
-                                min_size=1,
-                                max_size=1)
+        sshpool = ssh_utils.SSHPool("127.0.0.1", 22, 10,
+                                    "test",
+                                    password="test",
+                                    min_size=1,
+                                    max_size=1)
         with sshpool.item() as ssh:
             self.assertTrue(isinstance(ssh.get_policy(),
                                        paramiko.AutoAddPolicy))
@@ -899,11 +900,11 @@ class SSHPoolTestCase(test.TestCase):
         mock_sshclient.return_value = FakeSSHClient()
 
         # create with password
-        sshpool = utils.SSHPool("127.0.0.1", 22, 10,
-                                "test",
-                                password="test",
-                                min_size=1,
-                                max_size=1)
+        sshpool = ssh_utils.SSHPool("127.0.0.1", 22, 10,
+                                    "test",
+                                    password="test",
+                                    min_size=1,
+                                    max_size=1)
         with sshpool.item() as ssh:
             first_id = ssh.id
 
@@ -914,16 +915,16 @@ class SSHPoolTestCase(test.TestCase):
         mock_sshclient.connect.assert_called_once()
 
         # create with private key
-        sshpool = utils.SSHPool("127.0.0.1", 22, 10,
-                                "test",
-                                privatekey="test",
-                                min_size=1,
-                                max_size=1)
+        sshpool = ssh_utils.SSHPool("127.0.0.1", 22, 10,
+                                    "test",
+                                    privatekey="test",
+                                    min_size=1,
+                                    max_size=1)
         mock_sshclient.connect.assert_called_once()
 
         # attempt to create with no password or private key
         self.assertRaises(paramiko.SSHException,
-                          utils.SSHPool,
+                          ssh_utils.SSHPool,
                           "127.0.0.1", 22, 10,
                           "test",
                           min_size=1,
@@ -932,11 +933,11 @@ class SSHPoolTestCase(test.TestCase):
     @mock.patch('paramiko.SSHClient')
     def test_closed_reopend_ssh_connections(self, mock_sshclient):
         mock_sshclient.return_value = eval('FakeSSHClient')()
-        sshpool = utils.SSHPool("127.0.0.1", 22, 10,
-                                "test",
-                                password="test",
-                                min_size=1,
-                                max_size=4)
+        sshpool = ssh_utils.SSHPool("127.0.0.1", 22, 10,
+                                    "test",
+                                    password="test",
+                                    min_size=1,
+                                    max_size=4)
         with sshpool.item() as ssh:
             mock_sshclient.reset_mock()
             first_id = ssh.id
index 0807e94647aabefd27b70d95c762c7ed580b0d6c..440b634f9769fa187e12981bc680235e301f24c9 100644 (file)
@@ -31,9 +31,7 @@ import sys
 import tempfile
 
 from Crypto.Random import random
-from eventlet import pools
 from oslo.config import cfg
-import paramiko
 import six
 from xml.dom import minidom
 from xml.parsers import expat
@@ -179,96 +177,6 @@ def create_channel(client, width, height):
     return channel
 
 
-class SSHPool(pools.Pool):
-    """A simple eventlet pool to hold ssh connections."""
-
-    def __init__(self, ip, port, conn_timeout, login, password=None,
-                 privatekey=None, *args, **kwargs):
-        self.ip = ip
-        self.port = port
-        self.login = login
-        self.password = password
-        self.conn_timeout = conn_timeout if conn_timeout else None
-        self.privatekey = privatekey
-        if 'missing_key_policy' in kwargs.keys():
-            self.missing_key_policy = kwargs.pop('missing_key_policy')
-        else:
-            self.missing_key_policy = paramiko.AutoAddPolicy()
-        if 'hosts_key_file' in kwargs.keys():
-            self.hosts_key_file = kwargs.pop('hosts_key_file')
-        else:
-            self.hosts_key_file = None
-        super(SSHPool, self).__init__(*args, **kwargs)
-
-    def create(self):
-        try:
-            ssh = paramiko.SSHClient()
-            ssh.set_missing_host_key_policy(self.missing_key_policy)
-            if not self.hosts_key_file:
-                ssh.load_system_host_keys()
-            else:
-                ssh.load_host_keys(self.hosts_key_file)
-            if self.password:
-                ssh.connect(self.ip,
-                            port=self.port,
-                            username=self.login,
-                            password=self.password,
-                            timeout=self.conn_timeout)
-            elif self.privatekey:
-                pkfile = os.path.expanduser(self.privatekey)
-                privatekey = paramiko.RSAKey.from_private_key_file(pkfile)
-                ssh.connect(self.ip,
-                            port=self.port,
-                            username=self.login,
-                            pkey=privatekey,
-                            timeout=self.conn_timeout)
-            else:
-                msg = _("Specify a password or private_key")
-                raise exception.CinderException(msg)
-
-            # Paramiko by default sets the socket timeout to 0.1 seconds,
-            # ignoring what we set through the sshclient. This doesn't help for
-            # keeping long lived connections. Hence we have to bypass it, by
-            # overriding it after the transport is initialized. We are setting
-            # the sockettimeout to None and setting a keepalive packet so that,
-            # the server will keep the connection open. All that does is send
-            # a keepalive packet every ssh_conn_timeout seconds.
-            if self.conn_timeout:
-                transport = ssh.get_transport()
-                transport.sock.settimeout(None)
-                transport.set_keepalive(self.conn_timeout)
-            return ssh
-        except Exception as e:
-            msg = _("Error connecting via ssh: %s") % e
-            LOG.error(msg)
-            raise paramiko.SSHException(msg)
-
-    def get(self):
-        """Return an item from the pool, when one is available.
-
-        This may cause the calling greenthread to block. Check if a
-        connection is active before returning it.
-
-        For dead connections create and return a new connection.
-        """
-        conn = super(SSHPool, self).get()
-        if conn:
-            if conn.get_transport().is_active():
-                return conn
-            else:
-                conn.close()
-        return self.create()
-
-    def remove(self, ssh):
-        """Close an ssh client and remove it from free_items."""
-        ssh.close()
-        ssh = None
-        if ssh in self.free_items:
-            self.free_items.pop(ssh)
-        if self.current_size > 0:
-            self.current_size -= 1
-
-
 def cinderdir():
     import cinder
     return os.path.abspath(cinder.__file__).split('cinder/__init__.py')[0]
index 6eec44de0cbb5ffe9cf87771c0832d73dcfd4c76..1030bc8f29ba482c04a0b766547dff5c4e40b29d 100644 (file)
@@ -28,6 +28,7 @@ from cinder.openstack.common import excutils
 from cinder.openstack.common.gettextutils import _
 from cinder.openstack.common import log as logging
 from cinder.openstack.common import processutils
+from cinder import ssh_utils
 from cinder import utils
 from cinder.volume.drivers.san import SanISCSIDriver
 
@@ -183,14 +184,15 @@ class DellEQLSanISCSIDriver(SanISCSIDriver):
             privatekey = self.configuration.san_private_key
             min_size = self.configuration.ssh_min_pool_conn
             max_size = self.configuration.ssh_max_pool_conn
-            self.sshpool = utils.SSHPool(self.configuration.san_ip,
-                                         self.configuration.san_ssh_port,
-                                         self.configuration.ssh_conn_timeout,
-                                         self.configuration.san_login,
-                                         password=password,
-                                         privatekey=privatekey,
-                                         min_size=min_size,
-                                         max_size=max_size)
+            self.sshpool = ssh_utils.SSHPool(
+                self.configuration.san_ip,
+                self.configuration.san_ssh_port,
+                self.configuration.ssh_conn_timeout,
+                self.configuration.san_login,
+                password=password,
+                privatekey=privatekey,
+                min_size=min_size,
+                max_size=max_size)
         try:
             total_attempts = attempts
             with self.sshpool.item() as ssh:
index 80a374ae6f046e4fa03421f19d553617d5b45c1f..c9fe1e82077baaadc1e8b0d55f14c6210d3b744a 100644 (file)
@@ -32,6 +32,7 @@ from cinder import exception
 from cinder.openstack.common import excutils
 from cinder.openstack.common.gettextutils import _
 from cinder.openstack.common import log as logging
+from cinder import ssh_utils
 from cinder import utils
 from cinder.volume.drivers.huawei import huawei_utils
 from cinder.volume import volume_types
@@ -439,7 +440,8 @@ class TseriesCommon():
         user = self.login_info['UserName']
         pwd = self.login_info['UserPassword']
         if not self.ssh_pool:
-            self.ssh_pool = utils.SSHPool(ip0, 22, 30, user, pwd, max_size=2)
+            self.ssh_pool = ssh_utils.SSHPool(ip0, 22, 30, user, pwd,
+                                              max_size=2)
         ssh_client = None
         while True:
             try:
index 66810417bff095a1b49b6e29c8bedc7fa36c894b..a6436150811eaf21e54a341e03610f399eb29898 100644 (file)
@@ -29,6 +29,7 @@ from cinder.openstack.common import excutils
 from cinder.openstack.common.gettextutils import _
 from cinder.openstack.common import log as logging
 from cinder.openstack.common import processutils
+from cinder import ssh_utils
 from cinder import utils
 from cinder.volume import driver
 
@@ -109,14 +110,15 @@ class SanDriver(driver.VolumeDriver):
             privatekey = self.configuration.san_private_key
             min_size = self.configuration.ssh_min_pool_conn
             max_size = self.configuration.ssh_max_pool_conn
-            self.sshpool = utils.SSHPool(self.configuration.san_ip,
-                                         self.configuration.san_ssh_port,
-                                         self.configuration.ssh_conn_timeout,
-                                         self.configuration.san_login,
-                                         password=password,
-                                         privatekey=privatekey,
-                                         min_size=min_size,
-                                         max_size=max_size)
+            self.sshpool = ssh_utils.SSHPool(
+                self.configuration.san_ip,
+                self.configuration.san_ssh_port,
+                self.configuration.ssh_conn_timeout,
+                self.configuration.san_login,
+                password=password,
+                privatekey=privatekey,
+                min_size=min_size,
+                max_size=max_size)
         last_exception = None
         try:
             with self.sshpool.item() as ssh:
index 839b215f114024d6db8e3b062f5f30e8c3fea074..fb072d3ecdddf5f80c05156d77af019cc8cb1ae5 100644 (file)
@@ -31,6 +31,7 @@ from cinder.openstack.common import excutils
 from cinder.openstack.common.gettextutils import _
 from cinder.openstack.common import log as logging
 from cinder.openstack.common import processutils
+from cinder import ssh_utils
 from cinder import utils
 import cinder.zonemanager.drivers.brocade.fc_zone_constants as ZoneConstant
 
@@ -378,13 +379,13 @@ class BrcdFCZoneClientCLI(object):
         command = ' '. join(cmd_list)
 
         if not self.sshpool:
-            self.sshpool = utils.SSHPool(self.switch_ip,
-                                         self.switch_port,
-                                         None,
-                                         self.switch_user,
-                                         self.switch_pwd,
-                                         min_size=1,
-                                         max_size=5)
+            self.sshpool = ssh_utils.SSHPool(self.switch_ip,
+                                             self.switch_port,
+                                             None,
+                                             self.switch_user,
+                                             self.switch_pwd,
+                                             min_size=1,
+                                             max_size=5)
         last_exception = None
         try:
             with self.sshpool.item() as ssh:
@@ -424,13 +425,13 @@ class BrcdFCZoneClientCLI(object):
         command = ' '. join(cmd_list)
 
         if not self.sshpool:
-            self.sshpool = utils.SSHPool(self.switch_ip,
-                                         self.switch_port,
-                                         None,
-                                         self.switch_user,
-                                         self.switch_pwd,
-                                         min_size=1,
-                                         max_size=5)
+            self.sshpool = ssh_utils.SSHPool(self.switch_ip,
+                                             self.switch_port,
+                                             None,
+                                             self.switch_user,
+                                             self.switch_pwd,
+                                             min_size=1,
+                                             max_size=5)
         stdin, stdout, stderr = None, None, None
         LOG.debug("Executing command via ssh: %s" % command)
         last_exception = None
@@ -499,13 +500,13 @@ class BrcdFCZoneClientCLI(object):
         command = ' '. join(cmd)
         stdout, stderr = None, None
         if not self.sshpool:
-            self.sshpool = utils.SSHPool(self.switch_ip,
-                                         self.switch_port,
-                                         None,
-                                         self.switch_user,
-                                         self.switch_pwd,
-                                         min_size=1,
-                                         max_size=5)
+            self.sshpool = ssh_utils.SSHPool(self.switch_ip,
+                                             self.switch_port,
+                                             None,
+                                             self.switch_user,
+                                             self.switch_pwd,
+                                             min_size=1,
+                                             max_size=5)
         with self.sshpool.item() as ssh:
             LOG.debug('Running cmd (SSH): %s' % command)
             channel = ssh.invoke_shell()