Merge "Conditional Import for FIPS Compliance" into stable/2023.2

This commit is contained in:
Zuul 2024-04-24 20:39:02 +00:00 committed by Gerrit Code Review
commit 3a78c5c5cd
22 changed files with 432 additions and 345 deletions

View File

@ -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.")

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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(

View File

@ -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)

View File

@ -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,

View File

@ -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
View 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

View File

@ -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,

View File

@ -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',

View File

@ -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,

View File

@ -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")

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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()

View 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))

View File

@ -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."""

View File

@ -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 = ['`', '$', '|', '||', ';', '&', '&&', '>', '>>',
'<'] '<']