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
|
||||
"""
|
||||
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
|
||||
# check whether the server is ready => less API calls.
|
||||
time.sleep(5)
|
||||
|
@@ -112,6 +112,65 @@ def create_openstack_clients(users_endpoints, keys):
|
||||
)) 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
|
||||
|
||||
|
||||
|
@@ -20,6 +20,7 @@ import random
|
||||
import select
|
||||
import socket
|
||||
import string
|
||||
from StringIO import StringIO
|
||||
import time
|
||||
|
||||
from rally import exceptions
|
||||
@@ -31,27 +32,46 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
class SSH(object):
|
||||
"""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.
|
||||
|
||||
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.port = port
|
||||
self.user = user
|
||||
self.timeout = timeout
|
||||
self.client = None
|
||||
if key:
|
||||
self.key = key
|
||||
else:
|
||||
self.key = key
|
||||
self.key_type = key_type
|
||||
if not self.key:
|
||||
#Guess location of user's private key if no key is specified.
|
||||
self.key = os.path.expanduser('~/.ssh/id_rsa')
|
||||
|
||||
def _get_ssh_connection(self):
|
||||
self.client = paramiko.SSHClient()
|
||||
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
self.client.connect(self.ip, username=self.user,
|
||||
key_filename=self.key, port=self.port)
|
||||
connect_params = {
|
||||
'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):
|
||||
return (time.time() - self.timeout) > start_time
|
||||
@@ -136,14 +156,17 @@ class SSH(object):
|
||||
ftp.put(os.path.expanduser(source), destination)
|
||||
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."""
|
||||
destination = '/tmp/' + ''.join(
|
||||
random.choice(string.lowercase) for i in range(16))
|
||||
|
||||
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)
|
||||
return streams
|
||||
|
||||
def wait(self, timeout=120, interval=1):
|
||||
"""Wait for the host will be available via ssh."""
|
||||
|
@@ -58,7 +58,7 @@ class NovaScenarioTestCase(test.TestCase):
|
||||
fc.get_nova_client = lambda: fake_nova
|
||||
fsm = fakes.FakeServerManager(fake_nova.images)
|
||||
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_image_id = fsm.create_image(fake_server, 'img')
|
||||
fake_image = fsm.images.get(fake_image_id)
|
||||
|
@@ -62,9 +62,10 @@ class ScenarioTestCase(test.TestCase):
|
||||
endpoints = srunner._create_temp_tenants_and_users(
|
||||
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:
|
||||
self.assertEqual(set(endpoint.keys()), endpoint_keys)
|
||||
self.assertTrue(endpoint_keys.issubset(endpoint.keys()))
|
||||
|
||||
def test_run_scenario(self):
|
||||
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("multiprocessing.pool.IMapIterator.next")
|
||||
@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_next.side_effect = multiprocessing.TimeoutError()
|
||||
mock_osclients.Clients.return_value = fakes.FakeClients()
|
||||
|
@@ -79,7 +79,17 @@ class FakeKeypair(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):
|
||||
@@ -126,6 +136,16 @@ class FakeManager(object):
|
||||
resources.append(self.cache[uuid])
|
||||
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):
|
||||
|
||||
@@ -145,7 +165,7 @@ class FakeServerManager(FakeManager):
|
||||
server.name = name
|
||||
return server
|
||||
|
||||
def create(self, name, image_id, flavor_id):
|
||||
def create(self, name, image_id, flavor_id, **kwargs):
|
||||
return self._create(name=name)
|
||||
|
||||
def create_image(self, server, name):
|
||||
@@ -161,7 +181,7 @@ class FakeServerManager(FakeManager):
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -198,7 +218,7 @@ class FakeNetworkManager(FakeManager):
|
||||
|
||||
class FakeKeypairManager(FakeManager):
|
||||
|
||||
def create(self, name):
|
||||
def create(self, name, public_key=None):
|
||||
kp = FakeKeypair(self)
|
||||
kp.name = name or kp.name
|
||||
return self._cache(kp)
|
||||
@@ -206,12 +226,28 @@ class FakeKeypairManager(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.name = name or sg.name
|
||||
sg.description = description
|
||||
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):
|
||||
|
||||
def create(self, username, password, email, tenant_id):
|
||||
@@ -292,6 +328,7 @@ class FakeNovaClient(object):
|
||||
self.networks = FakeNetworkManager()
|
||||
self.keypairs = FakeKeypairManager()
|
||||
self.security_groups = FakeSecurityGroupManager()
|
||||
self.security_group_rules = FakeSecurityGroupRuleManager()
|
||||
|
||||
|
||||
class FakeKeystoneClient(object):
|
||||
|
@@ -16,6 +16,7 @@
|
||||
import jsonschema
|
||||
import mock
|
||||
import netaddr
|
||||
import os
|
||||
|
||||
from rally.openstack.common.fixture import mockpatch
|
||||
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_ipaddress.return_value = '10.0.0.2'
|
||||
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.call.check_call('virt-clone --connect=qemu+ssh://user@host/'
|
||||
'system -o prefix -n name --auto-clone',
|
||||
shell=True),
|
||||
mock.call.check_call('virsh --connect=qemu+ssh://user@host/system '
|
||||
'start name', shell=True),
|
||||
mock.call.check_call('scp -o StrictHostKeyChecking=no rally/serve'
|
||||
'rprovider/providers/virsh/get_domain_ip.sh u'
|
||||
'ser@host:~/get_domain_ip.sh', shell=True),
|
||||
mock.call.check_call('scp -o StrictHostKeyChecking=no %s u'
|
||||
'ser@host:~/get_domain_ip.sh' % script_path,
|
||||
shell=True),
|
||||
mock.call.check_output('ssh -o StrictHostKeyChecking=no user@host '
|
||||
'./get_domain_ip.sh name', shell=True),
|
||||
])
|
||||
|
@@ -77,7 +77,7 @@ class SSHTestCase(test.TestCase):
|
||||
self.ssh.upload('/tmp/s', '/tmp/d')
|
||||
|
||||
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(
|
||||
'~/.ssh/id_rsa'), port=22),
|
||||
mock.call.open_sftp(),
|
||||
@@ -94,8 +94,11 @@ class SSHTestCase(test.TestCase):
|
||||
self.ssh.execute_script('/bin/script')
|
||||
|
||||
up.assert_called_once_with('/bin/script', '/tmp/aaaaaaaaaaaaaaaa')
|
||||
ex.assert_has_calls([mock.call('/bin/sh /tmp/aaaaaaaaaaaaaaaa'),
|
||||
mock.call('rm /tmp/aaaaaaaaaaaaaaaa')])
|
||||
ex.assert_has_calls([
|
||||
mock.call('/bin/sh /tmp/aaaaaaaaaaaaaaaa',
|
||||
get_stderr=False, get_stdout=False),
|
||||
mock.call('rm /tmp/aaaaaaaaaaaaaaaa')
|
||||
])
|
||||
|
||||
@mock.patch('rally.sshutils.SSH.execute')
|
||||
def test_wait(self, ex):
|
||||
|
Reference in New Issue
Block a user