[Podified] Implement way to discover OCP workers

In podified topology OCP workers are actually OpenStack control plane
nodes. Currently we don't do ssh to those nodes in Tobiko but in some
cases, like e.g. in some tests from the
tobiko.tests.faults.neutron.test_agents module we need to filter such
nodes out as Tobiko tries to ssh to every node which it founds in the
"neutron agent-list" output and there is at least one ovn-controller
running on the controller node (OCP workers).

To workaround that issue this patch adds discoverability of the OCP
workers but without trying to create ssh_client for those nodes.

Related-Jira: #OSP-22166
Change-Id: Ib52d6b1a88046017081db30ece8969a20ead73c1
This commit is contained in:
Slawek Kaplonski 2024-01-11 14:26:29 +01:00
parent d85945a608
commit cf666804ef
5 changed files with 95 additions and 16 deletions

View File

@ -217,7 +217,7 @@ class OpenStackTopologyNode:
def __init__(self,
topology: 'OpenStackTopology',
name: str,
ssh_client: ssh.SSHClientFixture,
ssh_client: typing.Optional[ssh.SSHClientFixture],
addresses: typing.Iterable[netaddr.IPAddress],
hostname: str):
self._topology = weakref.ref(topology)
@ -474,8 +474,9 @@ class OpenStackTopology(tobiko.SharedFixture):
addresses: typing.List[netaddr.IPAddress],
hostname: str = None,
ssh_client: ssh.SSHClientFixture = None,
create_ssh_client: bool = True,
**create_params):
if ssh_client is None:
if ssh_client is None and create_ssh_client:
ssh_client = self._ssh_connect(hostname=hostname,
addresses=addresses)
addresses.extend(self._list_addresses_from_host(ssh_client=ssh_client))
@ -485,10 +486,12 @@ class OpenStackTopology(tobiko.SharedFixture):
try:
node = self._names[name]
except KeyError:
ssh_login = (
ssh_client.login if ssh_client else "No SSH Client configured")
LOG.debug("Add topology node:\n"
f" - name: {name}\n"
f" - hostname: {hostname}\n"
f" - login: {ssh_client.login}\n"
f" - login: {ssh_login}\n"
f" - addresses: {addresses}\n")
self._names[name] = node = self.create_node(name=name,
hostname=hostname,
@ -622,7 +625,11 @@ class OpenStackTopology(tobiko.SharedFixture):
ip_version = self.config.conf.ip_version
return ip_version and int(ip_version) or None
def _list_addresses_from_host(self, ssh_client: ssh.SSHClientFixture):
def _list_addresses_from_host(
self,
ssh_client: typing.Optional[ssh.SSHClientFixture]):
if not ssh_client:
return tobiko.Selection()
return ip.list_ip_addresses(ssh_client=ssh_client,
ip_version=self.ip_version,
scope='global')

View File

@ -13,6 +13,7 @@
# under the License.
from __future__ import absolute_import
import netaddr
import openshift as oc
from oslo_log import log
@ -23,6 +24,7 @@ LOG = log.getLogger(__name__)
OSP_CONTROLPLANE = 'openstackcontrolplane'
OSP_DP_NODESET = 'openstackdataplanenodeset'
DP_SSH_SECRET_NAME = 'secret/dataplane-ansible-ssh-private-key-secret'
OCP_WORKERS = 'nodes'
OVN_DP_SERVICE_NAME = 'ovn'
COMPUTE_DP_SERVICE_NAMES = ['nova', 'nova-custom']
@ -50,6 +52,19 @@ def _get_group(services):
return EDPM_OTHER_GROUP
def _get_ocp_worker_hostname(worker):
for address in worker.get('status', {}).get('addresses', []):
if address.get('type') == 'Hostname':
return address['address']
def _get_ocp_worker_addresses(worker):
return [
netaddr.IPAddress(address['address']) for
address in worker.get('status', {}).get('addresses', [])
if address.get('type') != 'Hostname']
def has_podified_cp() -> bool:
if not _is_oc_client_available():
LOG.debug("Openshift CLI client isn't installed.")
@ -98,4 +113,12 @@ def list_edpm_nodes():
def list_ocp_workers():
pass
nodes_sel = oc.selector(OCP_WORKERS)
ocp_workers = []
for node in nodes_sel.objects():
node_dict = node.as_dict()
ocp_workers.append({
'hostname': _get_ocp_worker_hostname(node_dict),
'addresses': _get_ocp_worker_addresses(node_dict)
})
return ocp_workers

View File

@ -41,6 +41,8 @@ COMPUTE_GROUPS = [
_openshift.EDPM_OTHER_GROUP
]
ALL_COMPUTES_GROUP_NAME = 'compute'
OCP_WORKER = 'ocp_worker'
EDPM_NODE = 'edpm_node'
class PodifiedTopology(rhosp.RhospTopology):
@ -65,6 +67,10 @@ class PodifiedTopology(rhosp.RhospTopology):
neutron.FRR: 'frr'
}
def __init__(self):
super(PodifiedTopology, self).__init__()
self.ocp_workers = {}
def add_node(self,
hostname: typing.Optional[str] = None,
address: typing.Optional[str] = None,
@ -89,10 +95,13 @@ class PodifiedTopology(rhosp.RhospTopology):
return node
def create_node(self, name, ssh_client, **kwargs):
return EdpmNode(topology=self,
name=name,
ssh_client=ssh_client,
**kwargs)
node_type = kwargs.pop('node_type')
if node_type == OCP_WORKER:
return OcpWorkerNode(topology=self, name=name, ssh_client=None,
**kwargs)
else:
return EdpmNode(topology=self, name=name, ssh_client=ssh_client,
**kwargs)
def discover_nodes(self):
self.discover_ssh_proxy_jump_node()
@ -103,8 +112,23 @@ class PodifiedTopology(rhosp.RhospTopology):
pass
def discover_ocp_worker_nodes(self):
# TODO(slaweq): discover OCP nodes where OpenStack CP is running
pass
# NOTE(slaweq): For now this will only discover nodes but there will be
# no ssh_client created to ssh to those nodes. Getting
# ssh_client to those nodes may be implemented in the future if that
# will be needed, but this may be hard e.g. for the CRC environments as
# in that case internal OCP worker's IP address is not accessible from
# outside at all
for worker_data in _openshift.list_ocp_workers():
node = self._add_node(
addresses=worker_data['addresses'],
hostname=worker_data['hostname'],
ssh_client=None,
create_ssh_client=False,
node_type=OCP_WORKER)
group_nodes = self.add_group(group='controller')
if node not in group_nodes:
group_nodes.append(node)
node.add_group(group='controller')
def discover_edpm_nodes(self):
for node in _openshift.list_edpm_nodes():
@ -115,7 +139,8 @@ class PodifiedTopology(rhosp.RhospTopology):
ssh_client = _edpm.edpm_ssh_client(host_config=host_config)
node = self.add_node(address=host_config.host,
group=group,
ssh_client=ssh_client)
ssh_client=ssh_client,
node_type=EDPM_NODE)
assert isinstance(node, EdpmNode)
@ -128,5 +153,9 @@ class EdpmNode(rhosp.RhospNode):
LOG.debug(f"Ensuring EDPM node {self.name} power is off...")
class OcpWorkerNode(rhosp.RhospNode):
pass
def setup_podified_topology():
topology.set_default_openstack_topology_class(PodifiedTopology)

