class SameHostNetworkTest(NetworkTest): #: Resources stack with Nova server to send messages to stack = tobiko.required_setup_fixture( stacks.CirrosSameHostServerStackFixture) def test_same_host(self): sender = self.stack.peer_stack.server_details receiver = self.stack.server_details self.assertEqual({'same_host': [sender.id]}, self.stack.scheduler_hints) self.assertEqual(getattr(sender, 'OS-EXT-SRV-ATTR:host'), getattr(receiver, 'OS-EXT-SRV-ATTR:host'))
class NetworkTestCase(testtools.TestCase): """Tests network creation""" #: Stack of resources with a network with a gateway router stack = tobiko.required_setup_fixture(stacks.NetworkStackFixture) @neutron.skip_if_missing_networking_extensions('port-security') def test_port_security_enabled(self): self.assertEqual(self.stack.port_security_enabled, self.stack.network_details['port_security_enabled']) self.assertEqual(self.stack.port_security_enabled, self.stack.outputs.port_security_enabled) @neutron.skip_if_missing_networking_extensions('net-mtu') def test_net_mtu(self): self.assertEqual(self.stack.network_details['mtu'], self.stack.outputs.mtu) def test_ipv4_subnet_cidr(self): if not self.stack.has_ipv4: tobiko.skip('Stack {!s} has no ipv4 subnet', self.stack.stack_name) subnet = neutron.find_subnet(cidr=str(self.stack.ipv4_subnet_cidr)) self.assertEqual(neutron.get_subnet(self.stack.ipv4_subnet_id), subnet) def test_ipv6_subnet_cidr(self): if not self.stack.has_ipv6: tobiko.skip('Stack {!s} has no ipv6 subnet', self.stack.stack_name) subnet = neutron.find_subnet(cidr=str(self.stack.ipv6_subnet_cidr)) self.assertEqual(neutron.get_subnet(self.stack.ipv6_subnet_id), subnet) def test_gateway_network(self): if not self.stack.has_gateway: tobiko.skip('Stack {!s} has no gateway', self.stack.stack_name) self.assertEqual( self.stack.gateway_network_id, self.stack.gateway_details['external_gateway_info']['network_id']) def test_ipv4_subnet_gateway_ip(self): if not self.stack.has_ipv4 or not self.stack.has_gateway: tobiko.skip('Stack {!s} has no IPv4 gateway', self.stack.stack_name) self.assertIn(self.stack.ipv4_subnet_gateway_ip, self.stack.ipv4_gateway_addresses) def test_ipv6_subnet_gateway_ip(self): if not self.stack.has_ipv6 or not self.stack.has_gateway: tobiko.skip('Stack {!s} has no IPv6 gateway', self.stack.stack_name) self.assertIn(self.stack.ipv6_subnet_gateway_ip, self.stack.ipv6_gateway_addresses)
class SSHClientManager(object): default = tobiko.required_setup_fixture(_config.SSHDefaultConfigFixture) def __init__(self): self.clients = {} def get_client(self, host, username=None, port=None, proxy_jump=None, host_config=None, config_files=None, proxy_client=None, **connect_parameters): if isinstance(host, netaddr.IPAddress): host = str(host) host_config = host_config or _config.ssh_host_config( host=host, config_files=config_files) hostname = host_config.hostname port = port or host_config.port username = username or host_config.username host_key = hostname, port, username, proxy_jump client = self.clients.get(host_key, UNDEFINED_CLIENT) if client is UNDEFINED_CLIENT: # Put a placeholder client to avoid infinite recursive lookup self.clients[host_key] = None proxy_client = proxy_client or self.get_proxy_client( host=host, proxy_jump=proxy_jump, config_files=config_files) self.clients[host_key] = client = SSHClientFixture( host=host, hostname=hostname, port=port, username=username, proxy_client=proxy_client, host_config=host_config, **connect_parameters) return client def get_proxy_client(self, host=None, proxy_jump=None, host_config=None, config_files=None): if isinstance(proxy_jump, SSHClientFixture): return proxy_jump host_config = host_config or _config.ssh_host_config( host=host, config_files=config_files) proxy_host = host_config.proxy_jump return proxy_host and self.get_client(proxy_host) or None
class PortTest(testtools.TestCase): """Test Neutron ports""" #: Resources stack with Nova server to send messages to stack = tobiko.required_setup_fixture(stacks.CirrosServerStackFixture) def test_port_ips(self): server_ips = ip.list_ip_addresses(scope='global', ssh_client=self.stack.ssh_client) port_ips = neutron.list_port_ip_addresses(port=self.stack.port_details) self.assertFalse(set(port_ips) - set(server_ips)) def test_port_network(self): self.assertEqual(self.stack.network_stack.network_id, self.stack.port_details['network_id']) def test_port_subnets(self): port_subnets = [ fixed_ip['subnet_id'] for fixed_ip in self.stack.port_details['fixed_ips'] ] network_subnets = self.stack.network_stack.network_details['subnets'] self.assertEqual(set(network_subnets), set(port_subnets)) def test_ping_subnet_gateways(self): network_id = self.stack.network_stack.network_id subnets = neutron.list_subnets(network_id=network_id) gateway_ips = [ netaddr.IPAddress(subnet['gateway_ip']) for subnet in subnets ] ping.assert_reachable_hosts(gateway_ips, ssh_client=self.stack.ssh_client) def test_ping_port(self, network_id=None, device_id=None): network_id = network_id or self.stack.network_stack.network_id device_id = device_id or self.stack.server_id ports = neutron.list_ports(network_id=network_id, device_id=device_id) port_ips = set() for port in ports: self.assertEqual(network_id, port['network_id']) self.assertEqual(device_id, port['device_id']) port_ips.update(neutron.list_port_ip_addresses(port=port)) ping.assert_reachable_hosts(port_ips, ssh_client=self.stack.ssh_client) def test_ping_inner_gateway_ip(self): if not self.stack.network_stack.has_gateway: self.skip('Server network has no gateway router') self.test_ping_port(device_id=self.stack.network_stack.gateway_id)
class L3HARouterTest(RouterTest): """Test Neutron HA routers""" #: Resources stack with Nova server to send messages to stack = tobiko.required_setup_fixture(stacks.L3haServerStackFixture) @neutron.skip_if_missing_networking_extensions('l3_agent_scheduler') def test_router_is_scheduled_on_l3_agents(self): router_agents = neutron.list_l3_agent_hosting_routers( self.router['id']) master_agent = router_agents.with_items(ha_state='active').unique backup_agents = router_agents.with_items(ha_state='standby') self.assertGreaterEqual(len(backup_agents), 1) self._check_routers_namespace_on_host(master_agent['host']) for backup_agent in backup_agents: self._check_routers_namespace_on_host(backup_agent['host'], state="backup")
class GlanceApiTestCase(testtools.TestCase): """Tests glance images API""" #: Stack of resources with a network with a gateway router fixture = tobiko.required_setup_fixture(stacks.CirrosImageFixture) def test_get_image(self): image = glance.get_image(self.fixture.image_id) self.assertEqual(self.fixture.image_id, image['id']) def test_find_image_by_id(self): image = glance.find_image(id=self.fixture.image_id) self.assertEqual(self.fixture.image_id, image['id']) def test_find_image_by_name(self): image = glance.find_image(name=self.fixture.image_name) self.assertEqual(self.fixture.image_name, image['name'])
class PortTest(testtools.TestCase): #: Stack of resources with a network with a gateway router stack = tobiko.required_setup_fixture(stacks.CentosServerStackFixture) def test_list_port_addresses(self, ip_version=None): port = neutron.find_port(device_id=self.stack.server_id) port_addresses = neutron.list_port_ip_addresses( port=port, ip_version=ip_version) server_addresses = nova.list_server_ip_addresses( server=self.stack.server_details, ip_version=ip_version, address_type='fixed') self.assertEqual(set(server_addresses), set(port_addresses)) if ip_version: self.assertEqual( port_addresses.with_attributes(version=ip_version), port_addresses) def test_list_port_addresses_with_ipv4(self): self.test_list_port_addresses(ip_version=4) def test_list_port_addresses_with_ipv6(self): self.test_list_port_addresses(ip_version=6) def test_find_port_address_with_ip_version(self): port = neutron.find_port(device_id=self.stack.server_id) server_addresses = nova.list_server_ip_addresses( server=self.stack.server_details, address_type='fixed') for server_address in server_addresses: port_address = neutron.find_port_ip_address( port=port, ip_version=server_address.version, unique=True) self.assertEqual(server_address, port_address) def test_find_port_address_with_subnet_id(self): port = neutron.find_port(device_id=self.stack.server_id) for subnet in neutron.list_subnets(network_id=port['network_id']): port_address = neutron.find_port_ip_address( port=port, subnet_id=subnet['id'], unique=True) cidr = netaddr.IPNetwork(subnet['cidr']) self.assertIn(port_address, cidr)
class SSHConfigFixture(tobiko.SharedFixture): default = tobiko.required_setup_fixture(SSHDefaultConfigFixture) config_files = None config = None def __init__(self, config_files=None): super(SSHConfigFixture, self).__init__() if config_files: self.config_files = tuple(config_files) else: self.config_files = self.default.config_files def setup_fixture(self): self.setup_ssh_config() def setup_ssh_config(self): self.config = paramiko.SSHConfig() for config_file in self.config_files: config_file = os.path.expanduser(config_file) if os.path.exists(config_file): LOG.debug("Parsing %r config file...", config_file) with open(config_file) as f: self.config.parse(f) LOG.debug("File %r parsed.", config_file) def lookup(self, host=None): host_config = host and self.config.lookup(host) or {} # remove unsupported directive include_files = host_config.pop('include', None) if include_files: LOG.warning('Ignoring unsupported directive: Include %s', include_files) return SSHHostConfig(host=host, ssh_config=self, host_config=host_config, config_files=self.config_files) def __repr__(self): return "{class_name!s}(config_files={config_files!r})".format( class_name=type(self).__name__, config_files=self.config_files)
class ServerStackResourcesTest(testtools.TestCase): stack = tobiko.required_setup_fixture(stacks.CirrosServerStackFixture) def test_server(self): "Test actual server details" server = self.stack.resources.server self.assertEqual('OS::Nova::Server', server.resource_type) # Verify actual server status (is alive, etc.) nova_client = nova.get_nova_client() server_data = nova_client.servers.get(server.physical_resource_id) self.assertEqual(self.stack.server_name, server_data.name) def test_port(self): "Test actual server port details" port = self.stack.resources.port self.assertEqual('OS::Neutron::Port', port.resource_type) # Verify actual port status (is alive, etc.) port_data = neutron.get_port(port.physical_resource_id) self.assertEqual(port.physical_resource_id, port_data['id'])
class OvercloudHostConfig(tobiko.SharedFixture): hostname = None port = None username = None key_file = tobiko.required_setup_fixture(OvercloudSshKeyFileFixture) ip_version = None network_name = None key_filename = None def __init__(self, host, ip_version=None, network_name=None, **kwargs): super(OvercloudHostConfig, self).__init__() tobiko.check_valid_type(host, six.string_types) self.host = host if ip_version: self.ip_version = ip_version if network_name: self.network_name = network_name self._connect_parameters = ssh.gather_ssh_connect_parameters(**kwargs) def setup_fixture(self): self.hostname = self.hostname or str( overcloud_node_ip_address(name=self.host, ip_version=self.ip_version, network_name=self.network_name)) self.port = self.port or CONF.tobiko.tripleo.overcloud_ssh_port self.username = (self.username or CONF.tobiko.tripleo.overcloud_ssh_username) self.key_filename = self.key_filename or self.key_file.key_filename @property def connect_parameters(self): parameters = ssh.ssh_host_config( host=str(self.hostname)).connect_parameters parameters.update(ssh.gather_ssh_connect_parameters(self)) parameters.update(self._connect_parameters) return parameters
class CloudsFileKeystoneCredentialsFixtureTest(openstack.OpenstackTest): config = tobiko.required_setup_fixture( _clouds_file.DefaultCloudsFileConfig) def test_init(self): fixture = keystone.CloudsFileKeystoneCredentialsFixture() self.assertEqual(self.config.cloud_name, fixture.cloud_name) self.assertIsNone(fixture.clouds_content) self.assertIsNone(fixture.clouds_file) self.assertEqual(self.config.clouds_files, fixture.clouds_files) def test_init_with_cloud_name(self): fixture = keystone.CloudsFileKeystoneCredentialsFixture( cloud_name='cloud-name') self.assertEqual('cloud-name', fixture.cloud_name) def test_init_with_clouds_content(self): fixture = keystone.CloudsFileKeystoneCredentialsFixture( clouds_content={}) self.assertEqual({}, fixture.clouds_content) def test_init_with_clouds_file(self): fixture = keystone.CloudsFileKeystoneCredentialsFixture( clouds_file='cloud-file') self.assertEqual('cloud-file', fixture.clouds_file) def test_init_with_clouds_files(self): fixture = keystone.CloudsFileKeystoneCredentialsFixture( clouds_files=['a', 'b', 'd']) self.assertEqual(['a', 'b', 'd'], fixture.clouds_files) def test_setup_from_default_clouds_files(self): file_fixture = self.useFixture(V3CloudsFileFixture()) self.patch(self.config, 'clouds_files', ['/a', file_fixture.clouds_file, '/c']) credentials_fixture = self.useFixture( keystone.CloudsFileKeystoneCredentialsFixture( cloud_name=file_fixture.cloud_name)) self.assertEqual(file_fixture.clouds_content, credentials_fixture.clouds_content) self.assertEqual(test_credentials.V3_PARAMS, credentials_fixture.credentials.to_dict()) def test_setup_from_json(self): file_fixture = self.useFixture(V3CloudsFileFixture(suffix='.json')) credentials_fixture = self.useFixture( keystone.CloudsFileKeystoneCredentialsFixture( cloud_name=file_fixture.cloud_name, clouds_file=file_fixture.clouds_file)) self.assertEqual(file_fixture.clouds_content, credentials_fixture.clouds_content) self.assertEqual(test_credentials.V3_PARAMS, credentials_fixture.credentials.to_dict()) def test_setup_from_yaml(self): file_fixture = self.useFixture(V3CloudsFileFixture(suffix='.yaml')) credentials_fixture = self.useFixture( keystone.CloudsFileKeystoneCredentialsFixture( cloud_name=file_fixture.cloud_name, clouds_file=file_fixture.clouds_file)) self.assertEqual(file_fixture.clouds_content, credentials_fixture.clouds_content) self.assertEqual(test_credentials.V3_PARAMS, credentials_fixture.credentials.to_dict()) def test_setup_from_yml(self): file_fixture = self.useFixture(V3CloudsFileFixture(suffix='.yml')) credentials_fixture = self.useFixture( keystone.CloudsFileKeystoneCredentialsFixture( cloud_name=file_fixture.cloud_name, clouds_file=file_fixture.clouds_file)) self.assertEqual(file_fixture.clouds_content, credentials_fixture.clouds_content) self.assertEqual(test_credentials.V3_PARAMS, credentials_fixture.credentials.to_dict()) def test_setup_v2_credentials(self): file_fixture = self.useFixture(V2CloudsFileFixture()) credentials_fixture = self.useFixture( keystone.CloudsFileKeystoneCredentialsFixture( cloud_name=file_fixture.cloud_name, clouds_file=file_fixture.clouds_file)) self.assertEqual(file_fixture.clouds_content, credentials_fixture.clouds_content) self.assertEqual(test_credentials.V2_PARAMS, credentials_fixture.credentials.to_dict()) def test_setup_with_cloud_name(self): file_fixture = self.useFixture(V3CloudsFileFixture()) credentials_fixture = keystone.CloudsFileKeystoneCredentialsFixture( cloud_name='cloud-name', clouds_file=file_fixture.clouds_file) ex = self.assertRaises(ValueError, tobiko.setup_fixture, credentials_fixture) self.assertEqual( "No such cloud with name 'cloud-name' in file " + repr(file_fixture.clouds_file), str(ex)) def test_setup_with_cloud_name_from_env(self): self.patch(self.config, 'cloud_name', None) file_fixture = self.useFixture(V2CloudsFileFixture()) self.patch(os, 'environ', {'OS_CLOUD': file_fixture.cloud_name}) credentials_fixture = keystone.CloudsFileKeystoneCredentialsFixture( clouds_file=file_fixture.clouds_file) self.assertIsNone(credentials_fixture.cloud_name) tobiko.setup_fixture(credentials_fixture) self.assertEqual(file_fixture.cloud_name, credentials_fixture.cloud_name) def test_setup_with_empty_cloud_name(self): file_fixture = self.useFixture(V2CloudsFileFixture()) credentials_fixture = keystone.CloudsFileKeystoneCredentialsFixture( clouds_file=file_fixture.clouds_file, cloud_name='') self.assertIsNone(credentials_fixture.credentials) self.assertEqual('', credentials_fixture.cloud_name) tobiko.setup_fixture(credentials_fixture) self.assertIsNone(credentials_fixture.credentials) self.assertEqual('', credentials_fixture.cloud_name) def test_setup_with_empty_cloud_name_from_env(self): self.patch(self.config, 'cloud_name', None) file_fixture = self.useFixture(V2CloudsFileFixture()) self.patch(os, 'environ', {'OS_CLOUD': ''}) credentials_fixture = keystone.CloudsFileKeystoneCredentialsFixture( clouds_file=file_fixture.clouds_file) self.assertIsNone(credentials_fixture.credentials) self.assertIsNone(credentials_fixture.cloud_name) tobiko.setup_fixture(credentials_fixture) self.assertIsNone(credentials_fixture.credentials) self.assertIsNone(credentials_fixture.cloud_name) def test_setup_with_no_cloud_name(self): self.patch(self.config, 'cloud_name', None) file_fixture = self.useFixture(V2CloudsFileFixture()) credentials_fixture = keystone.CloudsFileKeystoneCredentialsFixture( clouds_file=file_fixture.clouds_file) self.assertIsNone(credentials_fixture.credentials) self.assertIsNone(credentials_fixture.cloud_name) tobiko.setup_fixture(credentials_fixture) self.assertIsNone(credentials_fixture.credentials) self.assertIsNone(credentials_fixture.cloud_name) def test_setup_with_no_clouds_section(self): fixture = keystone.CloudsFileKeystoneCredentialsFixture( cloud_name='cloud-name', clouds_content={'other_data': None}, clouds_file='clouds-file') ex = self.assertRaises(ValueError, tobiko.setup_fixture, fixture) self.assertEqual('cloud-name', fixture.cloud_name) self.assertEqual({'other_data': None}, fixture.clouds_content) self.assertEqual( "'clouds' section not found in clouds file " "'clouds-file'", str(ex)) def test_setup_with_empty_clouds_content(self): fixture = keystone.CloudsFileKeystoneCredentialsFixture( cloud_name='cloud-name', clouds_content={}) ex = self.assertRaises(ValueError, tobiko.setup_fixture, fixture) self.assertEqual('cloud-name', fixture.cloud_name) self.assertEqual({}, fixture.clouds_content) self.assertEqual('Invalid clouds file content: {}', str(ex)) def test_setup_with_no_auth(self): clouds_content = make_clouds_content('cloud-name') fixture = keystone.CloudsFileKeystoneCredentialsFixture( cloud_name='cloud-name', clouds_content=clouds_content, clouds_file='cloud-file') ex = self.assertRaises(ValueError, tobiko.setup_fixture, fixture) self.assertEqual('cloud-name', fixture.cloud_name) self.assertEqual( "No such 'auth' section in cloud file 'cloud-file' for cloud " "name 'cloud-name'", str(ex)) def test_setup_with_no_auth_url(self): clouds_content = make_clouds_content('cloud-name', auth={}) fixture = keystone.CloudsFileKeystoneCredentialsFixture( cloud_name='cloud-name', clouds_content=clouds_content, clouds_file='cloud-file') ex = self.assertRaises(ValueError, tobiko.setup_fixture, fixture) self.assertEqual('cloud-name', fixture.cloud_name) self.assertEqual( "No such 'auth_url' in file 'cloud-file' for cloud name " "'cloud-name'", str(ex)) def test_setup_without_clouds_file(self): self.patch(self.config, 'clouds_files', ['/a', '/b', '/c']) fixture = keystone.CloudsFileKeystoneCredentialsFixture( cloud_name='cloud-name') ex = self.assertRaises(_clouds_file.CloudsFileNotFoundError, tobiko.setup_fixture, fixture) self.assertEqual('cloud-name', fixture.cloud_name) self.assertEqual("No such clouds file(s): /a, /b, /c", str(ex)) def test_setup_with_non_existing_clouds_file(self): fixture = keystone.CloudsFileKeystoneCredentialsFixture( clouds_file='/a.yaml', cloud_name='cloud-name') ex = self.assertRaises(_clouds_file.CloudsFileNotFoundError, tobiko.setup_fixture, fixture) self.assertEqual("No such clouds file(s): /a.yaml", str(ex))
class IpTest(testtools.TestCase): centos_stack = tobiko.required_setup_fixture( stacks.CentosServerStackFixture) cirros_stack = tobiko.required_setup_fixture( stacks.CirrosServerStackFixture) ubuntu_stack = tobiko.required_setup_fixture( stacks.UbuntuServerStackFixture) namespace = tobiko.required_setup_fixture(fixtures.NetworkNamespaceFixture) def test_list_ip_addresses(self, ip_version=None, scope=None, **execute_params): ips = ip.list_ip_addresses(ip_version=ip_version, scope=scope, **execute_params) self.assertIsInstance(ips, tobiko.Selection) for ip_address in ips: self.assertIsInstance(ip_address, netaddr.IPAddress) if ip_version: self.assertEqual(ips.with_attributes(version=ip_version), ips) if scope: if scope == 'link': self.assertEqual(ips.with_attributes(version=4), []) self.assertEqual(ips.with_attributes(version=6), ips) elif scope == 'host': for a in ips: self.assertTrue(a.is_loopback()) elif scope == 'global': self.assertNotIn(netaddr.IPAddress('127.0.0.1'), ips) self.assertNotIn(netaddr.IPAddress('::1'), ips) return ips def test_list_ip_addresses_with_host_scope(self, **execute_params): self.test_list_ip_addresses(scope='host', **execute_params) def test_list_ip_addresses_with_link_scope(self, **execute_params): self.test_list_ip_addresses(scope='link', **execute_params) def test_list_ip_addresses_with_global_scope(self, **execute_params): self.test_list_ip_addresses(scope='global', **execute_params) def test_list_ip_addresses_with_ipv4(self): self.test_list_ip_addresses(ip_version=4) def test_list_ip_addresses_with_ipv6(self): self.test_list_ip_addresses(ip_version=6) def test_list_ip_addresses_with_centos_server(self): self.test_list_ip_addresses(ssh_client=self.centos_stack.ssh_client) def test_list_ip_addresses_with_cirros_server(self): self.test_list_ip_addresses(ssh_client=self.cirros_stack.ssh_client) def test_list_ip_addresses_with_ubuntu_server(self): self.test_list_ip_addresses(ssh_client=self.ubuntu_stack.ssh_client) def test_list_ip_addresses_with_proxy_ssh_client(self): ssh_client = ssh.ssh_proxy_client() if ssh_client is None: self.skip('SSH proxy server not configured') self.test_list_ip_addresses(ssh_client=ssh_client) def test_list_ip_addresses_with_proxy_ssh_client_and_host_scope( self, **execute_params): self.test_list_ip_addresses(scope='host', **execute_params) def test_list_ip_addresses_with_proxy_ssh_client_and_link_scope( self, **execute_params): self.test_list_ip_addresses(scope='link', **execute_params) def test_list_ip_addresses_with_proxy_ssh_client_and_global_scope( self, **execute_params): self.test_list_ip_addresses(scope='global', **execute_params) def test_list_ip_addresses_with_namespace(self, **params): namespace_ips = ip.list_ip_addresses( ssh_client=self.namespace.ssh_client, network_namespace=self.namespace.network_namespace, **params) self.assertNotEqual([], namespace_ips) host_ips = ip.list_ip_addresses(ssh_client=self.namespace.ssh_client, **params) self.assertNotEqual(host_ips, namespace_ips) def test_list_ip_addresses_with_namespace_and_scope(self): self.test_list_ip_addresses_with_namespace(scope='global') def test_list_namespaces(self, **execute_params): namespaces = ip.list_network_namespaces(**execute_params) self.assertIsInstance(namespaces, list) for namespace in namespaces: self.assertIsInstance(namespace, six.string_types) self.test_list_ip_addresses(network_namespace=namespace) def test_list_namespaces_with_centos_server(self): self.test_list_namespaces(ssh_client=self.centos_stack.ssh_client) def test_list_namespaces_with_ubuntu_server(self): self.test_list_namespaces(ssh_client=self.ubuntu_stack.ssh_client) def test_list_namespaces_with_proxy_ssh_client(self): ssh_client = ssh.ssh_proxy_client() if ssh_client is None: self.skip('SSH proxy server not configured') self.test_list_namespaces(ssh_client=ssh_client)
class SSHHostConfig( collections.namedtuple( 'SSHHostConfig', ['host', 'ssh_config', 'host_config', 'config_files'])): default = tobiko.required_setup_fixture(SSHDefaultConfigFixture) @property def hostname(self): return self.host_config.get('hostname', self.host) @property def port(self): return (self.host_config.get('port') or self.default.port) @property def username(self): return (self.host_config.get('user') or self.default.username) @property def key_filename(self): return (self.host_config.get('identityfile') or self.default.key_file) @property def proxy_jump(self): proxy_jump = (self.host_config.get('proxyjump') or self.default.proxy_jump) if not proxy_jump: return None proxy_hostname = self.ssh_config.lookup(proxy_jump).hostname if ({proxy_jump, proxy_hostname} & {self.host, self.hostname}): # Avoid recursive proxy jump definition return None return proxy_jump @property def proxy_command(self): return (self.host_config.get('proxycommand') or self.default.proxy_command) @property def allow_agent(self): return (is_yes(self.host_config.get('forwardagent')) or self.default.allow_agent) @property def compress(self): return (is_yes(self.host_config.get('compression')) or self.default.compress) @property def timeout(self): return (self.host_config.get('connetcttimeout') or self.default.timeout) @property def connection_attempts(self): return (self.host_config.get('connectionattempts') or self.default.connection_attempts) @property def connection_interval(self): return (self.host_config.get('connetcttimeout') or self.default.connection_interval) @property def connect_parameters(self): return dict(hostname=self.hostname, port=self.port, username=self.username, key_filename=self.key_filename, compress=self.compress, timeout=self.timeout, allow_agent=self.allow_agent, connection_attempts=self.connection_attempts, connection_interval=self.connection_interval)
class RouterTest(testtools.TestCase): """Test Neutron routers""" #: Resources stack with Nova server to send messages to stack = tobiko.required_setup_fixture(stacks.CirrosServerStackFixture) def setUp(self): super(RouterTest, self).setUp() if not self.stack.network_stack.has_gateway: tobiko.skip('Stack {!s} has no gateway', self.stack.network_stack.stack_name) @property def router(self): return self.stack.network_stack.gateway_details @property def ipv4_subnet_gateway_ip(self): if not self.stack.network_stack.has_ipv4: tobiko.skip('Stack {!s} has no ipv4 subnet', self.stack.network_stack.stack_name) return self.stack.network_stack.ipv4_subnet_gateway_ip @property def ipv6_subnet_gateway_ip(self): if not self.stack.network_stack.has_ipv6: tobiko.skip('Stack {!s} has no ipv6 subnet', self.stack.network_stack.stack_name) return self.stack.network_stack.ipv6_subnet_gateway_ip @property def external_gateway_ips(self): return self.stack.network_stack.external_gateway_ips @property def router_ips(self): return tobiko.select( [self.ipv4_subnet_gateway_ip, self.ipv6_subnet_gateway_ip] + self.external_gateway_ips) def test_internal_router_ipv4_interface_is_reachable(self): ping.assert_reachable_hosts([self.ipv4_subnet_gateway_ip], ssh_client=self.stack.ssh_client) def test_internal_router_ipv6_interface_is_reachable(self): ping.assert_reachable_hosts([self.ipv6_subnet_gateway_ip], ssh_client=self.stack.ssh_client) def test_ipv4_subnet_gateway_ip(self): self.assertEqual(4, self.ipv4_subnet_gateway_ip.version) self.assertIn(self.ipv4_subnet_gateway_ip, self.stack.network_stack.ipv4_gateway_addresses) def test_ipv6_subnet_gateway_ip(self): self.assertEqual(6, self.ipv6_subnet_gateway_ip.version) self.assertIn(self.ipv6_subnet_gateway_ip, self.stack.network_stack.ipv6_gateway_addresses) @neutron.skip_if_missing_networking_extensions('l3_agent_scheduler') def test_router_is_scheduled_on_l3_agents(self): router_agent = neutron.find_l3_agent_hosting_router(self.router['id'], unique=True) self._check_routers_namespace_on_host(router_agent['host']) def _check_routers_namespace_on_host(self, hostname, state="master"): router_namespace = "qrouter-%s" % self.router['id'] agent_host = topology.get_openstack_node(hostname=hostname) namespaces = ip.list_network_namespaces( ssh_client=agent_host.ssh_client) self.assertIn(router_namespace, namespaces) namespace_ips = ip.list_ip_addresses( scope='global', network_namespace=router_namespace, ssh_client=agent_host.ssh_client) missing_ips = set(self.router_ips) - set(namespace_ips) if state == "master": self.assertFalse(missing_ips) else: self.assertTrue(missing_ips)
class NeutronApiTestCase(testtools.TestCase): """Tests network creation""" #: Stack of resources with a network with a gateway router stack = tobiko.required_setup_fixture(stacks.NetworkStackFixture) def test_find_network_with_id(self): network = neutron.find_network(id=self.stack.network_id) self.assertEqual(self.stack.network_id, network['id']) def test_find_floating_network(self): floating_network = CONF.tobiko.neutron.floating_network if not floating_network: tobiko.skip('floating_network not configured') network = neutron.find_network(name=floating_network) self.assertIn(floating_network, [network['name'], network['id']]) self.assertEqual(self.stack.gateway_network_id, network['id']) def test_list_networks(self): networks = neutron.list_networks() network_ids = {n['id'] for n in networks} self.assertIn(self.stack.network_id, network_ids) def test_list_subnets(self): subnets = neutron.list_subnets() subnets_ids = {s['id'] for s in subnets} if self.stack.has_ipv4: self.assertIn(self.stack.ipv4_subnet_id, subnets_ids) if self.stack.has_ipv6: self.assertIn(self.stack.ipv6_subnet_id, subnets_ids) def test_list_subnet_cidrs(self): subnets_cidrs = neutron.list_subnet_cidrs() if self.stack.has_ipv4: cidr = netaddr.IPNetwork(self.stack.ipv4_subnet_details['cidr']) self.assertIn(cidr, subnets_cidrs) if self.stack.has_ipv6: cidr = netaddr.IPNetwork(self.stack.ipv6_subnet_details['cidr']) self.assertIn(cidr, subnets_cidrs) def test_get_network(self): network = neutron.get_network(self.stack.network_id) self.assertEqual(self.stack.network_id, network['id']) self.assertEqual(self.stack.port_security_enabled, network['port_security_enabled']) if self.stack.has_ipv4: self.assertIn(self.stack.ipv4_subnet_id, network['subnets']) else: self.assertNotIn(self.stack.ipv4_subnet_id, network['subnets']) if self.stack.has_ipv6: self.assertIn(self.stack.ipv6_subnet_id, network['subnets']) else: self.assertNotIn(self.stack.ipv6_subnet_id, network['subnets']) def test_get_router(self): if not self.stack.has_gateway: tobiko.skip("Stack {stack} has no gateway router", stack=self.stack.stack_name) router = neutron.get_router(self.stack.gateway_id) self.assertEqual(self.stack.gateway_id, router['id']) def test_get_ipv4_subnet(self): if not self.stack.has_ipv4: tobiko.skip("Stack {stack} has no IPv4 subnet", stack=self.stack.stack_name) subnet = neutron.get_subnet(self.stack.ipv4_subnet_id) self.assertEqual(self.stack.ipv4_subnet_id, subnet['id']) self.assertEqual(self.stack.ipv4_subnet_details, subnet) def test_get_ipv6_subnet(self): if not self.stack.has_ipv6: tobiko.skip("Stack {stack} has no IPv6 subnet", stack=self.stack.stack_name) subnet = neutron.get_subnet(self.stack.ipv6_subnet_id) self.assertEqual(self.stack.ipv6_subnet_id, subnet['id']) self.assertEqual(self.stack.ipv6_subnet_details, subnet) def test_find_agents_with_binary(self): agent = neutron.list_agents().first agents = neutron.list_agents(binary=agent['binary']) self.assertIn(agent['id'], {a['id'] for a in agents})
class CirrosPeerServerStackFixture(CirrosServerStackFixture, _nova.PeerServerStackFixture): #: Peer server used to reach this one peer_stack = tobiko.required_setup_fixture(CirrosServerStackFixture)
class L3haPeerServerStackFixture(L3haServerStackFixture, _nova.PeerServerStackFixture): peer_stack = tobiko.required_setup_fixture(L3haServerStackFixture)
class OpenStackTopologyTest(testtools.TestCase): topology = tobiko.required_setup_fixture(topology.OpenStackTopology) def test_get_openstack_topology(self): topology_class = type(self.topology) topo = topology.get_openstack_topology(topology_class=topology_class) self.assertIs(topo, self.topology) self.assertIsInstance(topo, topology.OpenStackTopology) def test_ping_node(self): for node in self.topology.nodes: ping.ping(node.public_ip, count=1, timeout=5.).assert_replied() def test_ssh_client(self): for node in self.topology.nodes: self.assertIsNotNone(node.ssh_client) hostname = sh.get_hostname( ssh_client=node.ssh_client).split('.')[0] self.assertEqual(node.name, hostname) def test_controller_group(self): nodes = list(self.topology.get_group('controller')) self.assertNotEqual([], nodes) for node in nodes: self.assertIn('controller', node.groups) def test_compute_group(self): nodes = list(self.topology.get_group('compute')) self.assertNotEqual([], nodes) for node in nodes: self.assertIn('compute', node.groups) hypervisors = { hypervisor.hypervisor_hostname.split('.', 1)[0].lower(): hypervisor for hypervisor in nova.list_hypervisors() } for name, hypervisor in hypervisors.items(): node = self.topology.get_node(name) self.assertEqual(name, node.name) self.assertIn(node, nodes) def test_list_openstack_topology(self, group=None, hostnames=None): nodes = topology.list_openstack_nodes(topology=self.topology, group=group, hostnames=hostnames) self.assertTrue(set(nodes).issubset(set(self.topology.nodes))) for node in nodes: if group: self.assertIn(group, node.groups) if hostnames: hostnames = [node_name_from_hostname(h) for h in hostnames] self.assertIn(node.name, hostnames) return nodes def test_list_openstack_topology_with_group(self): self.test_list_openstack_topology(group='compute') def test_list_openstack_topology_with_hostnames(self): expected_nodes = self.topology.nodes[0::2] actual_nodes = self.test_list_openstack_topology( hostnames=[node.name for node in expected_nodes]) self.assertEqual(expected_nodes, actual_nodes)
class CloudsFileKeystoneCredentialsFixture( _credentials.KeystoneCredentialsFixture): cloud_name = None clouds_content = None clouds_file = None config = tobiko.required_setup_fixture(DefaultCloudsFileConfig) def __init__(self, credentials=None, cloud_name=None, clouds_content=None, clouds_file=None, clouds_files=None): super(CloudsFileKeystoneCredentialsFixture, self).__init__( credentials=credentials) config = self.config if cloud_name is None: cloud_name = config.cloud_name self.cloud_name = cloud_name if clouds_content is not None: self.clouds_content = dict(clouds_content) if clouds_file is not None: self.clouds_file = clouds_file if clouds_files is None: clouds_files = config.clouds_files self.clouds_files = list(clouds_files) def get_credentials(self): cloud_name = self._get_cloud_name() if cloud_name is None: return None clouds_content = self._get_clouds_content() clouds_section = clouds_content.get("clouds") if clouds_section is None: message = ("'clouds' section not found in clouds file " "{!r}").format(self.clouds_file) raise ValueError(message) clouds_config = clouds_section.get(cloud_name) if clouds_config is None: message = ("No such cloud with name {!r} in file " "{!r}").format(cloud_name, self.clouds_file) raise ValueError(message) auth = clouds_config.get("auth") if auth is None: message = ("No such 'auth' section in cloud file {!r} for cloud " "name {!r}").format(self.clouds_file, self.cloud_name) raise ValueError(message) auth_url = auth.get("auth_url") if not auth_url: message = ("No such 'auth_url' in file {!r} for cloud name " "{!r}").format(self.clouds_file, self.cloud_name) raise ValueError(message) username = auth.get('username') or auth.get('user_id') password = auth.get('password') project_name = (auth.get('project_name') or auth.get('tenant_namer') or auth.get('project_id') or auth.get_env('tenant_id')) api_version = (int(clouds_config.get("identity_api_version", 0)) or _credentials.api_version_from_url(auth_url)) if api_version == 2: return _credentials.keystone_credentials( api_version=api_version, auth_url=auth_url, username=username, password=password, project_name=project_name) else: domain_name = (auth.get("domain_name") or auth.get("domain_id")) user_domain_name = (auth.get("user_domain_name") or auth.get("user_domain_id")) project_domain_name = auth.get("project_domain_name") project_domain_id = auth.get("project_domain_id") trust_id = auth.get("trust_id") return _credentials.keystone_credentials( api_version=api_version, auth_url=auth_url, username=username, password=password, project_name=project_name, domain_name=domain_name, user_domain_name=user_domain_name, project_domain_name=project_domain_name, project_domain_id=project_domain_id, trust_id=trust_id) def _get_cloud_name(self): cloud_name = self.cloud_name if cloud_name is None: cloud_name = os.environ.get("OS_CLOUD") if cloud_name: LOG.debug("Got cloud name from 'OS_CLOUD' environment " "variable: %r", cloud_name) self.cloud_name = cloud_name else: LOG.debug("Undefined environment variable: 'OS_CLOUD'") return cloud_name or None def _get_clouds_content(self): clouds_content = self.clouds_content if clouds_content is None: clouds_file = self._get_clouds_file() with open(clouds_file, 'r') as f: _, suffix = os.path.splitext(clouds_file) if suffix in JSON_SUFFIXES: LOG.debug('Load JSON clouds file: %r', clouds_file) clouds_content = json.load(f) else: LOG.debug('Load YAML clouds file: %r', clouds_file) clouds_content = yaml.safe_load(f) LOG.debug('Clouds file content loaded from %r:\n%s', clouds_file, json.dumps(clouds_content, indent=4, sort_keys=True)) self.clouds_content = clouds_content if not clouds_content: message = "Invalid clouds file content: {!r}".format( clouds_content) raise ValueError(message) return clouds_content def _get_clouds_file(self): clouds_file = self.clouds_file if clouds_file: clouds_files = [self.clouds_file] else: clouds_files = list(self.clouds_files) for filename in clouds_files: if os.path.exists(filename): LOG.debug('Found clouds file at %r', filename) self.clouds_file = clouds_file = filename break else: raise CloudsFileNotFoundError(clouds_files=', '.join(clouds_files)) return clouds_file
class L3haNetworkTest(NetworkTest): #: Resources stack with floating IP and Nova server stack = tobiko.required_setup_fixture(stacks.L3haPeerServerStackFixture)
class ListRequiredFixtureTest(unit.TobikoUnitTest): required_fixture = tobiko.required_fixture(MyRequiredFixture) required_setup_fixture = tobiko.required_setup_fixture( MyRequiredSetupFixture) def test_with_module(self): module = sys.modules[__name__] result = tobiko.list_required_fixtures([module]) self.assertEqual([], result) def test_with_module_name(self): result = tobiko.list_required_fixtures([__name__]) self.assertEqual([], result) def test_with_testcase_type(self): result = tobiko.list_required_fixtures([ListRequiredFixtureTest]) self.assertEqual([ canonical_name(MyRequiredFixture), canonical_name(MyRequiredSetupFixture) ], result) def test_with_testcase_name(self): result = tobiko.list_required_fixtures( [canonical_name(ListRequiredFixtureTest)]) self.assertEqual([ canonical_name(MyRequiredFixture), canonical_name(MyRequiredSetupFixture) ], result) def test_with_unbound_method(self, fixture=MyFixture, fixture2=MyFixture2): result = tobiko.list_required_fixtures( [ListRequiredFixtureTest.test_with_unbound_method]) self.assertEqual([ canonical_name(fixture), canonical_name(fixture2), canonical_name(MyRequiredFixture), canonical_name(MyRequiredSetupFixture) ], result) def test_with_bound_method(self, fixture=MyFixture, fixture2=MyFixture2): result = tobiko.list_required_fixtures([self.test_with_bound_method]) self.assertEqual([ canonical_name(fixture), canonical_name(fixture2), canonical_name(MyRequiredFixture), canonical_name(MyRequiredSetupFixture) ], result) def test_with_method_name(self, fixture=MyFixture, fixture2=MyFixture2): result = tobiko.list_required_fixtures([self.id()]) self.assertEqual([ canonical_name(fixture), canonical_name(fixture2), canonical_name(MyRequiredFixture), canonical_name(MyRequiredSetupFixture) ], result) def test_with_fixture_name(self): result = tobiko.list_required_fixtures([canonical_name(MyFixture)]) self.assertEqual([canonical_name(MyFixture)], result) def test_with_fixture(self): result = tobiko.list_required_fixtures([MyFixture()]) self.assertEqual([canonical_name(MyFixture)], result) def test_with_fixture_type(self): result = tobiko.list_required_fixtures([MyFixture]) self.assertEqual([canonical_name(MyFixture)], result) def test_required_fixture_property(self): fixture = self.required_fixture self.assertIsInstance(fixture, MyRequiredFixture) fixture.setup_fixture.assert_not_called() fixture.cleanup_fixture.assert_not_called() def test_required_setup_fixture_property(self): fixture = self.required_setup_fixture self.assertIsInstance(fixture, MyRequiredSetupFixture) fixture.setup_fixture.assert_called_once_with() fixture.cleanup_fixture.assert_not_called()
class OctaviaOtherMemberServerStackFixture( stacks.OctaviaMemberServerStackFixture): server_stack = tobiko.required_setup_fixture( OctaviaOtherServerStackFixture)
class PingTest(testtools.TestCase): namespace = tobiko.required_setup_fixture(fixtures.NetworkNamespaceFixture) def test_ping_recheable_address(self): result = ping.ping('127.0.0.1', count=3) self.assertIsNone(result.source) self.assertEqual(netaddr.IPAddress('127.0.0.1'), result.destination) result.assert_transmitted() result.assert_replied() def test_ping_reachable_hostname(self): result = ping.ping('example.org', count=3) self.assertIsNone(result.source) # self.assertIsNotNone(result.destination) result.assert_transmitted() result.assert_replied() def test_ping_unreachable_address(self): result = ping.ping('1.2.3.4', count=3) self.assertIsNone(result.source) self.assertEqual(netaddr.IPAddress('1.2.3.4'), result.destination) result.assert_transmitted() result.assert_not_replied() def test_ping_unreachable_hostname(self): ex = self.assertRaises(ping.UnknowHostError, ping.ping, 'unreachable-host', count=3) if ex.details: self.assertEqual('unreachable-host', ex.details) def test_ping_until_received(self): result = ping.ping_until_received('127.0.0.1', count=3) self.assertIsNone(result.source) self.assertEqual(netaddr.IPAddress('127.0.0.1'), result.destination) result.assert_transmitted() result.assert_replied() def test_ping_until_received_unreachable(self): ex = self.assertRaises(ping.PingFailed, ping.ping_until_received, '1.2.3.4', count=3, timeout=6) self.assertEqual(6, ex.timeout) self.assertEqual(0, ex.count) self.assertEqual(3, ex.expected_count) self.assertEqual('received', ex.message_type) def test_ping_until_unreceived_recheable(self): ex = self.assertRaises(ping.PingFailed, ping.ping_until_unreceived, '127.0.0.1', count=3, timeout=6) self.assertEqual(6, ex.timeout) self.assertEqual(0, ex.count) self.assertEqual(3, ex.expected_count) self.assertEqual('unreceived', ex.message_type) def test_ping_until_unreceived_unrecheable(self): result = ping.ping_until_unreceived('1.2.3.4', count=3) self.assertIsNone(result.source) self.assertEqual(netaddr.IPAddress('1.2.3.4'), result.destination) result.assert_transmitted() result.assert_not_replied() def test_ping_reachable_with_timeout(self): ex = self.assertRaises(ping.PingFailed, ping.ping, '127.0.0.1', count=20, timeout=1.) self.assertEqual(1., ex.timeout) self.assertEqual(20, ex.expected_count) self.assertEqual('transmitted', ex.message_type) def test_ping_hosts(self, ssh_client=None, network_namespace=None, **params): ips = ip.list_ip_addresses(ssh_client=ssh_client, network_namespace=network_namespace) reachable_ips, unrecheable_ips = ping.ping_hosts( ips, ssh_client=ssh_client, network_namespace=network_namespace, **params) expected_reachable = [i for i in ips if i in reachable_ips] self.assertEqual(expected_reachable, reachable_ips) expected_unreachable = [i for i in ips if i not in reachable_ips] self.assertEqual(expected_unreachable, unrecheable_ips) def test_ping_hosts_from_network_namespace(self): self.test_ping_hosts( ssh_client=self.namespace.ssh_client, network_namespace=self.namespace.network_namespace)
class SSHClientFixture(tobiko.SharedFixture): host = None port = 22 username = getpass.getuser() client = None default = tobiko.required_setup_fixture(_config.SSHDefaultConfigFixture) config_files = None host_config = None proxy_client = None proxy_sock = None connect_parameters = None schema = SSH_CONNECT_PARAMETERS def __init__(self, host=None, proxy_client=None, host_config=None, config_files=None, schema=None, **kwargs): super(SSHClientFixture, self).__init__() if host: self.host = host if proxy_client: self.proxy_client = proxy_client if host_config: self.host_config = host_config if config_files: self.config_files = config_files self.schema = schema = dict(schema or self.schema) self._connect_parameters = gather_ssh_connect_parameters(schema=schema, **kwargs) self._forwarders = [] def setup_fixture(self): self.setup_connect_parameters() self.setup_ssh_client() def setup_host_config(self): if not self.host_config: self.host_config = _config.ssh_host_config( host=self.host, config_files=self.config_files) return self.host_config def setup_connect_parameters(self): """Fill connect parameters dict Get parameters values from below sources: - parameters passed to class constructor - parameters got from ~/.ssh/config and tobiko.conf - parameters got from fixture object attributes """ self.setup_host_config() if not self.connect_parameters: self.connect_parameters = self.get_connect_parameters() return self.connect_parameters def get_connect_parameters(self, schema=None): schema = dict(schema or self.schema) parameters = {} for gather_parameters in [ self.gather_initial_connect_parameters, self.gather_host_config_connect_parameters, self.gather_default_connect_parameters ]: gather_parameters(destination=parameters, schema=schema, remove_from_schema=True) if parameters: LOG.debug('SSH connect parameters for host %r:\n%r', self.host, parameters) return parameters def gather_initial_connect_parameters(self, **kwargs): parameters = gather_ssh_connect_parameters( source=self._connect_parameters, **kwargs) if parameters: LOG.debug('Initial SSH connect parameters for host %r:\n' '%r', self.host, parameters) return parameters def gather_host_config_connect_parameters(self, **kwargs): parameters = gather_ssh_connect_parameters( source=self.host_config.connect_parameters, **kwargs) if parameters: LOG.debug( 'Host configured SSH connect parameters for host %r:\n' '%r', self.host, parameters) return parameters def gather_default_connect_parameters(self, **kwargs): parameters = gather_ssh_connect_parameters(source=self, **kwargs) if parameters: LOG.debug('Default SSH connect parameters for host %r:\n' '%r', self.host, parameters) return parameters def setup_ssh_client(self): self.client, self.proxy_sock = ssh_connect( proxy_client=self.proxy_client, **self.connect_parameters) self.addCleanup(self.cleanup_ssh_client) if self.proxy_sock: self.addCleanup(self.cleanup_proxy_sock) for forwarder in self._forwarders: self.useFixture(forwarder) def cleanup_ssh_client(self): client = self.client self.client = None if client: try: client.close() except Exception: LOG.exception('Error closing client (%r)', self) def cleanup_proxy_sock(self): proxy_sock = self.proxy_sock self.proxy_sock = None if proxy_sock: try: proxy_sock.close() except Exception: LOG.exception('Error closing proxy socket (%r)', self) def connect(self): return tobiko.setup_fixture(self).client def get_ssh_command(self, host=None, username=None, port=None, command=None, config_files=None, host_config=None, proxy_command=None, key_filename=None, **options): connect_parameters = self.setup_connect_parameters() host = host or connect_parameters.get('hostname') username = username or connect_parameters.get('username') port = port or connect_parameters.get('port') config_files = config_files or connect_parameters.get('config_files') if not host_config: _host_config = self.setup_host_config() if hasattr(_host_config, 'host_config'): _host_config = host_config key_filename = key_filename or connect_parameters.get('key_filename') proxy_command = (proxy_command or connect_parameters.get('proxy_command')) if not proxy_command and self.proxy_client: proxy_command = self.proxy_client.get_ssh_command() return _command.ssh_command(host=host, username=username, port=port, command=command, config_files=config_files, host_config=host_config, proxy_command=proxy_command, key_filename=key_filename, **options)
class UbuntuServerL3HAPortTestWith(PortTest): #: Resources stack with floating IP and Nova server stack = tobiko.required_setup_fixture(stacks.L3haUbuntuServerStackFixture)
class L3HaNetworkTestCase(NetworkTestCase): #: Stack of resources with a network with a gateway router stack = tobiko.required_setup_fixture(stacks.L3haNetworkStackFixture)
class ServerStackFixture(heat.HeatStackFixture): #: Heat template file template = _hot.heat_template_file('nova/server.yaml') #: stack with the key pair for the server instance key_pair_stack = tobiko.required_setup_fixture(KeyPairStackFixture) #: stack with the internal where the server port is created network_stack = tobiko.required_setup_fixture(_neutron.NetworkStackFixture) #: Glance image used to create a Nova server instance image_fixture = None @property def image(self): return self.image_fixture.image_id @property def username(self): """username used to login to a Nova server instance""" return self.image_fixture.username @property def password(self): """password used to login to a Nova server instance""" return self.image_fixture.password # Stack used to create flavor for Nova server instance flavor_stack = None @property def flavor(self): """Flavor for Nova server instance""" return self.flavor_stack.flavor_id #: Whenever port security on internal network is enable port_security_enabled = False #: Security groups to be associated to network ports security_groups = [] # type: typing.List[str] @property def key_name(self): return self.key_pair_stack.key_name @property def network(self): return self.network_stack.network_id #: Floating IP network where the Neutron floating IP is created floating_network = CONF.tobiko.neutron.floating_network @property def has_floating_ip(self): """Whenever to allocate floating IP for the server""" return bool(self.floating_network) @property def ssh_client(self): return ssh.ssh_client(host=self.ip_address, username=self.username, password=self.password) @property def ssh_command(self): return ssh.ssh_command(host=self.ip_address, username=self.username) @property def ip_address(self): if self.has_floating_ip: return self.floating_ip_address else: return self.outputs.fixed_ips[0]['ip_address'] #: Schedule on different host that this Nova server instance ID different_host = None #: Schedule on same host as this Nova server instance ID same_host = None @property def scheduler_hints(self): scheduler_hints = {} if self.different_host: scheduler_hints.update(different_host=self.different_host) if self.same_host: scheduler_hints.update(same_host=self.same_host) return scheduler_hints @property def server_details(self): return nova.get_server(self.server_id) @property def port_details(self): return neutron.get_port(self.port_id) def getDetails(self): # pylint: disable=W0212 details = super(ServerStackFixture, self).getDetails() stack = self.get_stack() if stack: details[self.fixture_name + '.stack'] = (self.details_content( get_json=lambda: stack._info)) if stack.stack_status == 'CREATE_COMPLETE': details[self.fixture_name + '.server_details'] = (self.details_content( get_json=lambda: self.server_details._info)) details[self.fixture_name + '.console_output'] = (self.details_content( get_text=lambda: self.console_output)) return details def details_content(self, **kwargs): return tobiko.details_content(content_id=self.fixture_name, **kwargs) max_console_output_length = 64 * 1024 @property def console_output(self): return nova.get_console_output(server=self.server_id, length=self.max_console_output_length)
class OpenStackTopology(tobiko.SharedFixture): config = tobiko.required_setup_fixture(OpenStackTopologyConfig) def __init__(self): super(OpenStackTopology, self).__init__() self._reachable_ips = set() self._unreachable_ips = set() self._nodes_by_name = collections.OrderedDict() self._nodes_by_ips = collections.OrderedDict() self._nodes_by_group = collections.OrderedDict() def setup_fixture(self): self.discover_nodes() def cleanup_fixture(self): self._reachable_ips.clear() self._unreachable_ips.clear() self._nodes_by_name.clear() self._nodes_by_ips.clear() self._nodes_by_group.clear() def discover_nodes(self): self.discover_configured_nodes() self.discover_controller_nodes() self.discover_compute_nodes() def discover_configured_nodes(self): for address in self.config.conf.nodes or []: self.add_node(address=address) def discover_controller_nodes(self): endpoints = keystone.list_endpoints(interface='public') addresses = set( parse.urlparse(endpoint.url).hostname for endpoint in endpoints) for address in addresses: self.add_node(address=address, group='controller') def discover_compute_nodes(self): for hypervisor in nova.list_hypervisors(): self.add_node(hostname=hypervisor.hypervisor_hostname, address=hypervisor.host_ip, group='compute') def add_node(self, hostname=None, address=None, group=None, ssh_client=None): name = hostname and node_name_from_hostname(hostname) or None ips = set() if address: ips.update(self._ips(address)) if hostname: ips.update(self._ips(hostname)) ips = tobiko.select(ips) try: node = self.get_node(name=name, address=ips) except _exception.NoSuchOpenStackTopologyNode: node = self._add_node(hostname=hostname, ips=ips, ssh_client=ssh_client) if node and group: self.add_group(group=group).append(node) node.add_group(group=group) return node def _add_node(self, ips, hostname=None, ssh_client=None): public_ip = self._public_ip(ips, ssh_client=ssh_client) if public_ip is None: LOG.debug("Unable to SSH connect to any node IP address: %s" ','.join(str(ip_address) for ip_address in ips)) return None # I need to get a name for the new node ssh_client = ssh_client or self._ssh_client(public_ip) hostname = hostname or sh.get_hostname(ssh_client=ssh_client) name = node_name_from_hostname(hostname) try: node = self._nodes_by_name[name] except KeyError: self._nodes_by_name[name] = node = self.create_node( name=name, public_ip=public_ip, ssh_client=ssh_client) other = self._nodes_by_ips.setdefault(public_ip, node) if node is not other: LOG.error("Two nodes have the same IP address (%s): %r, %r", public_ip, node.name, other.name) return node def get_node(self, name=None, hostname=None, address=None): name = name or (hostname and node_name_from_hostname(hostname)) details = {} if name: tobiko.check_valid_type(name, six.string_types) details['name'] = name try: return self._nodes_by_name[name] except KeyError: pass if address: details['address'] = address for ip_address in self._ips(address): try: return self._nodes_by_ips[ip_address] except KeyError: pass raise _exception.NoSuchOpenStackTopologyNode(details=details) def create_node(self, name, public_ip, ssh_client, **kwargs): return OpenStackTopologyNode(topology=self, name=name, public_ip=public_ip, ssh_client=ssh_client, **kwargs) @property def nodes(self): return tobiko.select( self.get_node(name) for name in self._nodes_by_name) def add_group(self, group): try: return self._nodes_by_group[group] except KeyError: self._nodes_by_group[group] = nodes = self.create_group() return nodes def create_group(self): return tobiko.Selection() def get_group(self, group): try: return self._nodes_by_group[group] except KeyError: raise _exception.NoSuchOpenStackTopologyNodeGroup(group=group) @property def groups(self): return list(self._nodes_by_group) def _ssh_client(self, address, username=None, port=None, key_filename=None, **ssh_parameters): username = username or self.config.conf.username port = port or self.config.conf.port key_filename = key_filename or self.config.conf.key_file return ssh.ssh_client(host=str(address), username=username, key_filename=key_filename, **ssh_parameters) def _public_ip(self, ips, ssh_client=None): reachable_ip = self._reachable_ip(ips) if reachable_ip: return reachable_ip if not ssh_client: # Try connecting via other nodes to get target node IP # addresses proxy_client = None for proxy_node in self.nodes: proxy_client = proxy_node.ssh_client if proxy_client: internal_ip = self._reachable_ip(ips, proxy_client=proxy_client) if internal_ip: ssh_client = self._ssh_client( internal_ip, proxy_client=proxy_client) break if ssh_client: break if ssh_client: # Connect via SSH to to get target node IP addresses ips = self._ips_from_host(ssh_client=ssh_client) reachable_ip = self._reachable_ip(ips) if reachable_ip: return reachable_ip LOG.warning('Unable to reach remote host via any IP address: %s', ', '.join(str(a) for a in ips)) return None def _reachable_ip(self, ips, proxy_client=None, **kwargs): reachable = None if proxy_client: untested_ips = ips else: # Exclude unreachable addresses untested_ips = list() for address in ips: if address not in self._unreachable_ips: if address in self._reachable_ips: # Will take result from the first one of marked already # marked as reachable reachable = reachable or address else: # Will later search for results between the other IPs untested_ips.append(address) for address in untested_ips: if reachable is None: try: received = ping.ping(address, count=1, timeout=5., ssh_client=proxy_client, **kwargs).received except ping.PingFailed: pass else: if received: reachable = address # Mark IP as reachable self._reachable_ips.add(address) continue # Mark IP as unreachable self._unreachable_ips.add(address) return reachable @property def ip_version(self): ip_version = self.config.conf.ip_version return ip_version and int(ip_version) or None def _ips_from_host(self, **kwargs): return ip.list_ip_addresses(ip_version=self.ip_version, scope='global', **kwargs) def _ips(self, obj): if isinstance(obj, tobiko.Selection): ips = obj elif isinstance(obj, netaddr.IPAddress): ips = tobiko.select([obj]) elif isinstance(obj, six.string_types): try: ips = tobiko.select([netaddr.IPAddress(obj)]) except (netaddr.AddrFormatError, ValueError): ips = resolve_host_ips(obj) else: for item in iter(obj): tobiko.check_valid_type(item, netaddr.IPAddress) ips = tobiko.select(obj) if ips and self.ip_version: ips = ips.with_attributes(version=self.ip_version) return ips
class OctaviaBasicTrafficScenarioTest(base.TobikoTest): """Octavia traffic scenario test. Create a load balancer with 2 members that run a server application, Create a client that is connected to the load balancer VIP port, Generate network traffic from the client to the load balanacer. """ loadbalancer_stack = tobiko.required_setup_fixture( stacks.OctaviaLoadbalancerStackFixture) listener_stack = tobiko.required_setup_fixture( stacks.OctaviaListenerStackFixture) member1_stack = tobiko.required_setup_fixture( stacks.OctaviaMemberServerStackFixture) member2_stack = tobiko.required_setup_fixture( OctaviaOtherMemberServerStackFixture) client_stack = tobiko.required_setup_fixture( stacks.OctaviaClientServerStackFixture) members_count = 2 def setUp(self): super(OctaviaBasicTrafficScenarioTest, self).setUp() # Wait for members self._check_member(self.member1_stack) self._check_member(self.member2_stack) # Check if load balancer is functional self._check_loadbalancer() def _request(self, client_stack, server_ip_address, protocol, server_port): """Perform a request on a server. Returns the response in case of success, throws an RequestException otherwise. """ if ':' in server_ip_address: # Add square brackets around IPv6 address to please curl server_ip_address = "[{}]".format(server_ip_address) cmd = "curl {} {}://{}:{}/id".format(CURL_OPTIONS, protocol.lower(), server_ip_address, server_port) ssh_client = ssh.ssh_client( client_stack.floating_ip_address, username=client_stack.image_fixture.username) ret = sh.ssh_execute(ssh_client, cmd) if ret.exit_status != 0: raise RequestException(command=cmd, error=ret.stderr) return ret.stdout def _wait_resource_operating_status(self, resource_type, operating_status, resource_get, *args): start = time.time() while time.time() - start < CONF.tobiko.octavia.check_timeout: res = resource_get(*args) if res['operating_status'] == operating_status: return time.sleep(CONF.tobiko.octavia.check_interval) raise TimeoutException( reason=("Cannot get operating_status '{}' from {} {} " "within the timeout period.".format( operating_status, resource_type, args))) def _wait_lb_operating_status(self, lb_id, operating_status): LOG.debug("Wait for loadbalancer {} to have '{}' " "operating_status".format(lb_id, operating_status)) self._wait_resource_operating_status("loadbalancer", operating_status, octavia.get_loadbalancer, lb_id) def _wait_for_request_data(self, client_stack, server_ip_address, server_protocol, server_port): """Wait until a request on a server succeeds Throws a TimeoutException after CONF.tobiko.octavia.check_timeout if the server doesn't reply. """ start = time.time() while time.time() - start < CONF.tobiko.octavia.check_timeout: try: ret = self._request(client_stack, server_ip_address, server_protocol, server_port) except Exception as e: LOG.warning("Received exception {} while performing a " "request".format(e)) else: return ret time.sleep(CONF.tobiko.octavia.check_interval) raise TimeoutException( reason=("Cannot get data from {} on port {} with " "protocol {} within the timeout period.".format( server_ip_address, server_port, server_protocol))) def _check_loadbalancer(self): """Wait until the load balancer is functional.""" # Check load balancer status loadbalancer_id = self.loadbalancer_stack.loadbalancer_id self._wait_lb_operating_status(loadbalancer_id, 'ONLINE') loadbalancer_vip = self.loadbalancer_stack.loadbalancer_vip loadbalancer_port = self.listener_stack.lb_port loadbalancer_protocol = self.listener_stack.lb_protocol self._wait_for_request_data(self.client_stack, loadbalancer_vip, loadbalancer_protocol, loadbalancer_port) def _check_member(self, member_stack): """Wait until a member server is functional.""" member_ip = member_stack.server_stack.floating_ip_address member_port = member_stack.application_port member_protocol = self.listener_stack.pool_protocol self._wait_for_request_data(self.client_stack, member_ip, member_protocol, member_port) def _check_members_balanced(self): """Check if traffic is properly balanced between members.""" replies = {} loadbalancer_vip = self.loadbalancer_stack.loadbalancer_vip loadbalancer_port = self.listener_stack.lb_port loadbalancer_protocol = self.listener_stack.lb_protocol for _ in range(20): content = self._request(self.client_stack, loadbalancer_vip, loadbalancer_protocol, loadbalancer_port) if content not in replies: replies[content] = 0 replies[content] += 1 # wait one second (required when using cirros' nc fake webserver) time.sleep(1) LOG.debug("Replies from load balancer: {}".format(replies)) # assert that 'members_count' servers replied self.assertEqual(len(replies), self.members_count) if self.listener_stack.lb_algorithm == 'ROUND_ROBIN': # assert that requests have been fairly dispatched (each server # received the same number of requests) self.assertEqual(len(set(replies.values())), 1) def test_traffic(self): self._check_members_balanced()
class L3haServerStackFixture(_cirros.CirrosServerStackFixture): #: Heat stack for creating internal network with L3HA enabled network_stack = tobiko.required_setup_fixture(L3haNetworkStackFixture)