Merge "Fixing SSH actions to use names of private keys"
This commit is contained in:
commit
1ec594f0c1
@ -765,7 +765,9 @@ Input parameters:
|
|||||||
*Required*.
|
*Required*.
|
||||||
- **username** - User name to authenticate on the host.
|
- **username** - User name to authenticate on the host.
|
||||||
- **password** - User password to to authenticate on the host. *Optional.*
|
- **password** - User password to to authenticate on the host. *Optional.*
|
||||||
- **private_key** - Private key string which will be used for authentication on remote host. *Optional.*
|
- **private_key_filename** - Private key file name which will be used for authentication on remote host.
|
||||||
|
All private keys should be on executor host in **<home-user-directory>/.ssh/**.
|
||||||
|
**<home-user-directory>** should refer to user directory under which service is running. *Optional.*
|
||||||
|
|
||||||
**NOTE**: Authentication using key pairs is supported, key should be
|
**NOTE**: Authentication using key pairs is supported, key should be
|
||||||
on Mistral Executor server machine.
|
on Mistral Executor server machine.
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
RETVAL=0
|
RETVAL=0
|
||||||
|
|
||||||
|
sudo chmod -R a+rw /opt/stack/new/
|
||||||
cd /opt/stack/new/
|
cd /opt/stack/new/
|
||||||
|
|
||||||
echo "Repository: $ZUUL_PROJECT"
|
echo "Repository: $ZUUL_PROJECT"
|
||||||
@ -25,7 +26,7 @@ echo "Repository: $ZUUL_PROJECT"
|
|||||||
if [[ "$ZUUL_PROJECT" == "openstack/mistral" ]]; then
|
if [[ "$ZUUL_PROJECT" == "openstack/mistral" ]]; then
|
||||||
cd mistral/
|
cd mistral/
|
||||||
echo "Run mistral API tests"
|
echo "Run mistral API tests"
|
||||||
sudo bash ./functionaltests/run_tests.sh
|
./functionaltests/run_tests.sh
|
||||||
RETVAL=$?
|
RETVAL=$?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ fi
|
|||||||
if [[ RETVAL -eq 0 ]]; then
|
if [[ RETVAL -eq 0 ]]; then
|
||||||
cd /opt/stack/new/python-mistralclient/
|
cd /opt/stack/new/python-mistralclient/
|
||||||
echo "Run mistralclient tests"
|
echo "Run mistralclient tests"
|
||||||
sudo bash ./functionaltests/run_tests.sh
|
./functionaltests/run_tests.sh
|
||||||
RETVAL=$?
|
RETVAL=$?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -37,8 +37,5 @@ MISTRALCLIENT_DIR=/opt/stack/new/python-mistralclient
|
|||||||
# Define PYTHONPATH
|
# Define PYTHONPATH
|
||||||
export PYTHONPATH=$PYTHONPATH:$TEMPEST_DIR
|
export PYTHONPATH=$PYTHONPATH:$TEMPEST_DIR
|
||||||
|
|
||||||
#installing requirements for tempest
|
|
||||||
pip install -r $TEMPEST_DIR/requirements.txt
|
|
||||||
|
|
||||||
pwd
|
pwd
|
||||||
nosetests -sv mistral/tests/functional/
|
nosetests -sv mistral/tests/functional/
|
||||||
|
@ -330,19 +330,20 @@ class SSHAction(base.Action):
|
|||||||
def _execute_cmd_method(self):
|
def _execute_cmd_method(self):
|
||||||
return ssh_utils.execute_command
|
return ssh_utils.execute_command
|
||||||
|
|
||||||
def __init__(self, cmd, host, username, password=None, private_key=None):
|
def __init__(self, cmd, host, username,
|
||||||
|
password=None, private_key_filename=None):
|
||||||
self.cmd = cmd
|
self.cmd = cmd
|
||||||
self.host = host
|
self.host = host
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
self.private_key = private_key
|
self.private_key_filename = private_key_filename
|
||||||
|
|
||||||
self.params = {
|
self.params = {
|
||||||
'cmd': self.cmd,
|
'cmd': self.cmd,
|
||||||
'host': self.host,
|
'host': self.host,
|
||||||
'username': self.username,
|
'username': self.username,
|
||||||
'password': self.password,
|
'password': self.password,
|
||||||
'private_key': self.private_key
|
'private_key_filename': self.private_key_filename
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -386,14 +387,15 @@ class SSHProxiedAction(SSHAction):
|
|||||||
def _execute_cmd_method(self):
|
def _execute_cmd_method(self):
|
||||||
return ssh_utils.execute_command_via_gateway
|
return ssh_utils.execute_command_via_gateway
|
||||||
|
|
||||||
def __init__(self, cmd, host, username, private_key, gateway_host,
|
def __init__(self, cmd, host, username, private_key_filename,
|
||||||
gateway_username=None, password=None, proxy_command=None):
|
gateway_host, gateway_username=None,
|
||||||
|
password=None, proxy_command=None):
|
||||||
super(SSHProxiedAction, self).__init__(
|
super(SSHProxiedAction, self).__init__(
|
||||||
cmd,
|
cmd,
|
||||||
host,
|
host,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
private_key
|
private_key_filename
|
||||||
)
|
)
|
||||||
|
|
||||||
self.gateway_host = gateway_host
|
self.gateway_host = gateway_host
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
from os import path
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
@ -27,6 +29,7 @@ from mistral.utils import ssh_utils
|
|||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
CONF = config.CONF
|
CONF = config.CONF
|
||||||
|
SSH_KEYS_DIRECTORY = path.expanduser("~/.ssh/")
|
||||||
|
|
||||||
|
|
||||||
class SSHActionsTestsV2(base.TestCaseAdvanced):
|
class SSHActionsTestsV2(base.TestCaseAdvanced):
|
||||||
@ -138,6 +141,27 @@ class SSHActionsTestsV2(base.TestCaseAdvanced):
|
|||||||
cls.private_key, cls.public_key = utils.generate_key_pair()
|
cls.private_key, cls.public_key = utils.generate_key_pair()
|
||||||
cls.key_name = 'mistral-functional-tests-key'
|
cls.key_name = 'mistral-functional-tests-key'
|
||||||
|
|
||||||
|
# If ZUUL_PROJECT is specified, it means
|
||||||
|
# tests are running on Jenkins gate.
|
||||||
|
|
||||||
|
if os.environ.get('ZUUL_PROJECT'):
|
||||||
|
cls.key_dir = "/opt/stack/new/.ssh/"
|
||||||
|
|
||||||
|
if not path.exists(cls.key_dir):
|
||||||
|
os.mkdir(cls.key_dir)
|
||||||
|
else:
|
||||||
|
cls.key_dir = SSH_KEYS_DIRECTORY
|
||||||
|
|
||||||
|
utils.save_text_to(
|
||||||
|
cls.private_key,
|
||||||
|
cls.key_dir + cls.key_name,
|
||||||
|
overwrite=True
|
||||||
|
)
|
||||||
|
|
||||||
|
LOG.info(
|
||||||
|
"Private key saved to %s" % cls.key_dir + cls.key_name
|
||||||
|
)
|
||||||
|
|
||||||
# Create keypair in nova.
|
# Create keypair in nova.
|
||||||
cls.mgr.keypairs_client.create_keypair(
|
cls.mgr.keypairs_client.create_keypair(
|
||||||
name=cls.key_name,
|
name=cls.key_name,
|
||||||
@ -184,6 +208,7 @@ class SSHActionsTestsV2(base.TestCaseAdvanced):
|
|||||||
cls.mgr.security_group_rules_client.delete_security_group_rule(
|
cls.mgr.security_group_rules_client.delete_security_group_rule(
|
||||||
cls.ssh_rule_id
|
cls.ssh_rule_id
|
||||||
)
|
)
|
||||||
|
os.remove(cls.key_dir + cls.key_name)
|
||||||
|
|
||||||
super(SSHActionsTestsV2, cls).resource_cleanup()
|
super(SSHActionsTestsV2, cls).resource_cleanup()
|
||||||
|
|
||||||
@ -193,7 +218,7 @@ class SSHActionsTestsV2(base.TestCaseAdvanced):
|
|||||||
'cmd': 'hostname',
|
'cmd': 'hostname',
|
||||||
'host': self.public_vm_ip,
|
'host': self.public_vm_ip,
|
||||||
'username': CONF.scenario.ssh_user,
|
'username': CONF.scenario.ssh_user,
|
||||||
'private_key': self.private_key
|
'private_key_filename': self.key_name
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, body = self.client.create_action_execution(
|
resp, body = self.client.create_action_execution(
|
||||||
@ -217,7 +242,7 @@ class SSHActionsTestsV2(base.TestCaseAdvanced):
|
|||||||
'cmd': 'hostname',
|
'cmd': 'hostname',
|
||||||
'host': guest_vm_ip,
|
'host': guest_vm_ip,
|
||||||
'username': CONF.scenario.ssh_user,
|
'username': CONF.scenario.ssh_user,
|
||||||
'private_key': self.private_key,
|
'private_key_filename': self.key_name,
|
||||||
'gateway_host': self.public_vm_ip,
|
'gateway_host': self.public_vm_ip,
|
||||||
'gateway_username': CONF.scenario.ssh_user
|
'gateway_username': CONF.scenario.ssh_user
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,10 @@
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
|
from mistral import exceptions as exc
|
||||||
from mistral.tests import base
|
from mistral.tests import base
|
||||||
from mistral import utils
|
from mistral import utils
|
||||||
|
from mistral.utils import ssh_utils
|
||||||
|
|
||||||
LEFT = {
|
LEFT = {
|
||||||
'key1': {
|
'key1': {
|
||||||
@ -117,3 +119,15 @@ class UtilsTest(base.BaseTest):
|
|||||||
self.assertEqual(2, input_dict.get('param2'))
|
self.assertEqual(2, input_dict.get('param2'))
|
||||||
self.assertEqual('var3', input_dict.get('param3'))
|
self.assertEqual('var3', input_dict.get('param3'))
|
||||||
self.assertIs(input_dict.get('param1'), utils.NotDefined)
|
self.assertIs(input_dict.get('param1'), utils.NotDefined)
|
||||||
|
|
||||||
|
def test_paramiko_to_private_key(self):
|
||||||
|
self.assertRaises(
|
||||||
|
exc.DataAccessException,
|
||||||
|
ssh_utils._to_paramiko_private_key,
|
||||||
|
"../dir"
|
||||||
|
)
|
||||||
|
self.assertRaises(
|
||||||
|
exc.DataAccessException,
|
||||||
|
ssh_utils._to_paramiko_private_key,
|
||||||
|
"..\\dir"
|
||||||
|
)
|
||||||
|
@ -267,6 +267,7 @@ def tempdir(**kwargs):
|
|||||||
|
|
||||||
if 'dir' not in argdict:
|
if 'dir' not in argdict:
|
||||||
argdict['dir'] = '/tmp/'
|
argdict['dir'] = '/tmp/'
|
||||||
|
|
||||||
tmpdir = tempfile.mkdtemp(**argdict)
|
tmpdir = tempfile.mkdtemp(**argdict)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -281,6 +282,16 @@ def tempdir(**kwargs):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def save_text_to(text, file_path, overwrite=False):
|
||||||
|
if os.path.exists(file_path) and not overwrite:
|
||||||
|
raise exc.DataAccessException(
|
||||||
|
"Cannot save data to file. File %s already exists."
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(file_path, 'w') as f:
|
||||||
|
f.write(text)
|
||||||
|
|
||||||
|
|
||||||
def generate_key_pair(key_length=2048):
|
def generate_key_pair(key_length=2048):
|
||||||
"""Create RSA key pair with specified number of bits in key.
|
"""Create RSA key pair with specified number of bits in key.
|
||||||
Returns tuple of private and public keys.
|
Returns tuple of private and public keys.
|
||||||
@ -295,15 +306,20 @@ def generate_key_pair(key_length=2048):
|
|||||||
'-f', keyfile, # filename of the key file
|
'-f', keyfile, # filename of the key file
|
||||||
'-C', 'Generated-by-Mistral' # key comment
|
'-C', 'Generated-by-Mistral' # key comment
|
||||||
]
|
]
|
||||||
|
|
||||||
if key_length is not None:
|
if key_length is not None:
|
||||||
args.extend(['-b', key_length])
|
args.extend(['-b', key_length])
|
||||||
|
|
||||||
processutils.execute(*args)
|
processutils.execute(*args)
|
||||||
|
|
||||||
if not os.path.exists(keyfile):
|
if not os.path.exists(keyfile):
|
||||||
raise exc.DataAccessException(
|
raise exc.DataAccessException(
|
||||||
"Private key file hasn't been created"
|
"Private key file hasn't been created"
|
||||||
)
|
)
|
||||||
|
|
||||||
private_key = open(keyfile).read()
|
private_key = open(keyfile).read()
|
||||||
public_key_path = keyfile + '.pub'
|
public_key_path = keyfile + '.pub'
|
||||||
|
|
||||||
if not os.path.exists(public_key_path):
|
if not os.path.exists(public_key_path):
|
||||||
raise exc.DataAccessException(
|
raise exc.DataAccessException(
|
||||||
"Public key file hasn't been created"
|
"Public key file hasn't been created"
|
||||||
|
@ -14,12 +14,17 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
from os import path
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import paramiko
|
import paramiko
|
||||||
|
|
||||||
|
from mistral import exceptions as exc
|
||||||
|
|
||||||
|
|
||||||
|
KEY_PATH = path.expanduser("~/.ssh/")
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -33,9 +38,17 @@ def _read_paramimko_stream(recv_func):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _to_paramiko_private_key(private_key_raw, password):
|
def _to_paramiko_private_key(private_key_filename, password=None):
|
||||||
|
if '../' in private_key_filename or '..\\' in private_key_filename:
|
||||||
|
raise exc.DataAccessException(
|
||||||
|
"Private key filename must not contain '..'. "
|
||||||
|
"Actual: %s" % private_key_filename
|
||||||
|
)
|
||||||
|
|
||||||
|
private_key_path = KEY_PATH + private_key_filename
|
||||||
|
|
||||||
return paramiko.RSAKey(
|
return paramiko.RSAKey(
|
||||||
file_obj=six.StringIO(private_key_raw),
|
filename=private_key_path,
|
||||||
password=password
|
password=password
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -87,13 +100,12 @@ def _execute_command(ssh_client, cmd, get_stderr=False,
|
|||||||
_cleanup(ssh_client)
|
_cleanup(ssh_client)
|
||||||
|
|
||||||
|
|
||||||
def execute_command_via_gateway(cmd, host, username, private_key,
|
def execute_command_via_gateway(cmd, host, username, private_key_filename,
|
||||||
gateway_host, gateway_username=None,
|
gateway_host, gateway_username=None,
|
||||||
proxy_command=None, password=None):
|
proxy_command=None, password=None):
|
||||||
LOG.debug('Creating SSH connection')
|
LOG.debug('Creating SSH connection')
|
||||||
|
|
||||||
if isinstance(private_key, six.string_types):
|
private_key = _to_paramiko_private_key(private_key_filename, password)
|
||||||
private_key = _to_paramiko_private_key(private_key, password)
|
|
||||||
|
|
||||||
proxy = None
|
proxy = None
|
||||||
|
|
||||||
@ -138,9 +150,10 @@ def execute_command_via_gateway(cmd, host, username, private_key,
|
|||||||
_cleanup(_proxy_ssh_client)
|
_cleanup(_proxy_ssh_client)
|
||||||
|
|
||||||
|
|
||||||
def execute_command(cmd, host, username, password=None, private_key=None,
|
def execute_command(cmd, host, username, password=None,
|
||||||
get_stderr=False, raise_when_error=True):
|
private_key_filename=None, get_stderr=False,
|
||||||
ssh_client = _connect(host, username, password, private_key)
|
raise_when_error=True):
|
||||||
|
ssh_client = _connect(host, username, password, private_key_filename)
|
||||||
|
|
||||||
LOG.debug("Executing command %s" % cmd)
|
LOG.debug("Executing command %s" % cmd)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user