View File

@ -656,7 +656,14 @@ class OvnControllerTest(BaseAgentTest):
self.skipTest(f"Missing container(s): '{self.container_name}'")
for host in hosts:
ssh_client = topology.get_openstack_node(hostname=host).ssh_client
node = topology.get_openstack_node(hostname=host)
if not node.ssh_client:
LOG.debug(f'Host {host} is probably an OCP worker in '
f'the Podified environment. SSH to that kind of '
f'nodes is currently not supported.')
continue
ssh_client = node.ssh_client
pid = self._get_ovn_controller_pid(ssh_client)
self.assertIsNotNone(pid)
LOG.debug(f'Killing process {pid} from container '

View File

@ -16,6 +16,8 @@
from __future__ import absolute_import
import tobiko
from tobiko.shell import ping
from tobiko.shell import sh
from tobiko.tests.functional.openstack import test_topology
from tobiko import podified
@ -27,6 +29,17 @@ class PodifiedTopologyTest(test_topology.OpenStackTopologyTest):
def topology(self) -> podified.PodifiedTopology:
return tobiko.setup_fixture(podified.PodifiedTopology)
def test_controller_group(self):
self.skipTest("Discovery of the OCP workers is "
"not implemented yet.")
def test_ping_node(self):
# NOTE(slaweq): in podified topology we expect connectivity only to the
# edpm nodes, not to the OCP workers
for node in self.topology.get_group("compute"):
ping.ping(node.public_ip, count=1, timeout=5.).assert_replied()
def test_ssh_client(self):
# NOTE(slaweq): in podified topology we expect connectivity only to the
# edpm nodes, not to the OCP workers
for node in self.topology.get_group("compute"):
self.assertIsNotNone(node.ssh_client)
hostname = sh.ssh_hostname(
ssh_client=node.ssh_client).split('.')[0]
self.assertEqual(node.name, hostname)