diff --git a/tobiko/openstack/nova/__init__.py b/tobiko/openstack/nova/__init__.py index 3b3d90285..99ef56c6b 100644 --- a/tobiko/openstack/nova/__init__.py +++ b/tobiko/openstack/nova/__init__.py @@ -15,6 +15,7 @@ from __future__ import absolute_import from tobiko.openstack.nova import _client from tobiko.openstack.nova import _hypervisor +from tobiko.openstack.nova import _server CLIENT_CLASSES = _client.CLIENT_CLASSES @@ -26,7 +27,9 @@ nova_client = _client.nova_client NovaClientFixture = _client.NovaClientFixture find_hypervisor = _client.find_hypervisor get_console_output = _client.get_console_output - get_server = _client.get_server skip_if_missing_hypervisors = _hypervisor.skip_if_missing_hypervisors + +find_server_ip_address = _server.find_server_ip_address +list_server_ip_addresses = _server.list_server_ip_addresses diff --git a/tobiko/openstack/nova/_server.py b/tobiko/openstack/nova/_server.py new file mode 100644 index 000000000..20e150793 --- /dev/null +++ b/tobiko/openstack/nova/_server.py @@ -0,0 +1,50 @@ +# Copyright 2019 Red Hat +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from __future__ import absolute_import + +import netaddr + +import tobiko +from tobiko.shell import ping + + +def list_server_ip_addresses(server, network_name=None, ip_version=None, + check_connectivity=False): + selected_addresses = [] + for _network_name, addresses in server.addresses.items(): + if network_name and network_name != _network_name: + continue + for address in addresses: + _ip_version = address['version'] + if ip_version and ip_version != _ip_version: + continue + ip_address = netaddr.IPAddress(address['addr'], + version=_ip_version) + if check_connectivity: + if not ping.ping(host=ip_address).received: + continue + selected_addresses.append(ip_address) + return tobiko.Selection(selected_addresses) + + +def find_server_ip_address(server, network_name=None, ip_version=None, + check_connectivity=False, unique=False): + addresses = list_server_ip_addresses(server=server, + network_name=network_name, + ip_version=ip_version, + check_connectivity=check_connectivity) + if unique: + return addresses.unique + else: + return addresses.first diff --git a/tobiko/tests/functional/tripleo/test_overcloud.py b/tobiko/tests/functional/tripleo/test_overcloud.py index 86b3f4046..1e40ce3aa 100644 --- a/tobiko/tests/functional/tripleo/test_overcloud.py +++ b/tobiko/tests/functional/tripleo/test_overcloud.py @@ -16,10 +16,10 @@ from __future__ import absolute_import import netaddr import testtools -import tobiko from tobiko import config -from tobiko.shell import ping +from tobiko.openstack import nova from tobiko.tripleo import overcloud +import tobiko CONF = config.CONF @@ -40,32 +40,34 @@ class OvercloudSshConnectionTest(testtools.TestCase): @overcloud.skip_if_missing_overcloud -class OvercloudNovaAPITest(testtools.TestCase): +class OvercloudNovaApiTest(testtools.TestCase): def test_list_overcloud_nodes(self): nodes = overcloud.list_overcloud_nodes() self.assertTrue(nodes) - expected_network_name = None for node in nodes: - network_name, node_ip = get_recheable_node_ip(node=node) - self.assertTrue(node_ip) - if expected_network_name: - self.assertEqual(expected_network_name, network_name) - else: - expected_network_name = network_name + node_ip = nova.find_server_ip_address(server=node, + check_connectivity=True) + self.assertIsInstance(node_ip, netaddr.IPAddress) def test_find_overcloud_nodes(self): node = overcloud.find_overcloud_node() - network_name, node_ip = get_recheable_node_ip(node=node) - self.assertTrue(network_name) - self.assertTrue(node_ip) + node_ip = nova.find_server_ip_address(server=node, + check_connectivity=True) + self.assertIsInstance(node_ip, netaddr.IPAddress) + def test_get_overcloud_node_ip_address(self): + overcloud_node_ip = overcloud.overcloud_node_ip_address() + self.assertIsInstance(overcloud_node_ip, netaddr.IPAddress) -def get_recheable_node_ip(node): - for network_name, addresses in node.addresses.items(): - for address in addresses: - ip_address = netaddr.IPAddress(address['addr'], - version=address['version']) - if ping.ping(host=ip_address).received: - return network_name, ip_address - tobiko.fail('Unrecheable overcloud node {!r}', node.id) + def test_overcloud_host_config(self): + host_config = tobiko.setup_fixture( + overcloud.overcloud_host_config(hostname='controller-0')) + self.assertEqual('controller-0', host_config.host) + self.assertIsInstance(host_config.hostname, netaddr.IPAddress) + self.assertEqual(CONF.tobiko.tripleo.overcloud_ssh_port, + host_config.port) + self.assertEqual(CONF.tobiko.tripleo.overcloud_ssh_username, + host_config.username) + self.assertEqual(CONF.tobiko.tripleo.ssh_key_filename, + host_config.key_filename) diff --git a/tobiko/tripleo/config.py b/tobiko/tripleo/config.py index 3589dd02b..14f2c8ea9 100644 --- a/tobiko/tripleo/config.py +++ b/tobiko/tripleo/config.py @@ -48,7 +48,12 @@ OPTIONS = [ help="Default username used to connect to overcloud nodes"), cfg.StrOpt('overcloud_rcfile', default='~/overcloudrc', - help="Overcloud RC filename")] + help="Overcloud RC filename"), + cfg.IntOpt('overcloud_ip_version', + help=("Default IP address version to be used to connect to " + "overcloud nodes ")), + cfg.StrOpt('overcloud_network_name', + help="Name of network used to connect to overcloud nodes")] def register_tobiko_options(conf): diff --git a/tobiko/tripleo/overcloud.py b/tobiko/tripleo/overcloud.py index 28397c5ea..d400d1c60 100644 --- a/tobiko/tripleo/overcloud.py +++ b/tobiko/tripleo/overcloud.py @@ -13,6 +13,8 @@ # under the License. from __future__ import absolute_import +import six + import tobiko from tobiko import config from tobiko.openstack import keystone @@ -52,3 +54,45 @@ def find_overcloud_node(**params): session = undercloud.undercloud_keystone_session() client = nova.get_nova_client(session=session) return nova.find_server(client=client, **params) + + +def overcloud_host_config(hostname, ip_version=None, network_name=None): + host_config = OvercloudHostConfig(host=hostname, + ip_version=ip_version, + network_name=network_name) + return tobiko.setup_fixture(host_config) + + +def overcloud_node_ip_address(ip_version=None, network_name=None, + **params): + server = find_overcloud_node(**params) + ip_version = ip_version or CONF.tobiko.tripleo.overcloud_ip_version + network_name = network_name or CONF.tobiko.tripleo.overcloud_network_name + return nova.find_server_ip_address(server=server, ip_version=ip_version, + network_name=network_name) + + +class OvercloudHostConfig(tobiko.SharedFixture): + hostname = None + port = None + username = None + key_filename = None + ip_version = None + network_name = None + + def __init__(self, host, ip_version=None, network_name=None): + 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 + + def setup_fixture(self): + self.hostname = overcloud_node_ip_address( + name=self.host, ip_version=self.ip_version, + network_name=self.network_name) + self.port = CONF.tobiko.tripleo.overcloud_ssh_port + self.username = CONF.tobiko.tripleo.overcloud_ssh_username + self.key_filename = CONF.tobiko.tripleo.ssh_key_filename