Merge "Tobiko does not customize images during run time"
This commit is contained in:
commit
8914cf20e8
@ -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>
|
||||
|
||||
|
@ -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.
|
@ -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'
|
||||
|
@ -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) }}"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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}")
|
||||
|
@ -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"))
|
||||
]
|
||||
)]
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user