Merge "Tobiko does not customize images during run time"

This commit is contained in:
Zuul 2025-01-08 15:29:25 +00:00 committed by Gerrit Code Review
commit 8914cf20e8
8 changed files with 25 additions and 212 deletions

View File

@ -38,9 +38,6 @@
# Default cirros password (string value)
#password = <None>
# Default cirros interface name (string value)
#interface_name = <None>
# Default cirros SSH connection timeout (seconds) (floating point value)
#connection_timeout = <None>
@ -537,9 +534,6 @@
# Default ubuntu password (string value)
#password = <None>
# Default ubuntu interface name (string value)
#interface_name = <None>
# Default ubuntu SSH connection timeout (seconds) (floating point value)
#connection_timeout = <None>

View File

@ -0,0 +1,15 @@
---
deprecations:
- |
CustomizedGlanceImageFixture class is not supported anymore.
This means the Ubuntu image provided via tobiko.conf, which could be customized during test execution in previous tobiko versions,
has to include all the expected customizations.
When using the tobiko ansible roles, the tobiko-download-images roles performes the required customizations by default.
The URL to download an image without any customizations and the command that are executed during the opendev jobs can be found here:
https://opendev.org/x/tobiko/src/commit/eb83ebe860dfb4206b346a822675c01f8ba82ccf/roles/tobiko-common/defaults/main.yaml#L62
- |
Due to the deprecation of the CustomizedGlanceImageFixture class, some configuration parameters have been deprecated at image level.
* `interface_name` is not supported anymore because tobiko does not need to create a VLAN over that interface (the customized image already includes it).
* `customized_image_provided` boolean is not supported anymore because the provided images were previously customized.

View File

@ -56,6 +56,7 @@ test_log_file: '{{ test_report_dir | realpath }}/tobiko.log'
# Local where test cases results are being collected to
test_collect_dir: '{{ test_src_dir | realpath }}/{{ test_report_name }}'
# --- download-images options -------------------------------------------------
download_images_dir: "{{ ansible_user_dir }}/.downloaded-images"
download_images:
@ -72,5 +73,10 @@ download_images:
--run-command 'systemctl enable iperf3-server@5201'
--run-command 'echo "8021q" >> /etc/modules'
--run-command
'echo "network:\n version: 2\n vlans:\n vlan101:\n dhcp4: true\n dhcp4-overrides:\n use-routes: false\n dhcp6: true\n dhcp6-overrides:\n use-routes: false\n id: 101\n link: {{ ubuntu_nic_name | default('ens3') }}\n"
'echo "network:\n version: 2\n vlans:\n vlan101:\n dhcp4: true\n dhcp4-overrides:\n use-routes: false\n dhcp6: true\n dhcp6-overrides:\n use-routes: false\n id: 101\n link: {{ ubuntu_nic_name }}\n"
> /etc/netplan/75-tobiko-vlan.yaml'
# NIC name expected on Ubuntu VM instances.
# Depending on the openstack version, it has been observed the Ubuntu VM
# instances are created with an interface named 'ens3' or 'enp3s0'
ubuntu_nic_name: 'ens3'

View File

@ -25,17 +25,7 @@
- section: "{{ dict_value.type }}"
option: image_url
value: "file://{{ download_images_dir }}/{{ file_name }}"
{% if dict_value.customized | default(False) | bool or dict_value.customize_command_pattern is defined %}
- section: "{{ dict_value.type }}"
option: customized_image_provided
value: True
{% endif %}
{% endfor %}
{% if ubuntu_nic_name is defined %}
- section: ubuntu
option: interface_name
value: "{{ ubuntu_nic_name }}"
{% endif %}
vars:
sections: "{{ test_default_conf | combine(test_conf, recursive=True) }}"

View File

@ -31,7 +31,6 @@ delete_image = _client.delete_image
GlanceImageFixture = _image.GlanceImageFixture
HasImageMixin = _image.HasImageMixin
UrlGlanceImageFixture = _image.UrlGlanceImageFixture
CustomizedGlanceImageFixture = _image.CustomizedGlanceImageFixture
open_image_file = _io.open_image_file

