Prepare instances for SSH access
This patchset is in preparation for a scenario for running a test/benchmark within an instance. Change-Id: I94276237fb5b453b194e58ed00df589a3420a6ed
This commit is contained in:
@@ -38,8 +38,15 @@ class NovaScenario(base.Scenario):
|
|||||||
|
|
||||||
:returns: Created server object
|
:returns: Created server object
|
||||||
"""
|
"""
|
||||||
server = cls.clients("nova").servers.create(server_name, image_id,
|
|
||||||
flavor_id, **kwargs)
|
if 'security_groups' not in kwargs:
|
||||||
|
kwargs['security_groups'] = ['rally_open']
|
||||||
|
else:
|
||||||
|
if 'rally_open' not in kwargs['security_groups']:
|
||||||
|
kwargs['security_groups'].append('rally_open')
|
||||||
|
|
||||||
|
server = cls.clients("nova").servers.create(
|
||||||
|
server_name, image_id, flavor_id, **kwargs)
|
||||||
# NOTE(msdubov): It is reasonable to wait 5 secs before starting to
|
# NOTE(msdubov): It is reasonable to wait 5 secs before starting to
|
||||||
# check whether the server is ready => less API calls.
|
# check whether the server is ready => less API calls.
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
@@ -112,6 +112,65 @@ def create_openstack_clients(users_endpoints, keys):
|
|||||||
)) for cl in client_managers
|
)) for cl in client_managers
|
||||||
]
|
]
|
||||||
|
|
||||||
|
_prepare_for_instance_ssh(clients)
|
||||||
|
return clients
|
||||||
|
|
||||||
|
|
||||||
|
def _prepare_for_instance_ssh(clients):
|
||||||
|
"""Generate and store SSH keys, allow access to port 22.
|
||||||
|
|
||||||
|
In order to run tests on instances it is necessary to have SSH access.
|
||||||
|
This function generates an SSH key pair per user which is stored in the
|
||||||
|
clients dictionary. The public key is also submitted to nova via the
|
||||||
|
novaclient.
|
||||||
|
|
||||||
|
A security group rule is created to allow access to instances on port 22.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for client_dict in clients:
|
||||||
|
nova_client = client_dict['nova']
|
||||||
|
|
||||||
|
if ('rally_ssh_key' not in
|
||||||
|
[k.name for k in nova_client.keypairs.list()]):
|
||||||
|
keypair = nova_client.keypairs.create('rally_ssh_key')
|
||||||
|
client_dict['ssh_key_pair'] = dict(private=keypair.private_key,
|
||||||
|
public=keypair.public_key)
|
||||||
|
|
||||||
|
if 'rally_open' not in [sg.name for sg in
|
||||||
|
nova_client.security_groups.list()]:
|
||||||
|
rally_open = nova_client.security_groups.create(
|
||||||
|
'rally_open',
|
||||||
|
'Allow all access to VMs for benchmarking'
|
||||||
|
)
|
||||||
|
rally_open = nova_client.security_groups.find(name='rally_open')
|
||||||
|
|
||||||
|
rules_to_add = [dict(ip_protocol='tcp',
|
||||||
|
to_port=65536,
|
||||||
|
from_port=1,
|
||||||
|
ip_range=dict(cidr='0.0.0.0/0')),
|
||||||
|
dict(ip_protocol='udp',
|
||||||
|
to_port=65536,
|
||||||
|
from_port=1,
|
||||||
|
ip_range=dict(cidr='0.0.0.0/0')),
|
||||||
|
dict(ip_protocol='icmp',
|
||||||
|
to_port=255,
|
||||||
|
from_port=-1,
|
||||||
|
ip_range=dict(cidr='0.0.0.0/0'))
|
||||||
|
]
|
||||||
|
|
||||||
|
def rule_match(criteria, existing_rule):
|
||||||
|
return all(existing_rule[key] == value
|
||||||
|
for key, value in criteria.iteritems())
|
||||||
|
|
||||||
|
for new_rule in rules_to_add:
|
||||||
|
if not any(rule_match(new_rule, existing_rule) for existing_rule
|
||||||
|
in rally_open.rules):
|
||||||
|
nova_client.security_group_rules.create(
|
||||||
|
rally_open.id,
|
||||||
|
from_port=new_rule['from_port'],
|
||||||
|
to_port=new_rule['to_port'],
|
||||||
|
ip_protocol=new_rule['ip_protocol'],
|
||||||
|
cidr=new_rule['ip_range']['cidr'])
|
||||||
return clients
|
return clients
|
||||||
|
|
||||||
|
|
||||||
|
@@ -20,6 +20,7 @@ import random
|
|||||||
import select
|
import select
|
||||||
import socket
|
import socket
|
||||||
import string
|
import string
|
||||||
|
from StringIO import StringIO
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from rally import exceptions
|
from rally import exceptions
|
||||||
@@ -31,27 +32,46 @@ LOG = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class SSH(object):
|
class SSH(object):
|
||||||
"""SSH common functions."""
|
"""SSH common functions."""
|
||||||
|
STDOUT_INDEX = 0
|
||||||
|
STDERR_INDEX = 1
|
||||||
|
|
||||||
def __init__(self, ip, user, port=22, key=None, timeout=1800):
|
def __init__(self, ip, user, port=22, key=None, key_type="file",
|
||||||
|
timeout=1800):
|
||||||
"""Initialize SSH client with ip, username and the default values.
|
"""Initialize SSH client with ip, username and the default values.
|
||||||
|
|
||||||
timeout - the timeout for execution of the command
|
timeout - the timeout for execution of the command
|
||||||
|
key - path to private key file, or string containing actual key
|
||||||
|
key_type - "file" for key path, "string" for actual key
|
||||||
"""
|
"""
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.port = port
|
self.port = port
|
||||||
self.user = user
|
self.user = user
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.client = None
|
self.client = None
|
||||||
if key:
|
self.key = key
|
||||||
self.key = key
|
self.key_type = key_type
|
||||||
else:
|
if not self.key:
|
||||||
|
#Guess location of user's private key if no key is specified.
|
||||||
self.key = os.path.expanduser('~/.ssh/id_rsa')
|
self.key = os.path.expanduser('~/.ssh/id_rsa')
|
||||||
|
|
||||||
def _get_ssh_connection(self):
|
def _get_ssh_connection(self):
|
||||||
self.client = paramiko.SSHClient()
|
self.client = paramiko.SSHClient()
|
||||||
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
self.client.connect(self.ip, username=self.user,
|
connect_params = {
|
||||||
key_filename=self.key, port=self.port)
|
'hostname': self.ip,
|
||||||
|
'port': self.port,
|
||||||
|
'username': self.user
|
||||||
|
}
|
||||||
|
|
||||||
|
# NOTE(hughsaunders): Set correct paramiko parameter names for each
|
||||||
|
# method of supplying a key.
|
||||||
|
if self.key_type == 'file':
|
||||||
|
connect_params['key_filename'] = self.key
|
||||||
|
else:
|
||||||
|
connect_params['pkey'] = paramiko.RSAKey(
|
||||||
|
file_obj=StringIO(self.key))
|
||||||
|
|
||||||
|
self.client.connect(**connect_params)
|
||||||
|
|
||||||
def _is_timed_out(self, start_time):
|
def _is_timed_out(self, start_time):
|
||||||
return (time.time() - self.timeout) > start_time
|
return (time.time() - self.timeout) > start_time
|
||||||
@@ -136,14 +156,17 @@ class SSH(object):
|
|||||||
ftp.put(os.path.expanduser(source), destination)
|
ftp.put(os.path.expanduser(source), destination)
|
||||||
ftp.close()
|
ftp.close()
|
||||||
|
|
||||||
def execute_script(self, script, enterpreter='/bin/sh'):
|
def execute_script(self, script, interpreter='/bin/sh',
|
||||||
|
get_stdout=False, get_stderr=False):
|
||||||
"""Execute the specified local script on the remote server."""
|
"""Execute the specified local script on the remote server."""
|
||||||
destination = '/tmp/' + ''.join(
|
destination = '/tmp/' + ''.join(
|
||||||
random.choice(string.lowercase) for i in range(16))
|
random.choice(string.lowercase) for i in range(16))
|
||||||
|
|
||||||
self.upload(script, destination)
|
self.upload(script, destination)
|
||||||
self.execute('%s %s' % (enterpreter, destination))
|
streams = self.execute('%s %s' % (interpreter, destination),
|
||||||
|
get_stdout=get_stdout, get_stderr=get_stderr)
|
||||||
self.execute('rm %s' % destination)
|
self.execute('rm %s' % destination)
|
||||||
|
return streams
|
||||||
|
|
||||||
def wait(self, timeout=120, interval=1):
|
def wait(self, timeout=120, interval=1):
|
||||||
"""Wait for the host will be available via ssh."""
|
"""Wait for the host will be available via ssh."""
|
||||||
|
@@ -58,7 +58,7 @@ class NovaScenarioTestCase(test.TestCase):
|
|||||||
fc.get_nova_client = lambda: fake_nova
|
fc.get_nova_client = lambda: fake_nova
|
||||||
fsm = fakes.FakeServerManager(fake_nova.images)
|
fsm = fakes.FakeServerManager(fake_nova.images)
|
||||||
fake_server = fsm.create("s1", "i1", 1)
|
fake_server = fsm.create("s1", "i1", 1)
|
||||||
fsm.create = lambda name, iid, fid: fake_server
|
fsm.create = lambda name, iid, fid, **kwargs: fake_server
|
||||||
fake_nova.servers = fsm
|
fake_nova.servers = fsm
|
||||||
fake_image_id = fsm.create_image(fake_server, 'img')
|
fake_image_id = fsm.create_image(fake_server, 'img')
|
||||||
fake_image = fsm.images.get(fake_image_id)
|
fake_image = fsm.images.get(fake_image_id)
|
||||||
|
@@ -62,9 +62,10 @@ class ScenarioTestCase(test.TestCase):
|
|||||||
endpoints = srunner._create_temp_tenants_and_users(
|
endpoints = srunner._create_temp_tenants_and_users(
|
||||||
tenants, users_per_tenant)
|
tenants, users_per_tenant)
|
||||||
self.assertEqual(len(endpoints), tenants * users_per_tenant)
|
self.assertEqual(len(endpoints), tenants * users_per_tenant)
|
||||||
endpoint_keys = set(["username", "password", "tenant_name", "uri"])
|
endpoint_keys = set(["username", "password", "tenant_name",
|
||||||
|
"uri"])
|
||||||
for endpoint in endpoints:
|
for endpoint in endpoints:
|
||||||
self.assertEqual(set(endpoint.keys()), endpoint_keys)
|
self.assertTrue(endpoint_keys.issubset(endpoint.keys()))
|
||||||
|
|
||||||
def test_run_scenario(self):
|
def test_run_scenario(self):
|
||||||
with mock.patch("rally.benchmark.utils.osclients") as mock_osclients:
|
with mock.patch("rally.benchmark.utils.osclients") as mock_osclients:
|
||||||
@@ -98,7 +99,10 @@ class ScenarioTestCase(test.TestCase):
|
|||||||
@mock.patch("rally.benchmark.utils.osclients")
|
@mock.patch("rally.benchmark.utils.osclients")
|
||||||
@mock.patch("multiprocessing.pool.IMapIterator.next")
|
@mock.patch("multiprocessing.pool.IMapIterator.next")
|
||||||
@mock.patch("rally.benchmark.runner.time.time")
|
@mock.patch("rally.benchmark.runner.time.time")
|
||||||
def test_run_scenario_timeout(self, mock_time, mock_next, mock_osclients):
|
@mock.patch("rally.benchmark.utils._prepare_for_instance_ssh")
|
||||||
|
def test_run_scenario_timeout(self, mock_prepare_for_instance_ssh,
|
||||||
|
mock_time, mock_next, mock_osclients):
|
||||||
|
|
||||||
mock_time.side_effect = [1, 2, 3, 10]
|
mock_time.side_effect = [1, 2, 3, 10]
|
||||||
mock_next.side_effect = multiprocessing.TimeoutError()
|
mock_next.side_effect = multiprocessing.TimeoutError()
|
||||||
mock_osclients.Clients.return_value = fakes.FakeClients()
|
mock_osclients.Clients.return_value = fakes.FakeClients()
|
||||||
|
@@ -79,7 +79,17 @@ class FakeKeypair(FakeResource):
|
|||||||
|
|
||||||
|
|
||||||
class FakeSecurityGroup(FakeResource):
|
class FakeSecurityGroup(FakeResource):
|
||||||
pass
|
|
||||||
|
def __init__(self, manager=None):
|
||||||
|
super(FakeSecurityGroup, self).__init__(manager)
|
||||||
|
self.rules = []
|
||||||
|
|
||||||
|
|
||||||
|
class FakeSecurityGroupRule(FakeResource):
|
||||||
|
def __init__(self, name, **kwargs):
|
||||||
|
super(FakeSecurityGroupRule, self).__init__(name)
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
|
||||||
class FakeVolume(FakeResource):
|
class FakeVolume(FakeResource):
|
||||||
@@ -126,6 +136,16 @@ class FakeManager(object):
|
|||||||
resources.append(self.cache[uuid])
|
resources.append(self.cache[uuid])
|
||||||
return resources
|
return resources
|
||||||
|
|
||||||
|
def find(self, **kwargs):
|
||||||
|
for resource in self.cache.values():
|
||||||
|
match = True
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
if getattr(resource, key, None) != value:
|
||||||
|
match = False
|
||||||
|
break
|
||||||
|
if match:
|
||||||
|
return resource
|
||||||
|
|
||||||
|
|
||||||
class FakeServerManager(FakeManager):
|
class FakeServerManager(FakeManager):
|
||||||
|
|
||||||
@@ -145,7 +165,7 @@ class FakeServerManager(FakeManager):
|
|||||||
server.name = name
|
server.name = name
|
||||||
return server
|
return server
|
||||||
|
|
||||||
def create(self, name, image_id, flavor_id):
|
def create(self, name, image_id, flavor_id, **kwargs):
|
||||||
return self._create(name=name)
|
return self._create(name=name)
|
||||||
|
|
||||||
def create_image(self, server, name):
|
def create_image(self, server, name):
|
||||||
@@ -161,7 +181,7 @@ class FakeServerManager(FakeManager):
|
|||||||
|
|
||||||
class FakeFailedServerManager(FakeServerManager):
|
class FakeFailedServerManager(FakeServerManager):
|
||||||
|
|
||||||
def create(self, name, image_id, flavor_id):
|
def create(self, name, image_id, flavor_id, **kwargs):
|
||||||
return self._create(FakeFailedServer, name)
|
return self._create(FakeFailedServer, name)
|
||||||
|
|
||||||
|
|
||||||
@@ -198,7 +218,7 @@ class FakeNetworkManager(FakeManager):
|
|||||||
|
|
||||||
class FakeKeypairManager(FakeManager):
|
class FakeKeypairManager(FakeManager):
|
||||||
|
|
||||||
def create(self, name):
|
def create(self, name, public_key=None):
|
||||||
kp = FakeKeypair(self)
|
kp = FakeKeypair(self)
|
||||||
kp.name = name or kp.name
|
kp.name = name or kp.name
|
||||||
return self._cache(kp)
|
return self._cache(kp)
|
||||||
@@ -206,12 +226,28 @@ class FakeKeypairManager(FakeManager):
|
|||||||
|
|
||||||
class FakeSecurityGroupManager(FakeManager):
|
class FakeSecurityGroupManager(FakeManager):
|
||||||
|
|
||||||
def create(self, name):
|
def __init__(self):
|
||||||
|
super(FakeSecurityGroupManager, self).__init__()
|
||||||
|
self.create('default')
|
||||||
|
|
||||||
|
def create(self, name, description=""):
|
||||||
sg = FakeSecurityGroup(self)
|
sg = FakeSecurityGroup(self)
|
||||||
sg.name = name or sg.name
|
sg.name = name or sg.name
|
||||||
|
sg.description = description
|
||||||
return self._cache(sg)
|
return self._cache(sg)
|
||||||
|
|
||||||
|
|
||||||
|
class FakeSecurityGroupRuleManager(FakeManager):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(FakeSecurityGroupRuleManager, self).__init__()
|
||||||
|
|
||||||
|
def create(self, name, **kwargs):
|
||||||
|
sgr = FakeSecurityGroupRule(self, **kwargs)
|
||||||
|
sgr.name = name or sgr.name
|
||||||
|
return self._cache(sgr)
|
||||||
|
|
||||||
|
|
||||||
class FakeUsersManager(FakeManager):
|
class FakeUsersManager(FakeManager):
|
||||||
|
|
||||||
def create(self, username, password, email, tenant_id):
|
def create(self, username, password, email, tenant_id):
|
||||||
@@ -292,6 +328,7 @@ class FakeNovaClient(object):
|
|||||||
self.networks = FakeNetworkManager()
|
self.networks = FakeNetworkManager()
|
||||||
self.keypairs = FakeKeypairManager()
|
self.keypairs = FakeKeypairManager()
|
||||||
self.security_groups = FakeSecurityGroupManager()
|
self.security_groups = FakeSecurityGroupManager()
|
||||||
|
self.security_group_rules = FakeSecurityGroupRuleManager()
|
||||||
|
|
||||||
|
|
||||||
class FakeKeystoneClient(object):
|
class FakeKeystoneClient(object):
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
import jsonschema
|
import jsonschema
|
||||||
import mock
|
import mock
|
||||||
import netaddr
|
import netaddr
|
||||||
|
import os
|
||||||
|
|
||||||
from rally.openstack.common.fixture import mockpatch
|
from rally.openstack.common.fixture import mockpatch
|
||||||
from rally.openstack.common import test
|
from rally.openstack.common import test
|
||||||
@@ -43,15 +44,17 @@ class VirshProviderTestCase(test.BaseTestCase):
|
|||||||
mock_subp.check_output.return_value = '10.0.0.1'
|
mock_subp.check_output.return_value = '10.0.0.1'
|
||||||
mock_ipaddress.return_value = '10.0.0.2'
|
mock_ipaddress.return_value = '10.0.0.2'
|
||||||
server = self.provider.create_vm('name')
|
server = self.provider.create_vm('name')
|
||||||
|
script_path = '%(virsh_path)s/virsh/get_domain_ip.sh' % dict(
|
||||||
|
virsh_path=os.path.split(virsh.__file__)[0])
|
||||||
mock_subp.assert_has_calls([
|
mock_subp.assert_has_calls([
|
||||||
mock.call.check_call('virt-clone --connect=qemu+ssh://user@host/'
|
mock.call.check_call('virt-clone --connect=qemu+ssh://user@host/'
|
||||||
'system -o prefix -n name --auto-clone',
|
'system -o prefix -n name --auto-clone',
|
||||||
shell=True),
|
shell=True),
|
||||||
mock.call.check_call('virsh --connect=qemu+ssh://user@host/system '
|
mock.call.check_call('virsh --connect=qemu+ssh://user@host/system '
|
||||||
'start name', shell=True),
|
'start name', shell=True),
|
||||||
mock.call.check_call('scp -o StrictHostKeyChecking=no rally/serve'
|
mock.call.check_call('scp -o StrictHostKeyChecking=no %s u'
|
||||||
'rprovider/providers/virsh/get_domain_ip.sh u'
|
'ser@host:~/get_domain_ip.sh' % script_path,
|
||||||
'ser@host:~/get_domain_ip.sh', shell=True),
|
shell=True),
|
||||||
mock.call.check_output('ssh -o StrictHostKeyChecking=no user@host '
|
mock.call.check_output('ssh -o StrictHostKeyChecking=no user@host '
|
||||||
'./get_domain_ip.sh name', shell=True),
|
'./get_domain_ip.sh name', shell=True),
|
||||||
])
|
])
|
||||||
|
@@ -77,7 +77,7 @@ class SSHTestCase(test.TestCase):
|
|||||||
self.ssh.upload('/tmp/s', '/tmp/d')
|
self.ssh.upload('/tmp/s', '/tmp/d')
|
||||||
|
|
||||||
expected = [mock.call.set_missing_host_key_policy(self.policy),
|
expected = [mock.call.set_missing_host_key_policy(self.policy),
|
||||||
mock.call.connect('example.net', username='root',
|
mock.call.connect(hostname='example.net', username='root',
|
||||||
key_filename=os.path.expanduser(
|
key_filename=os.path.expanduser(
|
||||||
'~/.ssh/id_rsa'), port=22),
|
'~/.ssh/id_rsa'), port=22),
|
||||||
mock.call.open_sftp(),
|
mock.call.open_sftp(),
|
||||||
@@ -94,8 +94,11 @@ class SSHTestCase(test.TestCase):
|
|||||||
self.ssh.execute_script('/bin/script')
|
self.ssh.execute_script('/bin/script')
|
||||||
|
|
||||||
up.assert_called_once_with('/bin/script', '/tmp/aaaaaaaaaaaaaaaa')
|
up.assert_called_once_with('/bin/script', '/tmp/aaaaaaaaaaaaaaaa')
|
||||||
ex.assert_has_calls([mock.call('/bin/sh /tmp/aaaaaaaaaaaaaaaa'),
|
ex.assert_has_calls([
|
||||||
mock.call('rm /tmp/aaaaaaaaaaaaaaaa')])
|
mock.call('/bin/sh /tmp/aaaaaaaaaaaaaaaa',
|
||||||
|
get_stderr=False, get_stdout=False),
|
||||||
|
mock.call('rm /tmp/aaaaaaaaaaaaaaaa')
|
||||||
|
])
|
||||||
|
|
||||||
@mock.patch('rally.sshutils.SSH.execute')
|
@mock.patch('rally.sshutils.SSH.execute')
|
||||||
def test_wait(self, ex):
|
def test_wait(self, ex):
|
||||||
|
Reference in New Issue
Block a user