Fix containers runtime discovery
Change-Id: If2547a3e265dfccd2a65e0ce87e3e389cea0dde6
This commit is contained in:
parent
87d0671260
commit
9a2a8f5e0f
@ -73,11 +73,12 @@ def test_neutron_agents_are_alive(timeout=300., interval=5.) \
|
||||
def ovn_dbs_are_synchronized(test_case):
|
||||
from tobiko.tripleo import containers
|
||||
# declare commands
|
||||
runtime_name = containers.get_container_runtime_name()
|
||||
search_container_cmd = (
|
||||
"%s ps --format '{{.Names}}' -f name=ovn-dbs-bundle" %
|
||||
containers.container_runtime_name)
|
||||
runtime_name)
|
||||
container_cmd_prefix = ('%s exec -uroot {container}' %
|
||||
containers.container_runtime_name)
|
||||
runtime_name)
|
||||
ovndb_sync_cmd = ('ovs-appctl -t /var/run/openvswitch/{ovndb_ctl_file} '
|
||||
'ovsdb-server/sync-status')
|
||||
ovndb_show_cmd = '{ovndb} show'
|
||||
|
@ -69,7 +69,7 @@ class BaseAgentTest(testtools.TestCase):
|
||||
@property
|
||||
def container_runtime_name(self):
|
||||
if overcloud.has_overcloud():
|
||||
return containers.container_runtime_name
|
||||
return containers.get_container_runtime_name()
|
||||
else:
|
||||
return 'docker'
|
||||
|
||||
|
31
tobiko/tests/functional/tripleo/test_containers.py
Normal file
31
tobiko/tests/functional/tripleo/test_containers.py
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright 2021 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 testtools
|
||||
|
||||
from tobiko.tripleo import containers
|
||||
|
||||
|
||||
@containers.skip_unless_has_container_runtime()
|
||||
class RuntimeRuntimeTest(testtools.TestCase):
|
||||
|
||||
def test_get_container_runtime(self):
|
||||
runtime = containers.get_container_runtime()
|
||||
self.assertIsInstance(runtime, containers.ContainerRuntime)
|
||||
|
||||
def test_list_containers(self):
|
||||
containers_list = containers.list_containers()
|
||||
for container in containers_list:
|
||||
self.assertIsNotNone(container)
|
@ -1,13 +1,14 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import abc
|
||||
import functools
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import typing
|
||||
|
||||
from oslo_log import log
|
||||
import pandas
|
||||
import docker as dockerlib
|
||||
|
||||
import tobiko
|
||||
from tobiko import podman
|
||||
@ -23,57 +24,136 @@ from tobiko.tripleo import topology as tripleo_topology
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def get_container_runtime_module():
|
||||
"""check what container runtime is running
|
||||
and return a handle to it"""
|
||||
# TODO THIS LOCKS SSH CLIENT TO CONTROLLER
|
||||
ssh_client = topology.list_openstack_nodes(group='controller')[
|
||||
0].ssh_client
|
||||
if docker.is_docker_running(ssh_client=ssh_client):
|
||||
return docker
|
||||
else:
|
||||
return podman
|
||||
class ContainerRuntime(abc.ABC):
|
||||
runtime_name: str
|
||||
version_pattern: typing.Pattern
|
||||
|
||||
def match_version(self, version: str) -> bool:
|
||||
for version_line in version.splitlines():
|
||||
if self.version_pattern.match(version_line) is not None:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_client(self, ssh_client):
|
||||
for attempt in tobiko.retry(timeout=60.0,
|
||||
interval=5.0):
|
||||
try:
|
||||
client = self._get_client(ssh_client=ssh_client)
|
||||
break
|
||||
# TODO chose a better exception type
|
||||
except Exception:
|
||||
if attempt.is_last:
|
||||
raise
|
||||
LOG.debug('Unable to connect to docker server',
|
||||
exc_info=1)
|
||||
ssh.reset_default_ssh_port_forward_manager()
|
||||
else:
|
||||
raise RuntimeError("Broken retry loop")
|
||||
return client
|
||||
|
||||
def _get_client(self, ssh_client):
|
||||
raise NotImplementedError
|
||||
|
||||
def list_containers(self, ssh_client):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def get_container_runtime_name():
|
||||
return container_runtime_module.__name__.rsplit('.', 1)[1]
|
||||
class DockerContainerRuntime(ContainerRuntime):
|
||||
runtime_name = 'docker'
|
||||
version_pattern = re.compile('Docker version .*')
|
||||
|
||||
def _get_client(self, ssh_client):
|
||||
return docker.get_docker_client(ssh_client=ssh_client,
|
||||
sudo=True).connect()
|
||||
|
||||
def list_containers(self, ssh_client):
|
||||
client = self.get_client(ssh_client=ssh_client)
|
||||
return docker.list_docker_containers(client=client)
|
||||
|
||||
|
||||
if overcloud.has_overcloud():
|
||||
container_runtime_module = get_container_runtime_module()
|
||||
container_runtime_name = get_container_runtime_name()
|
||||
class PodmanContainerRuntime(ContainerRuntime):
|
||||
runtime_name = 'podman'
|
||||
version_pattern = re.compile('Podman version .*')
|
||||
|
||||
def _get_client(self, ssh_client):
|
||||
return podman.get_podman_client(ssh_client=ssh_client).connect()
|
||||
|
||||
def list_containers(self, ssh_client):
|
||||
client = self.get_client(ssh_client=ssh_client)
|
||||
return podman.list_podman_containers(client=client)
|
||||
|
||||
|
||||
DOCKER_RUNTIME = DockerContainerRuntime()
|
||||
PODMAN_RUNTIME = PodmanContainerRuntime()
|
||||
CONTAINER_RUNTIMES = [PODMAN_RUNTIME, DOCKER_RUNTIME]
|
||||
|
||||
|
||||
class ContainerRuntimeFixture(tobiko.SharedFixture):
|
||||
|
||||
runtime: typing.Optional[ContainerRuntime] = None
|
||||
|
||||
def setup_fixture(self):
|
||||
if overcloud.has_overcloud():
|
||||
self.runtime = self.get_runtime()
|
||||
|
||||
def cleanup_fixture(self):
|
||||
self.runtime = None
|
||||
|
||||
@staticmethod
|
||||
def get_runtime() -> typing.Optional[ContainerRuntime]:
|
||||
"""check what container runtime is running
|
||||
and return a handle to it"""
|
||||
# TODO THIS LOCKS SSH CLIENT TO CONTROLLER
|
||||
for node in topology.list_openstack_nodes(group='controller'):
|
||||
try:
|
||||
result = sh.execute('podman --version || docker --version',
|
||||
ssh_client=node.ssh_client)
|
||||
except sh.ShellCommandFailed:
|
||||
continue
|
||||
for runtime in CONTAINER_RUNTIMES:
|
||||
for version in [result.stdout, result.stderr]:
|
||||
if runtime.match_version(version):
|
||||
return runtime
|
||||
raise RuntimeError(
|
||||
"Unable to find any container runtime in any overcloud "
|
||||
"controller node")
|
||||
|
||||
|
||||
def get_container_runtime() -> ContainerRuntime:
|
||||
runtime = tobiko.setup_fixture(ContainerRuntimeFixture).runtime
|
||||
return runtime
|
||||
|
||||
|
||||
def get_container_runtime_name() -> str:
|
||||
return get_container_runtime().runtime_name
|
||||
|
||||
|
||||
def is_docker() -> bool:
|
||||
return get_container_runtime().runtime_name == 'docker'
|
||||
|
||||
|
||||
def is_podman() -> bool:
|
||||
return get_container_runtime().runtime_name == 'podman'
|
||||
|
||||
|
||||
def has_container_runtime() -> bool:
|
||||
return get_container_runtime() is not None
|
||||
|
||||
|
||||
def skip_unless_has_container_runtime():
|
||||
return tobiko.skip_unless('Container runtime not found',
|
||||
has_container_runtime)
|
||||
|
||||
|
||||
@functools.lru_cache()
|
||||
def list_node_containers(ssh_client):
|
||||
"""returns a list of containers and their run state"""
|
||||
client = get_container_client(ssh_client=ssh_client)
|
||||
if container_runtime_module == podman:
|
||||
return container_runtime_module.list_podman_containers(client=client)
|
||||
|
||||
elif container_runtime_module == docker:
|
||||
return container_runtime_module.list_docker_containers(client=client)
|
||||
return get_container_runtime().list_containers(ssh_client=ssh_client)
|
||||
|
||||
|
||||
def get_container_client(ssh_client=None):
|
||||
"""returns a list of containers and their run state"""
|
||||
|
||||
for attempt in tobiko.retry(
|
||||
timeout=60.0,
|
||||
interval=5.0):
|
||||
try:
|
||||
if container_runtime_module == podman:
|
||||
return container_runtime_module.get_podman_client(
|
||||
ssh_client=ssh_client).connect()
|
||||
elif container_runtime_module == docker:
|
||||
return container_runtime_module.get_docker_client(
|
||||
ssh_client=ssh_client).connect()
|
||||
except dockerlib.errors.DockerException:
|
||||
LOG.debug('Unable to connect to docker API')
|
||||
attempt.check_limits()
|
||||
ssh.reset_default_ssh_port_forward_manager()
|
||||
# no successful connection to docker/podman API has been performed
|
||||
raise RuntimeError('Unable to connect to container mgmt tool')
|
||||
return get_container_runtime().get_client(ssh_client=ssh_client)
|
||||
|
||||
|
||||
def list_containers_df(group=None):
|
||||
@ -93,13 +173,13 @@ def list_containers(group=None):
|
||||
# AttributeError: module 'tobiko.openstack.topology' has no
|
||||
# attribute 'container_runtime'
|
||||
|
||||
if group is None:
|
||||
group = 'overcloud'
|
||||
containers_list = tobiko.Selection()
|
||||
if group:
|
||||
openstack_nodes = topology.list_openstack_nodes(group=group)
|
||||
else:
|
||||
openstack_nodes = topology.list_openstack_nodes(group='overcloud')
|
||||
openstack_nodes = topology.list_openstack_nodes(group=group)
|
||||
|
||||
for node in openstack_nodes:
|
||||
LOG.debug(f"List containers for node {node.name}")
|
||||
node_containers_list = list_node_containers(ssh_client=node.ssh_client)
|
||||
containers_list.extend(node_containers_list)
|
||||
return containers_list
|
||||
@ -222,6 +302,7 @@ def assert_all_tripleo_containers_running():
|
||||
def assert_ovn_containers_running():
|
||||
# specific OVN verifications
|
||||
if neutron.has_ovn():
|
||||
container_runtime_name = get_container_runtime_name()
|
||||
ovn_controller_containers = ['ovn_controller',
|
||||
'ovn-dbs-bundle-{}-'.
|
||||
format(container_runtime_name)]
|
||||
@ -275,6 +356,7 @@ def run_container_config_validations():
|
||||
'expected_value': 'openvswitch'}]}]
|
||||
config_checkings += ovs_config_checkings
|
||||
|
||||
container_runtime_name = get_container_runtime_name()
|
||||
for config_check in config_checkings:
|
||||
for node in topology.list_openstack_nodes(
|
||||
group=config_check['node_group']):
|
||||
@ -320,18 +402,17 @@ def comparable_container_keys(container, include_container_objects=False):
|
||||
container._client._context.hostname), # pylint: disable=W0212
|
||||
container.data['names'], container.data['status'])
|
||||
|
||||
if container_runtime_module == podman and include_container_objects:
|
||||
if is_podman() and include_container_objects:
|
||||
return con_host_name_stat_obj_tuple
|
||||
|
||||
elif container_runtime_module == podman:
|
||||
elif is_podman():
|
||||
return con_host_name_stat_tuple
|
||||
|
||||
elif container_runtime_module == docker and include_container_objects:
|
||||
elif is_docker() and include_container_objects:
|
||||
return (container.attrs['Config']['Hostname'],
|
||||
container.attrs['Name'].strip('/'),
|
||||
container.attrs['State']['Status'],
|
||||
container)
|
||||
elif container_runtime_module == docker:
|
||||
elif is_docker() == docker:
|
||||
return (container.attrs['Config']['Hostname'],
|
||||
container.attrs['Name'].strip('/'),
|
||||
container.attrs['State']['Status'])
|
||||
|
Loading…
Reference in New Issue
Block a user