View File

@ -18,8 +18,8 @@ import io
import os
import tempfile
import time
import typing # noqa
from abc import ABC, abstractmethod
import typing
from abc import ABC
from urllib.parse import urlparse
from oslo_log import log
@ -30,7 +30,6 @@ from tobiko.config import get_bool_env
from tobiko.openstack.glance import _client
from tobiko.openstack.glance import _io
from tobiko.openstack import keystone
from tobiko.shell import sh
LOG = log.getLogger(__name__)
@ -347,11 +346,7 @@ class UrlGlanceImageFixture(UploadGlanceImageFixture, ABC):
# else, download the image
return self.get_image_from_url(real_image_file)
def customize_image_file(self, base_file: str) -> str:
return base_file
def get_image_from_file(self, image_file: str):
image_file = self.customize_image_file(base_file=image_file)
image_size = os.path.getsize(image_file)
LOG.debug('Uploading image %r data from file %r (%d bytes)',
self.image_name, image_file, image_size)
@ -418,114 +413,6 @@ class UrlGlanceImageFixture(UploadGlanceImageFixture, ABC):
os.rename(temp_file, image_file)
class CustomizedGlanceImageFixture(UrlGlanceImageFixture, ABC):
@property
def firstboot_commands(self) -> typing.List[str]:
return []
@property
def install_packages(self) -> typing.List[str]:
return []
@property
def run_commands(self) -> typing.List[str]:
return []
@property
def write_files(self) -> typing.Dict[str, str]:
return {}
@property
@abstractmethod
def customization_required(self) -> bool:
pass
username: str = ''
password: str = ''
def _get_customized_suffix(self) -> str:
return 'customized'
def customize_image_file(self, base_file: str) -> str:
def workaround_passt(full_command, exc):
which_passt = sh.execute(
'which passt', expect_exit_status=None).stdout.rstrip()
if which_passt == '':
raise exc
cmd = f'mv {which_passt} {which_passt}.bak'
cmd_cleanup = f'mv {which_passt}.bak {which_passt}'
tobiko.add_cleanup(sh.execute, cmd_cleanup,
expect_exit_status=None, sudo=True)
LOG.exception("Executing virt-customize without passt")
sh.execute(cmd, sudo=True)
sh.execute(full_command)
# if the image does not have to be customized, then do nothing
if not self.customization_required:
return base_file
customized_file = f'{base_file}-{self._get_customized_suffix()}'
if os.path.isfile(customized_file):
if (os.stat(base_file).st_mtime_ns <
os.stat(customized_file).st_mtime_ns):
LOG.debug(f"Image file is up to date '{customized_file}'")
return customized_file
else:
LOG.debug(f"Remove obsolete image file '{customized_file}'")
os.remove(customized_file)
work_file = sh.execute('mktemp').stdout.strip()
try:
LOG.debug(f"Copy base image file: '{base_file}' to '{work_file}'")
sh.put_file(base_file, work_file)
options = self.get_virt_customize_options()
if options:
command = sh.shell_command(['LIBGUESTFS_BACKEND=direct',
'virt-customize',
'-a',
work_file])
try:
sh.execute(command + options)
except sh.ShellCommandFailed as exc:
workaround_passt(command + options, exc)
sh.get_file(work_file, customized_file)
return customized_file
finally:
sh.execute(['rm', '-f', work_file])
def get_virt_customize_options(self) -> sh.ShellCommand:
options = sh.ShellCommand()
firstboot_commands = self.firstboot_commands
if firstboot_commands:
for cmd in firstboot_commands:
options += ['--firstboot-command', cmd]
install_packages = self.install_packages
if install_packages:
options += ['--install', ','.join(install_packages)]
username = self.username
password = self.password
if username and password:
options += f'--password "{username}:password:{password}"'
run_commands = self.run_commands
if run_commands:
for cmd in run_commands:
options += ['--run-command', cmd]
write_files = self.write_files
if write_files:
for filename, content in write_files.items():
options += ['--write', f'{filename}:{content}']
return options
class InvalidGlanceImageStatus(tobiko.TobikoException):
message = ("Invalid image {image_name!r} (id {image_id!r}) status: "
"{actual_status!r} not in {expected_status!r}")

