Merge "Conditional Import for FIPS Compliance" into stable/2023.2
This commit is contained in:
commit
3a78c5c5cd
@ -574,6 +574,10 @@ class ShareBackendException(ManilaException):
|
|||||||
message = _("Share backend error: %(msg)s.")
|
message = _("Share backend error: %(msg)s.")
|
||||||
|
|
||||||
|
|
||||||
|
class RequirementMissing(ManilaException):
|
||||||
|
message = _("Requirement %(req)s is not installed.")
|
||||||
|
|
||||||
|
|
||||||
class ExportLocationNotFound(NotFound):
|
class ExportLocationNotFound(NotFound):
|
||||||
message = _("Export location %(uuid)s could not be found.")
|
message = _("Export location %(uuid)s could not be found.")
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ from manila import exception
|
|||||||
from manila.i18n import _
|
from manila.i18n import _
|
||||||
from manila.share.drivers.dell_emc.common.enas import constants
|
from manila.share.drivers.dell_emc.common.enas import constants
|
||||||
from manila.share.drivers.dell_emc.common.enas import utils as enas_utils
|
from manila.share.drivers.dell_emc.common.enas import utils as enas_utils
|
||||||
from manila import utils
|
from manila import ssh_utils
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
@ -140,11 +140,11 @@ class SSHConnector(object):
|
|||||||
self.password = configuration.emc_nas_password
|
self.password = configuration.emc_nas_password
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
|
|
||||||
self.sshpool = utils.SSHPool(ip=self.storage_ip,
|
self.sshpool = ssh_utils.SSHPool(ip=self.storage_ip,
|
||||||
port=22,
|
port=22,
|
||||||
conn_timeout=None,
|
conn_timeout=None,
|
||||||
login=self.username,
|
login=self.username,
|
||||||
password=self.password)
|
password=self.password)
|
||||||
|
|
||||||
def run_ssh(self, cmd_list, check_exit_code=False):
|
def run_ssh(self, cmd_list, check_exit_code=False):
|
||||||
command = ' '.join(pipes.quote(cmd_arg) for cmd_arg in cmd_list)
|
command = ' '.join(pipes.quote(cmd_arg) for cmd_arg in cmd_list)
|
||||||
|
@ -21,6 +21,7 @@ from oslo_log import log
|
|||||||
|
|
||||||
from manila import exception
|
from manila import exception
|
||||||
from manila.i18n import _
|
from manila.i18n import _
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import utils
|
from manila import utils
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
@ -63,7 +64,7 @@ class SSHExecutor(object):
|
|||||||
"""Callable encapsulating exec through ssh."""
|
"""Callable encapsulating exec through ssh."""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.pool = utils.SSHPool(*args, **kwargs)
|
self.pool = ssh_utils.SSHPool(*args, **kwargs)
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
# argument with identifier 'run_as_root=' is not accepted by
|
# argument with identifier 'run_as_root=' is not accepted by
|
||||||
|
@ -31,6 +31,7 @@ from manila import exception
|
|||||||
from manila.i18n import _
|
from manila.i18n import _
|
||||||
from manila.share import driver
|
from manila.share import driver
|
||||||
from manila.share.drivers import service_instance
|
from manila.share.drivers import service_instance
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import utils
|
from manila import utils
|
||||||
from manila import volume
|
from manila import volume
|
||||||
|
|
||||||
@ -144,13 +145,13 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver):
|
|||||||
connection = self.ssh_connections.get(server['instance_id'])
|
connection = self.ssh_connections.get(server['instance_id'])
|
||||||
ssh_conn_timeout = self.configuration.ssh_conn_timeout
|
ssh_conn_timeout = self.configuration.ssh_conn_timeout
|
||||||
if not connection:
|
if not connection:
|
||||||
ssh_pool = utils.SSHPool(server['ip'],
|
ssh_pool = ssh_utils.SSHPool(server['ip'],
|
||||||
22,
|
22,
|
||||||
ssh_conn_timeout,
|
ssh_conn_timeout,
|
||||||
server['username'],
|
server['username'],
|
||||||
server.get('password'),
|
server.get('password'),
|
||||||
server.get('pk_path'),
|
server.get('pk_path'),
|
||||||
max_size=1)
|
max_size=1)
|
||||||
ssh = ssh_pool.create()
|
ssh = ssh_pool.create()
|
||||||
self.ssh_connections[server['instance_id']] = (ssh_pool, ssh)
|
self.ssh_connections[server['instance_id']] = (ssh_pool, ssh)
|
||||||
else:
|
else:
|
||||||
|
@ -38,6 +38,7 @@ from oslo_utils import units
|
|||||||
from manila import exception
|
from manila import exception
|
||||||
from manila.i18n import _
|
from manila.i18n import _
|
||||||
from manila.share import driver
|
from manila.share import driver
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import utils
|
from manila import utils
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
@ -124,14 +125,14 @@ class HDFSNativeShareDriver(driver.ExecuteMixin, driver.ShareDriver):
|
|||||||
min_size = self.configuration.ssh_min_pool_conn
|
min_size = self.configuration.ssh_min_pool_conn
|
||||||
max_size = self.configuration.ssh_max_pool_conn
|
max_size = self.configuration.ssh_max_pool_conn
|
||||||
|
|
||||||
ssh_pool = utils.SSHPool(host,
|
ssh_pool = ssh_utils.SSHPool(host,
|
||||||
hdfs_ssh_port,
|
hdfs_ssh_port,
|
||||||
ssh_conn_timeout,
|
ssh_conn_timeout,
|
||||||
hdfs_ssh_name,
|
hdfs_ssh_name,
|
||||||
password=password,
|
password=password,
|
||||||
privatekey=privatekey,
|
privatekey=privatekey,
|
||||||
min_size=min_size,
|
min_size=min_size,
|
||||||
max_size=max_size)
|
max_size=max_size)
|
||||||
ssh = ssh_pool.create()
|
ssh = ssh_pool.create()
|
||||||
self.ssh_connections[host] = (ssh_pool, ssh)
|
self.ssh_connections[host] = (ssh_pool, ssh)
|
||||||
else:
|
else:
|
||||||
|
@ -24,6 +24,7 @@ import time
|
|||||||
|
|
||||||
from manila import exception
|
from manila import exception
|
||||||
from manila.i18n import _
|
from manila.i18n import _
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import utils as mutils
|
from manila import utils as mutils
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
@ -633,12 +634,12 @@ class HNASSSHBackend(object):
|
|||||||
commands = ' '.join(commands)
|
commands = ' '.join(commands)
|
||||||
|
|
||||||
if not self.sshpool:
|
if not self.sshpool:
|
||||||
self.sshpool = mutils.SSHPool(ip=self.ip,
|
self.sshpool = ssh_utils.SSHPool(ip=self.ip,
|
||||||
port=self.port,
|
port=self.port,
|
||||||
conn_timeout=None,
|
conn_timeout=None,
|
||||||
login=self.user,
|
login=self.user,
|
||||||
password=self.password,
|
password=self.password,
|
||||||
privatekey=self.priv_key)
|
privatekey=self.priv_key)
|
||||||
with self.sshpool.item() as ssh:
|
with self.sshpool.item() as ssh:
|
||||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
try:
|
try:
|
||||||
|
@ -48,6 +48,7 @@ from manila.i18n import _
|
|||||||
from manila.share import driver
|
from manila.share import driver
|
||||||
from manila.share.drivers.helpers import NFSHelper
|
from manila.share.drivers.helpers import NFSHelper
|
||||||
from manila.share import share_types
|
from manila.share import share_types
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import utils
|
from manila import utils
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
@ -175,14 +176,14 @@ class GPFSShareDriver(driver.ExecuteMixin, driver.GaneshaMixin,
|
|||||||
min_size = self.configuration.ssh_min_pool_conn
|
min_size = self.configuration.ssh_min_pool_conn
|
||||||
max_size = self.configuration.ssh_max_pool_conn
|
max_size = self.configuration.ssh_max_pool_conn
|
||||||
|
|
||||||
self.sshpool = utils.SSHPool(host,
|
self.sshpool = ssh_utils.SSHPool(host,
|
||||||
gpfs_ssh_port,
|
gpfs_ssh_port,
|
||||||
ssh_conn_timeout,
|
ssh_conn_timeout,
|
||||||
gpfs_ssh_login,
|
gpfs_ssh_login,
|
||||||
password=password,
|
password=password,
|
||||||
privatekey=privatekey,
|
privatekey=privatekey,
|
||||||
min_size=min_size,
|
min_size=min_size,
|
||||||
max_size=max_size)
|
max_size=max_size)
|
||||||
try:
|
try:
|
||||||
with self.sshpool.item() as ssh:
|
with self.sshpool.item() as ssh:
|
||||||
return self._gpfs_ssh_execute(
|
return self._gpfs_ssh_execute(
|
||||||
|
@ -24,8 +24,10 @@ from manila.common import constants
|
|||||||
from manila import exception
|
from manila import exception
|
||||||
from manila.i18n import _
|
from manila.i18n import _
|
||||||
from manila.share import utils as share_utils
|
from manila.share import utils as share_utils
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import utils as manila_utils
|
from manila import utils as manila_utils
|
||||||
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -120,21 +122,21 @@ class InfortrendNAS(object):
|
|||||||
|
|
||||||
def _init_connect(self):
|
def _init_connect(self):
|
||||||
if not (self.sshpool and self.ssh):
|
if not (self.sshpool and self.ssh):
|
||||||
self.sshpool = manila_utils.SSHPool(ip=self.nas_ip,
|
self.sshpool = ssh_utils.SSHPool(ip=self.nas_ip,
|
||||||
port=self.port,
|
port=self.port,
|
||||||
conn_timeout=None,
|
conn_timeout=None,
|
||||||
login=self.username,
|
login=self.username,
|
||||||
password=self.password,
|
password=self.password,
|
||||||
privatekey=self.ssh_key)
|
privatekey=self.ssh_key)
|
||||||
self.ssh = self.sshpool.create()
|
self.ssh = self.sshpool.create()
|
||||||
|
|
||||||
if not self.ssh.get_transport().is_active():
|
if not self.ssh.get_transport().is_active():
|
||||||
self.sshpool = manila_utils.SSHPool(ip=self.nas_ip,
|
self.sshpool = ssh_utils.SSHPool(ip=self.nas_ip,
|
||||||
port=self.port,
|
port=self.port,
|
||||||
conn_timeout=None,
|
conn_timeout=None,
|
||||||
login=self.username,
|
login=self.username,
|
||||||
password=self.password,
|
password=self.password,
|
||||||
privatekey=self.ssh_key)
|
privatekey=self.ssh_key)
|
||||||
self.ssh = self.sshpool.create()
|
self.ssh = self.sshpool.create()
|
||||||
|
|
||||||
LOG.debug('NAScmd [%s@%s] start!', self.username, self.nas_ip)
|
LOG.debug('NAScmd [%s@%s] start!', self.username, self.nas_ip)
|
||||||
|
@ -28,6 +28,7 @@ from oslo_utils import excutils
|
|||||||
|
|
||||||
from manila import exception
|
from manila import exception
|
||||||
from manila.i18n import _
|
from manila.i18n import _
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import utils as manila_utils
|
from manila import utils as manila_utils
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
@ -55,7 +56,7 @@ class SSHRunner(object):
|
|||||||
command = ' '.join(cmd_list)
|
command = ' '.join(cmd_list)
|
||||||
if not self.sshpool:
|
if not self.sshpool:
|
||||||
try:
|
try:
|
||||||
self.sshpool = manila_utils.SSHPool(
|
self.sshpool = ssh_utils.SSHPool(
|
||||||
self.host,
|
self.host,
|
||||||
self.port,
|
self.port,
|
||||||
self.ssh_conn_timeout,
|
self.ssh_conn_timeout,
|
||||||
|
@ -26,6 +26,7 @@ from oslo_log import log
|
|||||||
from manila.common import constants
|
from manila.common import constants
|
||||||
from manila import exception
|
from manila import exception
|
||||||
from manila.i18n import _
|
from manila.i18n import _
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import utils
|
from manila import utils
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
@ -92,14 +93,14 @@ class BaseDriverUtil(object):
|
|||||||
min_size = self.configuration.ssh_min_pool_conn
|
min_size = self.configuration.ssh_min_pool_conn
|
||||||
max_size = self.configuration.ssh_max_pool_conn
|
max_size = self.configuration.ssh_max_pool_conn
|
||||||
|
|
||||||
ssh_pool = utils.SSHPool(host,
|
ssh_pool = ssh_utils.SSHPool(host,
|
||||||
remote_ssh_port,
|
remote_ssh_port,
|
||||||
ssh_conn_timeout,
|
ssh_conn_timeout,
|
||||||
ssh_name,
|
ssh_name,
|
||||||
password=password,
|
password=password,
|
||||||
privatekey=private_key,
|
privatekey=private_key,
|
||||||
min_size=min_size,
|
min_size=min_size,
|
||||||
max_size=max_size)
|
max_size=max_size)
|
||||||
ssh = ssh_pool.create()
|
ssh = ssh_pool.create()
|
||||||
self.ssh_connections[host] = (ssh_pool, ssh)
|
self.ssh_connections[host] = (ssh_pool, ssh)
|
||||||
else:
|
else:
|
||||||
|
132
manila/ssh_utils.py
Normal file
132
manila/ssh_utils.py
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Ssh utilities."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from eventlet import pools
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_log import log
|
||||||
|
from oslo_utils.secretutils import md5
|
||||||
|
|
||||||
|
from manila import exception
|
||||||
|
from manila.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import paramiko
|
||||||
|
except ImportError:
|
||||||
|
paramiko = None
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
if getattr(CONF, 'debug', False):
|
||||||
|
logging.getLogger("paramiko").setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
|
def get_fingerprint(self):
|
||||||
|
"""Patch paramiko
|
||||||
|
|
||||||
|
This method needs to be patched to allow paramiko to work under FIPS.
|
||||||
|
Until the patch to do this merges, patch paramiko here.
|
||||||
|
|
||||||
|
TODO(carloss) Remove this when paramiko is patched.
|
||||||
|
See https://github.com/paramiko/paramiko/pull/1928
|
||||||
|
"""
|
||||||
|
return md5(self.asbytes(), usedforsecurity=False).digest()
|
||||||
|
|
||||||
|
|
||||||
|
if paramiko is None:
|
||||||
|
raise exception.RequirementMissing(req='paramiko')
|
||||||
|
|
||||||
|
paramiko.pkey.PKey.get_fingerprint = get_fingerprint
|
||||||
|
|
||||||
|
|
||||||
|
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.path_to_private_key = privatekey
|
||||||
|
super(SSHPool, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def create(self): # pylint: disable=method-hidden
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
look_for_keys = True
|
||||||
|
if self.path_to_private_key:
|
||||||
|
self.path_to_private_key = os.path.expanduser(
|
||||||
|
self.path_to_private_key)
|
||||||
|
look_for_keys = False
|
||||||
|
elif self.password:
|
||||||
|
look_for_keys = False
|
||||||
|
try:
|
||||||
|
LOG.debug("ssh.connect: ip: %s, port: %s, look_for_keys: %s, "
|
||||||
|
"timeout: %s, banner_timeout: %s",
|
||||||
|
self.ip,
|
||||||
|
self.port,
|
||||||
|
look_for_keys,
|
||||||
|
self.conn_timeout,
|
||||||
|
self.conn_timeout)
|
||||||
|
ssh.connect(self.ip,
|
||||||
|
port=self.port,
|
||||||
|
username=self.login,
|
||||||
|
password=self.password,
|
||||||
|
key_filename=self.path_to_private_key,
|
||||||
|
look_for_keys=look_for_keys,
|
||||||
|
timeout=self.conn_timeout,
|
||||||
|
banner_timeout=self.conn_timeout)
|
||||||
|
if self.conn_timeout:
|
||||||
|
transport = ssh.get_transport()
|
||||||
|
transport.set_keepalive(self.conn_timeout)
|
||||||
|
return ssh
|
||||||
|
except Exception as e:
|
||||||
|
msg = _("Check whether private key or password are correctly "
|
||||||
|
"set. Error connecting via ssh: %s") % e
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.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.
|
||||||
|
"""
|
||||||
|
if self.free_items:
|
||||||
|
conn = self.free_items.popleft()
|
||||||
|
if conn:
|
||||||
|
if conn.get_transport().is_active():
|
||||||
|
return conn
|
||||||
|
else:
|
||||||
|
conn.close()
|
||||||
|
return self.create()
|
||||||
|
if self.current_size < self.max_size:
|
||||||
|
created = self.create()
|
||||||
|
self.current_size += 1
|
||||||
|
return created
|
||||||
|
return self.channel.get()
|
||||||
|
|
||||||
|
def remove(self, ssh):
|
||||||
|
"""Close an ssh client and remove it from free_items."""
|
||||||
|
ssh.close()
|
||||||
|
if ssh in self.free_items:
|
||||||
|
self.free_items.remove(ssh)
|
||||||
|
if self.current_size > 0:
|
||||||
|
self.current_size -= 1
|
@ -23,10 +23,10 @@ from oslo_concurrency import processutils
|
|||||||
from manila import exception
|
from manila import exception
|
||||||
from manila.share import configuration as conf
|
from manila.share import configuration as conf
|
||||||
from manila.share.drivers.dell_emc.common.enas import connector
|
from manila.share.drivers.dell_emc.common.enas import connector
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import test
|
from manila import test
|
||||||
from manila.tests.share.drivers.dell_emc.common.enas import fakes
|
from manila.tests.share.drivers.dell_emc.common.enas import fakes
|
||||||
from manila.tests.share.drivers.dell_emc.common.enas import utils as enas_utils
|
from manila.tests.share.drivers.dell_emc.common.enas import utils as enas_utils
|
||||||
from manila import utils
|
|
||||||
|
|
||||||
|
|
||||||
class XMLAPIConnectorTestData(object):
|
class XMLAPIConnectorTestData(object):
|
||||||
@ -165,12 +165,12 @@ class CmdConnectorTest(test.TestCase):
|
|||||||
self.configuration.emc_ssl_cert_path = None
|
self.configuration.emc_ssl_cert_path = None
|
||||||
|
|
||||||
self.sshpool = MockSSHPool()
|
self.sshpool = MockSSHPool()
|
||||||
with mock.patch.object(utils, "SSHPool",
|
with mock.patch.object(ssh_utils, "SSHPool",
|
||||||
mock.Mock(return_value=self.sshpool)):
|
mock.Mock(return_value=self.sshpool)):
|
||||||
self.CmdHelper = connector.SSHConnector(
|
self.CmdHelper = connector.SSHConnector(
|
||||||
configuration=self.configuration, debug=False)
|
configuration=self.configuration, debug=False)
|
||||||
|
|
||||||
utils.SSHPool.assert_called_once_with(
|
ssh_utils.SSHPool.assert_called_once_with(
|
||||||
ip=fakes.FakeData.emc_nas_server,
|
ip=fakes.FakeData.emc_nas_server,
|
||||||
port=22,
|
port=22,
|
||||||
conn_timeout=None,
|
conn_timeout=None,
|
||||||
@ -207,7 +207,7 @@ class CmdConnectorTest(test.TestCase):
|
|||||||
|
|
||||||
sshpool = MockSSHPool()
|
sshpool = MockSSHPool()
|
||||||
|
|
||||||
with mock.patch.object(utils, "SSHPool",
|
with mock.patch.object(ssh_utils, "SSHPool",
|
||||||
mock.Mock(return_value=sshpool)):
|
mock.Mock(return_value=sshpool)):
|
||||||
self.CmdHelper = connector.SSHConnector(self.configuration)
|
self.CmdHelper = connector.SSHConnector(self.configuration)
|
||||||
|
|
||||||
@ -216,7 +216,7 @@ class CmdConnectorTest(test.TestCase):
|
|||||||
cmd_list,
|
cmd_list,
|
||||||
True)
|
True)
|
||||||
|
|
||||||
utils.SSHPool.assert_called_once_with(
|
ssh_utils.SSHPool.assert_called_once_with(
|
||||||
ip=fakes.FakeData.emc_nas_server,
|
ip=fakes.FakeData.emc_nas_server,
|
||||||
port=22,
|
port=22,
|
||||||
conn_timeout=None,
|
conn_timeout=None,
|
||||||
|
@ -128,7 +128,7 @@ class SSHExecutorTestCase(test.TestCase):
|
|||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
def test_call_ssh_exec_object_with_run_as_root(
|
def test_call_ssh_exec_object_with_run_as_root(
|
||||||
self, run_as_root, expected_prefix):
|
self, run_as_root, expected_prefix):
|
||||||
with mock.patch.object(ganesha_utils.utils, 'SSHPool'):
|
with mock.patch.object(ganesha_utils.ssh_utils, 'SSHPool'):
|
||||||
self.execute = ganesha_utils.SSHExecutor()
|
self.execute = ganesha_utils.SSHExecutor()
|
||||||
fake_ssh_object = mock.Mock()
|
fake_ssh_object = mock.Mock()
|
||||||
self.mock_object(self.execute.pool, 'get',
|
self.mock_object(self.execute.pool, 'get',
|
||||||
|
@ -24,6 +24,7 @@ from manila import context
|
|||||||
from manila import exception
|
from manila import exception
|
||||||
import manila.share.configuration as config
|
import manila.share.configuration as config
|
||||||
import manila.share.drivers.hdfs.hdfs_native as hdfs_native
|
import manila.share.drivers.hdfs.hdfs_native as hdfs_native
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import test
|
from manila import test
|
||||||
from manila.tests import fake_share
|
from manila.tests import fake_share
|
||||||
from manila import utils
|
from manila import utils
|
||||||
@ -438,11 +439,13 @@ class HDFSNativeShareDriverTestCase(test.TestCase):
|
|||||||
ssh.get_transport().is_active = mock.Mock(return_value=True)
|
ssh.get_transport().is_active = mock.Mock(return_value=True)
|
||||||
ssh_pool = mock.Mock()
|
ssh_pool = mock.Mock()
|
||||||
ssh_pool.create = mock.Mock(return_value=ssh)
|
ssh_pool.create = mock.Mock(return_value=ssh)
|
||||||
self.mock_object(utils, 'SSHPool', mock.Mock(return_value=ssh_pool))
|
self.mock_object(ssh_utils,
|
||||||
|
'SSHPool',
|
||||||
|
mock.Mock(return_value=ssh_pool))
|
||||||
self.mock_object(processutils, 'ssh_execute',
|
self.mock_object(processutils, 'ssh_execute',
|
||||||
mock.Mock(return_value=ssh_output))
|
mock.Mock(return_value=ssh_output))
|
||||||
result = self._driver._run_ssh(self.local_ip, cmd_list)
|
result = self._driver._run_ssh(self.local_ip, cmd_list)
|
||||||
utils.SSHPool.assert_called_once_with(
|
ssh_utils.SSHPool.assert_called_once_with(
|
||||||
self._driver.configuration.hdfs_namenode_ip,
|
self._driver.configuration.hdfs_namenode_ip,
|
||||||
self._driver.configuration.hdfs_ssh_port,
|
self._driver.configuration.hdfs_ssh_port,
|
||||||
self._driver.configuration.ssh_conn_timeout,
|
self._driver.configuration.ssh_conn_timeout,
|
||||||
@ -464,14 +467,16 @@ class HDFSNativeShareDriverTestCase(test.TestCase):
|
|||||||
ssh.get_transport().is_active = mock.Mock(return_value=True)
|
ssh.get_transport().is_active = mock.Mock(return_value=True)
|
||||||
ssh_pool = mock.Mock()
|
ssh_pool = mock.Mock()
|
||||||
ssh_pool.create = mock.Mock(return_value=ssh)
|
ssh_pool.create = mock.Mock(return_value=ssh)
|
||||||
self.mock_object(utils, 'SSHPool', mock.Mock(return_value=ssh_pool))
|
self.mock_object(ssh_utils,
|
||||||
|
'SSHPool',
|
||||||
|
mock.Mock(return_value=ssh_pool))
|
||||||
self.mock_object(processutils, 'ssh_execute',
|
self.mock_object(processutils, 'ssh_execute',
|
||||||
mock.Mock(side_effect=Exception))
|
mock.Mock(side_effect=Exception))
|
||||||
self.assertRaises(exception.HDFSException,
|
self.assertRaises(exception.HDFSException,
|
||||||
self._driver._run_ssh,
|
self._driver._run_ssh,
|
||||||
self.local_ip,
|
self.local_ip,
|
||||||
cmd_list)
|
cmd_list)
|
||||||
utils.SSHPool.assert_called_once_with(
|
ssh_utils.SSHPool.assert_called_once_with(
|
||||||
self._driver.configuration.hdfs_namenode_ip,
|
self._driver.configuration.hdfs_namenode_ip,
|
||||||
self._driver.configuration.hdfs_ssh_port,
|
self._driver.configuration.hdfs_ssh_port,
|
||||||
self._driver.configuration.ssh_conn_timeout,
|
self._driver.configuration.ssh_conn_timeout,
|
||||||
|
@ -23,8 +23,8 @@ import paramiko
|
|||||||
|
|
||||||
from manila import exception
|
from manila import exception
|
||||||
from manila.share.drivers.hitachi.hnas import ssh
|
from manila.share.drivers.hitachi.hnas import ssh
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import test
|
from manila import test
|
||||||
from manila import utils as mutils
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@ -1506,7 +1506,7 @@ class HNASSSHTestCase(test.TestCase):
|
|||||||
mock.Mock(side_effect=[
|
mock.Mock(side_effect=[
|
||||||
putils.ProcessExecutionError(stderr=msg),
|
putils.ProcessExecutionError(stderr=msg),
|
||||||
putils.ProcessExecutionError(stderr='Invalid!')]))
|
putils.ProcessExecutionError(stderr='Invalid!')]))
|
||||||
self.mock_object(mutils.SSHPool, "item",
|
self.mock_object(ssh_utils.SSHPool, "item",
|
||||||
mock.Mock(return_value=paramiko.SSHClient()))
|
mock.Mock(return_value=paramiko.SSHClient()))
|
||||||
self.mock_object(paramiko.SSHClient, "set_missing_host_key_policy")
|
self.mock_object(paramiko.SSHClient, "set_missing_host_key_policy")
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ from manila import exception
|
|||||||
import manila.share.configuration as config
|
import manila.share.configuration as config
|
||||||
import manila.share.drivers.ibm.gpfs as gpfs
|
import manila.share.drivers.ibm.gpfs as gpfs
|
||||||
from manila.share import share_types
|
from manila.share import share_types
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import test
|
from manila import test
|
||||||
from manila.tests import fake_share
|
from manila.tests import fake_share
|
||||||
from manila import utils
|
from manila import utils
|
||||||
@ -117,7 +118,9 @@ mmcesnfslsexport:nfsexports:HEADER:version:reserved:reserved:Path:Delegations:Cl
|
|||||||
expected_cmd = 'fake cmd'
|
expected_cmd = 'fake cmd'
|
||||||
ssh_pool = mock.Mock()
|
ssh_pool = mock.Mock()
|
||||||
ssh = mock.Mock()
|
ssh = mock.Mock()
|
||||||
self.mock_object(utils, 'SSHPool', mock.Mock(return_value=ssh_pool))
|
self.mock_object(ssh_utils,
|
||||||
|
'SSHPool',
|
||||||
|
mock.Mock(return_value=ssh_pool))
|
||||||
ssh_pool.item = mock.Mock(return_value=ssh)
|
ssh_pool.item = mock.Mock(return_value=ssh)
|
||||||
setattr(ssh, '__enter__', mock.Mock())
|
setattr(ssh, '__enter__', mock.Mock())
|
||||||
setattr(ssh, '__exit__', mock.Mock())
|
setattr(ssh, '__exit__', mock.Mock())
|
||||||
@ -132,7 +135,9 @@ mmcesnfslsexport:nfsexports:HEADER:version:reserved:reserved:Path:Delegations:Cl
|
|||||||
cmd_list = ['fake', 'cmd']
|
cmd_list = ['fake', 'cmd']
|
||||||
ssh_pool = mock.Mock()
|
ssh_pool = mock.Mock()
|
||||||
ssh = mock.Mock()
|
ssh = mock.Mock()
|
||||||
self.mock_object(utils, 'SSHPool', mock.Mock(return_value=ssh_pool))
|
self.mock_object(ssh_utils,
|
||||||
|
'SSHPool',
|
||||||
|
mock.Mock(return_value=ssh_pool))
|
||||||
ssh_pool.item = mock.Mock(return_value=ssh)
|
ssh_pool.item = mock.Mock(return_value=ssh)
|
||||||
self.mock_object(self._driver, '_gpfs_ssh_execute')
|
self.mock_object(self._driver, '_gpfs_ssh_execute')
|
||||||
self.assertRaises(exception.GPFSException,
|
self.assertRaises(exception.GPFSException,
|
||||||
|
@ -30,6 +30,7 @@ from manila import exception
|
|||||||
from manila.share import driver
|
from manila.share import driver
|
||||||
from manila.share.drivers.inspur.instorage import cli_helper
|
from manila.share.drivers.inspur.instorage import cli_helper
|
||||||
from manila.share.drivers.inspur.instorage import instorage
|
from manila.share.drivers.inspur.instorage import instorage
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import test
|
from manila import test
|
||||||
from manila.tests import fake_share
|
from manila.tests import fake_share
|
||||||
from manila import utils as manila_utils
|
from manila import utils as manila_utils
|
||||||
@ -275,7 +276,7 @@ class SSHRunnerTestCase(test.TestCase):
|
|||||||
def test___call___success(self):
|
def test___call___success(self):
|
||||||
mock_csi = self.mock_object(manila_utils, 'check_ssh_injection')
|
mock_csi = self.mock_object(manila_utils, 'check_ssh_injection')
|
||||||
mock_sshpool = mock.Mock(return_value=self.fakePool)
|
mock_sshpool = mock.Mock(return_value=self.fakePool)
|
||||||
self.mock_object(manila_utils, 'SSHPool', mock_sshpool)
|
self.mock_object(ssh_utils, 'SSHPool', mock_sshpool)
|
||||||
mock_se = mock.Mock(return_value='fake_value')
|
mock_se = mock.Mock(return_value='fake_value')
|
||||||
self.mock_object(cli_helper.SSHRunner, '_ssh_execute', mock_se)
|
self.mock_object(cli_helper.SSHRunner, '_ssh_execute', mock_se)
|
||||||
|
|
||||||
@ -303,7 +304,7 @@ class SSHRunnerTestCase(test.TestCase):
|
|||||||
def test___call___ssh_pool_failed(self):
|
def test___call___ssh_pool_failed(self):
|
||||||
mock_csi = self.mock_object(manila_utils, 'check_ssh_injection')
|
mock_csi = self.mock_object(manila_utils, 'check_ssh_injection')
|
||||||
mock_sshpool = mock.Mock(side_effect=paramiko.SSHException())
|
mock_sshpool = mock.Mock(side_effect=paramiko.SSHException())
|
||||||
self.mock_object(manila_utils, 'SSHPool', mock_sshpool)
|
self.mock_object(ssh_utils, 'SSHPool', mock_sshpool)
|
||||||
|
|
||||||
runner = cli_helper.SSHRunner(
|
runner = cli_helper.SSHRunner(
|
||||||
'127.0.0.1', '22', 'fakeuser', 'fakepassword'
|
'127.0.0.1', '22', 'fakeuser', 'fakepassword'
|
||||||
@ -315,7 +316,7 @@ class SSHRunnerTestCase(test.TestCase):
|
|||||||
def test___call___ssh_exec_failed(self):
|
def test___call___ssh_exec_failed(self):
|
||||||
mock_csi = self.mock_object(manila_utils, 'check_ssh_injection')
|
mock_csi = self.mock_object(manila_utils, 'check_ssh_injection')
|
||||||
mock_sshpool = mock.Mock(return_value=self.fakePool)
|
mock_sshpool = mock.Mock(return_value=self.fakePool)
|
||||||
self.mock_object(manila_utils, 'SSHPool', mock_sshpool)
|
self.mock_object(ssh_utils, 'SSHPool', mock_sshpool)
|
||||||
exception = processutils.ProcessExecutionError()
|
exception = processutils.ProcessExecutionError()
|
||||||
mock_se = mock.Mock(side_effect=exception)
|
mock_se = mock.Mock(side_effect=exception)
|
||||||
self.mock_object(cli_helper.SSHRunner, '_ssh_execute', mock_se)
|
self.mock_object(cli_helper.SSHRunner, '_ssh_execute', mock_se)
|
||||||
|
@ -24,6 +24,7 @@ from manila import exception
|
|||||||
import manila.share.configuration as config
|
import manila.share.configuration as config
|
||||||
from manila.share.drivers.maprfs import driver_util as mapru
|
from manila.share.drivers.maprfs import driver_util as mapru
|
||||||
import manila.share.drivers.maprfs.maprfs_native as maprfs
|
import manila.share.drivers.maprfs.maprfs_native as maprfs
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import test
|
from manila import test
|
||||||
from manila.tests import fake_share
|
from manila.tests import fake_share
|
||||||
from manila import utils
|
from manila import utils
|
||||||
@ -797,12 +798,14 @@ class MapRFSNativeShareDriverTestCase(test.TestCase):
|
|||||||
ssh.get_transport().is_active = mock.Mock(return_value=False)
|
ssh.get_transport().is_active = mock.Mock(return_value=False)
|
||||||
ssh_pool = mock.Mock()
|
ssh_pool = mock.Mock()
|
||||||
ssh_pool.create = mock.Mock(return_value=ssh)
|
ssh_pool.create = mock.Mock(return_value=ssh)
|
||||||
self.mock_object(utils, 'SSHPool', mock.Mock(return_value=ssh_pool))
|
self.mock_object(ssh_utils,
|
||||||
|
'SSHPool',
|
||||||
|
mock.Mock(return_value=ssh_pool))
|
||||||
self.mock_object(processutils, 'ssh_execute',
|
self.mock_object(processutils, 'ssh_execute',
|
||||||
mock.Mock(return_value=ssh_output))
|
mock.Mock(return_value=ssh_output))
|
||||||
result = self._driver._maprfs_util._run_ssh(
|
result = self._driver._maprfs_util._run_ssh(
|
||||||
self.local_ip, cmd_list, check_exit_code=False)
|
self.local_ip, cmd_list, check_exit_code=False)
|
||||||
utils.SSHPool.assert_called_once_with(
|
ssh_utils.SSHPool.assert_called_once_with(
|
||||||
self._driver.configuration.maprfs_clinode_ip[0],
|
self._driver.configuration.maprfs_clinode_ip[0],
|
||||||
self._driver.configuration.maprfs_ssh_port,
|
self._driver.configuration.maprfs_ssh_port,
|
||||||
self._driver.configuration.ssh_conn_timeout,
|
self._driver.configuration.ssh_conn_timeout,
|
||||||
@ -824,14 +827,16 @@ class MapRFSNativeShareDriverTestCase(test.TestCase):
|
|||||||
ssh.get_transport().is_active = mock.Mock(return_value=True)
|
ssh.get_transport().is_active = mock.Mock(return_value=True)
|
||||||
ssh_pool = mock.Mock()
|
ssh_pool = mock.Mock()
|
||||||
ssh_pool.create = mock.Mock(return_value=ssh)
|
ssh_pool.create = mock.Mock(return_value=ssh)
|
||||||
self.mock_object(utils, 'SSHPool', mock.Mock(return_value=ssh_pool))
|
self.mock_object(ssh_utils,
|
||||||
|
'SSHPool',
|
||||||
|
mock.Mock(return_value=ssh_pool))
|
||||||
self.mock_object(processutils, 'ssh_execute', mock.Mock(
|
self.mock_object(processutils, 'ssh_execute', mock.Mock(
|
||||||
side_effect=exception.ProcessExecutionError))
|
side_effect=exception.ProcessExecutionError))
|
||||||
self.assertRaises(exception.ProcessExecutionError,
|
self.assertRaises(exception.ProcessExecutionError,
|
||||||
self._driver._maprfs_util._run_ssh,
|
self._driver._maprfs_util._run_ssh,
|
||||||
self.local_ip,
|
self.local_ip,
|
||||||
cmd_list)
|
cmd_list)
|
||||||
utils.SSHPool.assert_called_once_with(
|
ssh_utils.SSHPool.assert_called_once_with(
|
||||||
self._driver.configuration.maprfs_clinode_ip[0],
|
self._driver.configuration.maprfs_clinode_ip[0],
|
||||||
self._driver.configuration.maprfs_ssh_port,
|
self._driver.configuration.maprfs_ssh_port,
|
||||||
self._driver.configuration.ssh_conn_timeout,
|
self._driver.configuration.ssh_conn_timeout,
|
||||||
|
@ -31,6 +31,7 @@ from manila import exception
|
|||||||
import manila.share.configuration
|
import manila.share.configuration
|
||||||
from manila.share.drivers import generic
|
from manila.share.drivers import generic
|
||||||
from manila.share import share_types
|
from manila.share import share_types
|
||||||
|
from manila import ssh_utils
|
||||||
from manila import test
|
from manila import test
|
||||||
from manila.tests import fake_compute
|
from manila.tests import fake_compute
|
||||||
from manila.tests import fake_service_instance
|
from manila.tests import fake_service_instance
|
||||||
@ -1227,14 +1228,16 @@ class GenericShareDriverTestCase(test.TestCase):
|
|||||||
ssh.get_transport().is_active = mock.Mock(return_value=True)
|
ssh.get_transport().is_active = mock.Mock(return_value=True)
|
||||||
ssh_pool = mock.Mock()
|
ssh_pool = mock.Mock()
|
||||||
ssh_pool.create = mock.Mock(return_value=ssh)
|
ssh_pool.create = mock.Mock(return_value=ssh)
|
||||||
self.mock_object(utils, 'SSHPool', mock.Mock(return_value=ssh_pool))
|
self.mock_object(ssh_utils,
|
||||||
|
'SSHPool',
|
||||||
|
mock.Mock(return_value=ssh_pool))
|
||||||
self.mock_object(processutils, 'ssh_execute',
|
self.mock_object(processutils, 'ssh_execute',
|
||||||
mock.Mock(return_value=ssh_output))
|
mock.Mock(return_value=ssh_output))
|
||||||
self._driver.ssh_connections = {}
|
self._driver.ssh_connections = {}
|
||||||
|
|
||||||
result = self._driver._ssh_exec(self.server, cmd)
|
result = self._driver._ssh_exec(self.server, cmd)
|
||||||
|
|
||||||
utils.SSHPool.assert_called_once_with(
|
ssh_utils.SSHPool.assert_called_once_with(
|
||||||
self.server['ip'], 22, ssh_conn_timeout, self.server['username'],
|
self.server['ip'], 22, ssh_conn_timeout, self.server['username'],
|
||||||
self.server['password'], self.server['pk_path'], max_size=1)
|
self.server['password'], self.server['pk_path'], max_size=1)
|
||||||
ssh_pool.create.assert_called_once_with()
|
ssh_pool.create.assert_called_once_with()
|
||||||
|
188
manila/tests/test_ssh_utils.py
Normal file
188
manila/tests/test_ssh_utils.py
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from manila import exception
|
||||||
|
from manila import ssh_utils
|
||||||
|
from manila import test
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
import paramiko
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
|
||||||
|
class FakeSock(object):
|
||||||
|
def settimeout(self, timeout):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FakeTransport(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.active = True
|
||||||
|
self.sock = FakeSock()
|
||||||
|
|
||||||
|
def set_keepalive(self, timeout):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def is_active(self):
|
||||||
|
return self.active
|
||||||
|
|
||||||
|
|
||||||
|
class FakeSSHClient(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.id = uuidutils.generate_uuid()
|
||||||
|
self.transport = FakeTransport()
|
||||||
|
|
||||||
|
def set_missing_host_key_policy(self, policy):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def connect(self, ip, port=22, username=None, password=None,
|
||||||
|
key_filename=None, look_for_keys=None, timeout=10,
|
||||||
|
banner_timeout=10):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_transport(self):
|
||||||
|
return self.transport
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SSHPoolTestCase(test.TestCase):
|
||||||
|
"""Unit test for SSH Connection Pool."""
|
||||||
|
|
||||||
|
def test_single_ssh_connect(self):
|
||||||
|
with mock.patch.object(paramiko, "SSHClient",
|
||||||
|
mock.Mock(return_value=FakeSSHClient())):
|
||||||
|
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
|
||||||
|
|
||||||
|
with sshpool.item() as ssh:
|
||||||
|
second_id = ssh.id
|
||||||
|
|
||||||
|
self.assertEqual(first_id, second_id)
|
||||||
|
paramiko.SSHClient.assert_called_once_with()
|
||||||
|
|
||||||
|
def test_create_ssh_with_password(self):
|
||||||
|
fake_ssh_client = mock.Mock()
|
||||||
|
ssh_pool = ssh_utils.SSHPool("127.0.0.1", 22, 10, "test",
|
||||||
|
password="test")
|
||||||
|
with mock.patch.object(paramiko, "SSHClient",
|
||||||
|
return_value=fake_ssh_client):
|
||||||
|
ssh_pool.create()
|
||||||
|
|
||||||
|
fake_ssh_client.connect.assert_called_once_with(
|
||||||
|
"127.0.0.1", port=22, username="test",
|
||||||
|
password="test", key_filename=None, look_for_keys=False,
|
||||||
|
timeout=10, banner_timeout=10)
|
||||||
|
|
||||||
|
def test_create_ssh_with_key(self):
|
||||||
|
path_to_private_key = "/fakepath/to/privatekey"
|
||||||
|
fake_ssh_client = mock.Mock()
|
||||||
|
ssh_pool = ssh_utils.SSHPool("127.0.0.1", 22, 10,
|
||||||
|
"test",
|
||||||
|
privatekey="/fakepath/to/privatekey")
|
||||||
|
with mock.patch.object(paramiko, "SSHClient",
|
||||||
|
return_value=fake_ssh_client):
|
||||||
|
ssh_pool.create()
|
||||||
|
fake_ssh_client.connect.assert_called_once_with(
|
||||||
|
"127.0.0.1", port=22, username="test", password=None,
|
||||||
|
key_filename=path_to_private_key, look_for_keys=False,
|
||||||
|
timeout=10, banner_timeout=10)
|
||||||
|
|
||||||
|
def test_create_ssh_with_nothing(self):
|
||||||
|
fake_ssh_client = mock.Mock()
|
||||||
|
ssh_pool = ssh_utils.SSHPool("127.0.0.1", 22, 10, "test")
|
||||||
|
with mock.patch.object(paramiko, "SSHClient",
|
||||||
|
return_value=fake_ssh_client):
|
||||||
|
ssh_pool.create()
|
||||||
|
fake_ssh_client.connect.assert_called_once_with(
|
||||||
|
"127.0.0.1", port=22, username="test", password=None,
|
||||||
|
key_filename=None, look_for_keys=True,
|
||||||
|
timeout=10, banner_timeout=10)
|
||||||
|
|
||||||
|
def test_create_ssh_error_connecting(self):
|
||||||
|
attrs = {'connect.side_effect': paramiko.SSHException, }
|
||||||
|
fake_ssh_client = mock.Mock(**attrs)
|
||||||
|
ssh_pool = ssh_utils.SSHPool("127.0.0.1", 22, 10, "test")
|
||||||
|
with mock.patch.object(paramiko, "SSHClient",
|
||||||
|
return_value=fake_ssh_client):
|
||||||
|
self.assertRaises(exception.SSHException, ssh_pool.create)
|
||||||
|
fake_ssh_client.connect.assert_called_once_with(
|
||||||
|
"127.0.0.1", port=22, username="test", password=None,
|
||||||
|
key_filename=None, look_for_keys=True,
|
||||||
|
timeout=10, banner_timeout=10)
|
||||||
|
|
||||||
|
def test_closed_reopend_ssh_connections(self):
|
||||||
|
with mock.patch.object(paramiko, "SSHClient",
|
||||||
|
mock.Mock(return_value=FakeSSHClient())):
|
||||||
|
sshpool = ssh_utils.SSHPool("127.0.0.1", 22, 10,
|
||||||
|
"test", password="test",
|
||||||
|
min_size=1, max_size=2)
|
||||||
|
with sshpool.item() as ssh:
|
||||||
|
first_id = ssh.id
|
||||||
|
with sshpool.item() as ssh:
|
||||||
|
second_id = ssh.id
|
||||||
|
# Close the connection and test for a new connection
|
||||||
|
ssh.get_transport().active = False
|
||||||
|
self.assertEqual(first_id, second_id)
|
||||||
|
paramiko.SSHClient.assert_called_once_with()
|
||||||
|
|
||||||
|
# Expected new ssh pool
|
||||||
|
with mock.patch.object(paramiko, "SSHClient",
|
||||||
|
mock.Mock(return_value=FakeSSHClient())):
|
||||||
|
with sshpool.item() as ssh:
|
||||||
|
third_id = ssh.id
|
||||||
|
self.assertNotEqual(first_id, third_id)
|
||||||
|
paramiko.SSHClient.assert_called_once_with()
|
||||||
|
|
||||||
|
@mock.patch('builtins.open')
|
||||||
|
@mock.patch('paramiko.SSHClient')
|
||||||
|
@mock.patch('os.path.isfile', return_value=True)
|
||||||
|
def test_sshpool_remove(self, mock_isfile, mock_sshclient, mock_open):
|
||||||
|
ssh_to_remove = mock.Mock()
|
||||||
|
mock_sshclient.side_effect = [mock.Mock(), ssh_to_remove, mock.Mock()]
|
||||||
|
sshpool = ssh_utils.SSHPool("127.0.0.1", 22, 10,
|
||||||
|
"test", password="test",
|
||||||
|
min_size=3, max_size=3)
|
||||||
|
|
||||||
|
self.assertIn(ssh_to_remove, list(sshpool.free_items))
|
||||||
|
|
||||||
|
sshpool.remove(ssh_to_remove)
|
||||||
|
|
||||||
|
self.assertNotIn(ssh_to_remove, list(sshpool.free_items))
|
||||||
|
|
||||||
|
@mock.patch('builtins.open')
|
||||||
|
@mock.patch('paramiko.SSHClient')
|
||||||
|
@mock.patch('os.path.isfile', return_value=True)
|
||||||
|
def test_sshpool_remove_object_not_in_pool(self, mock_isfile,
|
||||||
|
mock_sshclient, mock_open):
|
||||||
|
# create an SSH Client that is not a part of sshpool.
|
||||||
|
ssh_to_remove = mock.Mock()
|
||||||
|
mock_sshclient.side_effect = [mock.Mock(), mock.Mock()]
|
||||||
|
|
||||||
|
sshpool = ssh_utils.SSHPool("127.0.0.1", 22, 10,
|
||||||
|
"test", password="test",
|
||||||
|
min_size=2, max_size=2)
|
||||||
|
listBefore = list(sshpool.free_items)
|
||||||
|
|
||||||
|
self.assertNotIn(ssh_to_remove, listBefore)
|
||||||
|
|
||||||
|
sshpool.remove(ssh_to_remove)
|
||||||
|
|
||||||
|
self.assertEqual(listBefore, list(sshpool.free_items))
|
@ -23,8 +23,6 @@ import ddt
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_utils import encodeutils
|
from oslo_utils import encodeutils
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
from oslo_utils import uuidutils
|
|
||||||
import paramiko
|
|
||||||
import tenacity
|
import tenacity
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
@ -196,171 +194,6 @@ class MonkeyPatchTestCase(test.TestCase):
|
|||||||
manila.tests.monkey_patch_example.CALLED_FUNCTION)
|
manila.tests.monkey_patch_example.CALLED_FUNCTION)
|
||||||
|
|
||||||
|
|
||||||
class FakeSSHClient(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.id = uuidutils.generate_uuid()
|
|
||||||
self.transport = FakeTransport()
|
|
||||||
|
|
||||||
def set_missing_host_key_policy(self, policy):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def connect(self, ip, port=22, username=None, password=None,
|
|
||||||
key_filename=None, look_for_keys=None, timeout=10,
|
|
||||||
banner_timeout=10):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_transport(self):
|
|
||||||
return self.transport
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class FakeSock(object):
|
|
||||||
def settimeout(self, timeout):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class FakeTransport(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.active = True
|
|
||||||
self.sock = FakeSock()
|
|
||||||
|
|
||||||
def set_keepalive(self, timeout):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def is_active(self):
|
|
||||||
return self.active
|
|
||||||
|
|
||||||
|
|
||||||
class SSHPoolTestCase(test.TestCase):
|
|
||||||
"""Unit test for SSH Connection Pool."""
|
|
||||||
|
|
||||||
def test_single_ssh_connect(self):
|
|
||||||
with mock.patch.object(paramiko, "SSHClient",
|
|
||||||
mock.Mock(return_value=FakeSSHClient())):
|
|
||||||
sshpool = 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
|
|
||||||
|
|
||||||
with sshpool.item() as ssh:
|
|
||||||
second_id = ssh.id
|
|
||||||
|
|
||||||
self.assertEqual(first_id, second_id)
|
|
||||||
paramiko.SSHClient.assert_called_once_with()
|
|
||||||
|
|
||||||
def test_create_ssh_with_password(self):
|
|
||||||
fake_ssh_client = mock.Mock()
|
|
||||||
ssh_pool = utils.SSHPool("127.0.0.1", 22, 10, "test",
|
|
||||||
password="test")
|
|
||||||
with mock.patch.object(paramiko, "SSHClient",
|
|
||||||
return_value=fake_ssh_client):
|
|
||||||
ssh_pool.create()
|
|
||||||
|
|
||||||
fake_ssh_client.connect.assert_called_once_with(
|
|
||||||
"127.0.0.1", port=22, username="test",
|
|
||||||
password="test", key_filename=None, look_for_keys=False,
|
|
||||||
timeout=10, banner_timeout=10)
|
|
||||||
|
|
||||||
def test_create_ssh_with_key(self):
|
|
||||||
path_to_private_key = "/fakepath/to/privatekey"
|
|
||||||
fake_ssh_client = mock.Mock()
|
|
||||||
ssh_pool = utils.SSHPool("127.0.0.1", 22, 10, "test",
|
|
||||||
privatekey="/fakepath/to/privatekey")
|
|
||||||
with mock.patch.object(paramiko, "SSHClient",
|
|
||||||
return_value=fake_ssh_client):
|
|
||||||
ssh_pool.create()
|
|
||||||
fake_ssh_client.connect.assert_called_once_with(
|
|
||||||
"127.0.0.1", port=22, username="test", password=None,
|
|
||||||
key_filename=path_to_private_key, look_for_keys=False,
|
|
||||||
timeout=10, banner_timeout=10)
|
|
||||||
|
|
||||||
def test_create_ssh_with_nothing(self):
|
|
||||||
fake_ssh_client = mock.Mock()
|
|
||||||
ssh_pool = utils.SSHPool("127.0.0.1", 22, 10, "test")
|
|
||||||
with mock.patch.object(paramiko, "SSHClient",
|
|
||||||
return_value=fake_ssh_client):
|
|
||||||
ssh_pool.create()
|
|
||||||
fake_ssh_client.connect.assert_called_once_with(
|
|
||||||
"127.0.0.1", port=22, username="test", password=None,
|
|
||||||
key_filename=None, look_for_keys=True,
|
|
||||||
timeout=10, banner_timeout=10)
|
|
||||||
|
|
||||||
def test_create_ssh_error_connecting(self):
|
|
||||||
attrs = {'connect.side_effect': paramiko.SSHException, }
|
|
||||||
fake_ssh_client = mock.Mock(**attrs)
|
|
||||||
ssh_pool = utils.SSHPool("127.0.0.1", 22, 10, "test")
|
|
||||||
with mock.patch.object(paramiko, "SSHClient",
|
|
||||||
return_value=fake_ssh_client):
|
|
||||||
self.assertRaises(exception.SSHException, ssh_pool.create)
|
|
||||||
fake_ssh_client.connect.assert_called_once_with(
|
|
||||||
"127.0.0.1", port=22, username="test", password=None,
|
|
||||||
key_filename=None, look_for_keys=True,
|
|
||||||
timeout=10, banner_timeout=10)
|
|
||||||
|
|
||||||
def test_closed_reopend_ssh_connections(self):
|
|
||||||
with mock.patch.object(paramiko, "SSHClient",
|
|
||||||
mock.Mock(return_value=FakeSSHClient())):
|
|
||||||
sshpool = utils.SSHPool("127.0.0.1", 22, 10, "test",
|
|
||||||
password="test", min_size=1, max_size=2)
|
|
||||||
with sshpool.item() as ssh:
|
|
||||||
first_id = ssh.id
|
|
||||||
with sshpool.item() as ssh:
|
|
||||||
second_id = ssh.id
|
|
||||||
# Close the connection and test for a new connection
|
|
||||||
ssh.get_transport().active = False
|
|
||||||
self.assertEqual(first_id, second_id)
|
|
||||||
paramiko.SSHClient.assert_called_once_with()
|
|
||||||
|
|
||||||
# Expected new ssh pool
|
|
||||||
with mock.patch.object(paramiko, "SSHClient",
|
|
||||||
mock.Mock(return_value=FakeSSHClient())):
|
|
||||||
with sshpool.item() as ssh:
|
|
||||||
third_id = ssh.id
|
|
||||||
self.assertNotEqual(first_id, third_id)
|
|
||||||
paramiko.SSHClient.assert_called_once_with()
|
|
||||||
|
|
||||||
@mock.patch('builtins.open')
|
|
||||||
@mock.patch('paramiko.SSHClient')
|
|
||||||
@mock.patch('os.path.isfile', return_value=True)
|
|
||||||
def test_sshpool_remove(self, mock_isfile, mock_sshclient, mock_open):
|
|
||||||
ssh_to_remove = mock.Mock()
|
|
||||||
mock_sshclient.side_effect = [mock.Mock(), ssh_to_remove, mock.Mock()]
|
|
||||||
sshpool = utils.SSHPool("127.0.0.1", 22, 10, "test", password="test",
|
|
||||||
min_size=3, max_size=3)
|
|
||||||
|
|
||||||
self.assertIn(ssh_to_remove, list(sshpool.free_items))
|
|
||||||
|
|
||||||
sshpool.remove(ssh_to_remove)
|
|
||||||
|
|
||||||
self.assertNotIn(ssh_to_remove, list(sshpool.free_items))
|
|
||||||
|
|
||||||
@mock.patch('builtins.open')
|
|
||||||
@mock.patch('paramiko.SSHClient')
|
|
||||||
@mock.patch('os.path.isfile', return_value=True)
|
|
||||||
def test_sshpool_remove_object_not_in_pool(self, mock_isfile,
|
|
||||||
mock_sshclient, mock_open):
|
|
||||||
# create an SSH Client that is not a part of sshpool.
|
|
||||||
ssh_to_remove = mock.Mock()
|
|
||||||
mock_sshclient.side_effect = [mock.Mock(), mock.Mock()]
|
|
||||||
|
|
||||||
sshpool = utils.SSHPool("127.0.0.1", 22, 10, "test", password="test",
|
|
||||||
min_size=2, max_size=2)
|
|
||||||
listBefore = list(sshpool.free_items)
|
|
||||||
|
|
||||||
self.assertNotIn(ssh_to_remove, listBefore)
|
|
||||||
|
|
||||||
sshpool.remove(ssh_to_remove)
|
|
||||||
|
|
||||||
self.assertEqual(listBefore, list(sshpool.free_items))
|
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
@ddt.ddt
|
||||||
class CidrToNetmaskTestCase(test.TestCase):
|
class CidrToNetmaskTestCase(test.TestCase):
|
||||||
"""Unit test for cidr to netmask."""
|
"""Unit test for cidr to netmask."""
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
import contextlib
|
import contextlib
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
|
||||||
import pyclbr
|
import pyclbr
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
@ -29,7 +28,6 @@ import tempfile
|
|||||||
import tenacity
|
import tenacity
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from eventlet import pools
|
|
||||||
import logging
|
import logging
|
||||||
import netaddr
|
import netaddr
|
||||||
from oslo_concurrency import lockutils
|
from oslo_concurrency import lockutils
|
||||||
@ -38,10 +36,8 @@ from oslo_config import cfg
|
|||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
from oslo_utils import netutils
|
from oslo_utils import netutils
|
||||||
from oslo_utils.secretutils import md5
|
|
||||||
from oslo_utils import strutils
|
from oslo_utils import strutils
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
import paramiko
|
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
|
|
||||||
@ -62,21 +58,6 @@ _ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
|
|||||||
synchronized = lockutils.synchronized_with_prefix('manila-')
|
synchronized = lockutils.synchronized_with_prefix('manila-')
|
||||||
|
|
||||||
|
|
||||||
def get_fingerprint(self):
|
|
||||||
"""Patch paramiko
|
|
||||||
|
|
||||||
This method needs to be patched to allow paramiko to work under FIPS.
|
|
||||||
Until the patch to do this merges, patch paramiko here.
|
|
||||||
|
|
||||||
TODO(carloss) Remove this when paramiko is patched.
|
|
||||||
See https://github.com/paramiko/paramiko/pull/1928
|
|
||||||
"""
|
|
||||||
return md5(self.asbytes(), usedforsecurity=False).digest()
|
|
||||||
|
|
||||||
|
|
||||||
paramiko.pkey.PKey.get_fingerprint = get_fingerprint
|
|
||||||
|
|
||||||
|
|
||||||
def isotime(at=None, subsecond=False):
|
def isotime(at=None, subsecond=False):
|
||||||
"""Stringify time in ISO 8601 format."""
|
"""Stringify time in ISO 8601 format."""
|
||||||
|
|
||||||
@ -115,85 +96,6 @@ def execute(*cmd, **kwargs):
|
|||||||
return processutils.execute(*cmd, **kwargs)
|
return processutils.execute(*cmd, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
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.path_to_private_key = privatekey
|
|
||||||
super(SSHPool, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def create(self): # pylint: disable=method-hidden
|
|
||||||
ssh = paramiko.SSHClient()
|
|
||||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
||||||
look_for_keys = True
|
|
||||||
if self.path_to_private_key:
|
|
||||||
self.path_to_private_key = os.path.expanduser(
|
|
||||||
self.path_to_private_key)
|
|
||||||
look_for_keys = False
|
|
||||||
elif self.password:
|
|
||||||
look_for_keys = False
|
|
||||||
try:
|
|
||||||
LOG.debug("ssh.connect: ip: %s, port: %s, look_for_keys: %s, "
|
|
||||||
"timeout: %s, banner_timeout: %s",
|
|
||||||
self.ip,
|
|
||||||
self.port,
|
|
||||||
look_for_keys,
|
|
||||||
self.conn_timeout,
|
|
||||||
self.conn_timeout)
|
|
||||||
ssh.connect(self.ip,
|
|
||||||
port=self.port,
|
|
||||||
username=self.login,
|
|
||||||
password=self.password,
|
|
||||||
key_filename=self.path_to_private_key,
|
|
||||||
look_for_keys=look_for_keys,
|
|
||||||
timeout=self.conn_timeout,
|
|
||||||
banner_timeout=self.conn_timeout)
|
|
||||||
if self.conn_timeout:
|
|
||||||
transport = ssh.get_transport()
|
|
||||||
transport.set_keepalive(self.conn_timeout)
|
|
||||||
return ssh
|
|
||||||
except Exception as e:
|
|
||||||
msg = _("Check whether private key or password are correctly "
|
|
||||||
"set. Error connecting via ssh: %s") % e
|
|
||||||
LOG.error(msg)
|
|
||||||
raise exception.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.
|
|
||||||
"""
|
|
||||||
if self.free_items:
|
|
||||||
conn = self.free_items.popleft()
|
|
||||||
if conn:
|
|
||||||
if conn.get_transport().is_active():
|
|
||||||
return conn
|
|
||||||
else:
|
|
||||||
conn.close()
|
|
||||||
return self.create()
|
|
||||||
if self.current_size < self.max_size:
|
|
||||||
created = self.create()
|
|
||||||
self.current_size += 1
|
|
||||||
return created
|
|
||||||
return self.channel.get()
|
|
||||||
|
|
||||||
def remove(self, ssh):
|
|
||||||
"""Close an ssh client and remove it from free_items."""
|
|
||||||
ssh.close()
|
|
||||||
if ssh in self.free_items:
|
|
||||||
self.free_items.remove(ssh)
|
|
||||||
if self.current_size > 0:
|
|
||||||
self.current_size -= 1
|
|
||||||
|
|
||||||
|
|
||||||
def check_ssh_injection(cmd_list):
|
def check_ssh_injection(cmd_list):
|
||||||
ssh_injection_pattern = ['`', '$', '|', '||', ';', '&', '&&', '>', '>>',
|
ssh_injection_pattern = ['`', '$', '|', '||', ';', '&', '&&', '>', '>>',
|
||||||
'<']
|
'<']
|
||||||
|
Loading…
Reference in New Issue
Block a user