View File

@ -58,9 +58,6 @@ def get_images_options():
help="Default " + name + " username"),
cfg.StrOpt('password',
help="Default " + name + " password"),
cfg.StrOpt('interface_name',
default=None if name != 'ubuntu' else 'ens3',
help="Default " + name + " interface name"),
cfg.FloatOpt('connection_timeout',
default=None,
help=("Default " + name +
@ -70,10 +67,6 @@ def get_images_options():
help=("Allow to disable SSH auth algorithms"
"in order to SSH to old servers like"
"CirrOS ones")),
cfg.BoolOpt('customized_image_provided',
default=False,
help=("Whether the provided image (URL or file) is"
"already customized or not"))
]
)]

View File

@ -13,8 +13,6 @@
# under the License.
from __future__ import absolute_import
import typing
import tobiko
from tobiko import config
from tobiko.openstack import glance
@ -41,7 +39,7 @@ DefaultInstance=5201
"""
class UbuntuImageFixture(glance.CustomizedGlanceImageFixture):
class UbuntuImageFixture(glance.UrlGlanceImageFixture):
"""Ubuntu server image running an HTTP server
The server has additional installed packages compared to
@ -68,30 +66,6 @@ class UbuntuImageFixture(glance.CustomizedGlanceImageFixture):
disabled_algorithms = CONF.tobiko.ubuntu.disabled_algorithms
is_reachable_timeout = CONF.tobiko.nova.ubuntu_is_reachable_timeout
def __init__(self,
ethernet_devide: str = None,
**kwargs):
super().__init__(**kwargs)
self._ethernet_device = ethernet_devide
@property
def customization_required(self) -> bool:
return not CONF.tobiko.ubuntu.customized_image_provided
@property
def firstboot_commands(self) -> typing.List[str]:
return super().firstboot_commands + [
'sh -c "hostname > /var/www/html/id"']
@property
def install_packages(self) -> typing.List[str]:
return super().install_packages + ['iperf3',
'iputils-ping',
'nano',
'ncat',
'nginx',
'vlan']
# port of running HTTP server
http_port = 80
@ -102,22 +76,6 @@ class UbuntuImageFixture(glance.CustomizedGlanceImageFixture):
def iperf3_service_name(self) -> str:
return f"iperf3-server@{self.iperf3_port}.service"
@property
def run_commands(self) -> typing.List[str]:
run_commands = super().run_commands
run_commands.append(
f'echo "{IPERF3_SERVICE_FILE}" '
'> /etc/systemd/system/iperf3-server@.service')
run_commands.append(
f"systemctl enable iperf3-server@{self.iperf3_port}")
run_commands.append('echo "8021q" >> /etc/modules')
return run_commands
ethernet_device = CONF.tobiko.ubuntu.interface_name
def _get_customized_suffix(self) -> str:
return f'{super()._get_customized_suffix()}-{self.ethernet_device}'
@property
def vlan_id(self) -> int:
return tobiko.tobiko_config().neutron.vlan_id
@ -126,35 +84,6 @@ class UbuntuImageFixture(glance.CustomizedGlanceImageFixture):
def vlan_device(self) -> str:
return f'vlan{self.vlan_id}'
@property
def vlan_config(self) -> typing.Dict[str, typing.Any]:
return {
'network': {
'version': 2,
'vlans': {
self.vlan_device: {
'link': self.ethernet_device,
'dhcp4': True,
'dhcp4-overrides': {
'use-routes': False
},
'dhcp6': True,
'dhcp6-overrides': {
'use-routes': False
},
'id': self.vlan_id,
}
}
}
}
@property
def write_files(self) -> typing.Dict[str, str]:
write_files = super().write_files
write_files['/etc/netplan/75-tobiko-vlan.yaml'] = tobiko.dump_yaml(
self.vlan_config)
return write_files
class UbuntuFlavorStackFixture(_nova.FlavorStackFixture):
ram = 256