602 lines
26 KiB
Python
602 lines
26 KiB
Python
# Copyright 2014 Mirantis, Inc.
|
|
#
|
|
# 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 io import open
|
|
import os
|
|
import shutil
|
|
import signal
|
|
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
import six
|
|
import yaml
|
|
|
|
from bareon.actions import bootloader
|
|
from bareon.actions import configdrive
|
|
from bareon.actions import copyimage
|
|
from bareon.actions import partitioning
|
|
from bareon.drivers.deploy.base import BaseDeployDriver
|
|
from bareon import errors
|
|
from bareon.utils import build as bu
|
|
from bareon.utils import fs as fu
|
|
from bareon.utils import utils
|
|
|
|
CONF = cfg.CONF
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class Manager(BaseDeployDriver):
|
|
|
|
def do_partitioning(self):
|
|
partitioning.PartitioningAction(self.driver).execute()
|
|
|
|
def do_configdrive(self):
|
|
configdrive.ConfigDriveAction(self.driver).execute()
|
|
|
|
def do_copyimage(self):
|
|
copyimage.CopyImageAction(self.driver).execute()
|
|
|
|
def do_bootloader(self):
|
|
bootloader.BootLoaderAction(self.driver).execute()
|
|
|
|
@staticmethod
|
|
def _update_metadata_with_repos(metadata, repos):
|
|
"""Update action metadata with information about repositories
|
|
|
|
:param metadata: dict contains action metadata
|
|
:param repos: list of Repo objects
|
|
:return:
|
|
"""
|
|
|
|
for repo in repos:
|
|
metadata.setdefault('repos', []).append({
|
|
'type': 'deb',
|
|
'name': repo.name,
|
|
'uri': repo.uri,
|
|
'suite': repo.suite,
|
|
'section': repo.section,
|
|
'priority': repo.priority,
|
|
'meta': repo.meta})
|
|
|
|
@staticmethod
|
|
def _set_apt_repos(chroot, repos, proxies=None, direct_repo_addrs=None):
|
|
"""Configure APT to use the specified repositories
|
|
|
|
Set apt-sources for chroot and update metadata in Manager.
|
|
|
|
:param chroot: path to OS to operate on
|
|
:param repos: list of DEBRepo objects
|
|
:param proxies: dict protocol:uri format
|
|
:param direct_repo_addrs: list of addreses which should be bypassed by
|
|
proxy
|
|
"""
|
|
LOG.debug("For set apt repositories will be used proxies: %s and"
|
|
" no_proxy: %s", proxies, direct_repo_addrs)
|
|
for repo in repos:
|
|
LOG.debug(
|
|
'Adding repository source: name={name}, uri={uri}, '
|
|
'suite={suite}, section={section}'.format(
|
|
name=repo.name,
|
|
uri=repo.uri,
|
|
suite=repo.suite,
|
|
section=repo.section))
|
|
bu.add_apt_source(name=repo.name, uri=repo.uri, suite=repo.suite,
|
|
section=repo.section, chroot=chroot)
|
|
LOG.debug(
|
|
'Adding repository preference: name={name}, '
|
|
'priority={priority}'.format(name=repo.name,
|
|
priority=repo.priority))
|
|
if repo.priority is not None:
|
|
bu.add_apt_preference(
|
|
name=repo.name, priority=repo.priority, suite=repo.suite,
|
|
section=repo.section, chroot=chroot, uri=repo.uri,
|
|
proxies=proxies, direct_repo_addrs=direct_repo_addrs)
|
|
|
|
def mount_target(self, chroot, treat_mtab=True, pseudo=True):
|
|
"""Mount a set of file systems into a chroot
|
|
|
|
:param chroot: Directory where to mount file systems
|
|
:param treat_mtab: If mtab needs to be actualized (Default: True)
|
|
:param pseudo: If pseudo file systems
|
|
need to be mounted (Default: True)
|
|
"""
|
|
LOG.debug('Mounting target file systems: %s', chroot)
|
|
# Here we are going to mount all file systems in partition scheme.
|
|
for fs in self.driver.partition_scheme.fs_sorted_by_depth():
|
|
if fs.mount == 'swap':
|
|
continue
|
|
mount = chroot + fs.mount
|
|
utils.makedirs_if_not_exists(mount)
|
|
fu.mount_fs(fs.type, str(fs.device), mount)
|
|
|
|
if pseudo:
|
|
for path in ('/sys', '/dev', '/proc'):
|
|
utils.makedirs_if_not_exists(chroot + path)
|
|
fu.mount_bind(chroot, path)
|
|
|
|
if treat_mtab:
|
|
mtab = utils.execute(
|
|
'chroot', chroot, 'grep', '-v', 'rootfs', '/proc/mounts')[0]
|
|
mtab_path = chroot + '/etc/mtab'
|
|
if os.path.islink(mtab_path):
|
|
os.remove(mtab_path)
|
|
with open(mtab_path, 'wt', encoding='utf-8') as f:
|
|
f.write(six.text_type(mtab))
|
|
|
|
def umount_target(self, chroot, pseudo=True):
|
|
LOG.debug('Umounting target file systems: %s', chroot)
|
|
if pseudo:
|
|
# umount fusectl (typically mounted at /sys/fs/fuse/connections)
|
|
for path in ('/proc', '/dev', '/sys/fs/fuse/connections', '/sys'):
|
|
fu.umount_fs(chroot + path)
|
|
for fs in self.driver.partition_scheme.fs_sorted_by_depth(
|
|
reverse=True):
|
|
if fs.mount == 'swap':
|
|
continue
|
|
fu.umount_fs(chroot + fs.mount)
|
|
|
|
def install_base_os(self, chroot):
|
|
"""Bootstrap a basic Linux system
|
|
|
|
:param chroot directory where the installed OS can be found
|
|
For now only Ubuntu is supported.
|
|
Note: the data gets written to a different location (a set of
|
|
ext4 images located in the image_build_dir directory)
|
|
Includes the following steps
|
|
1) create temporary sparse files for all images (truncate)
|
|
2) attach temporary files to loop devices (losetup)
|
|
3) create file systems on these loop devices
|
|
4) create temporary chroot directory
|
|
5) mount loop devices into chroot directory
|
|
6) install operating system (debootstrap and apt-get)
|
|
"""
|
|
LOG.info('*** Preparing image space ***')
|
|
for image in self.driver.image_scheme.images:
|
|
LOG.debug('Creating temporary sparsed file for the '
|
|
'image: %s', image.uri)
|
|
img_tmp_file = bu.create_sparse_tmp_file(
|
|
dir=CONF.image_build_dir, suffix=CONF.image_build_suffix,
|
|
size=CONF.sparse_file_size)
|
|
LOG.debug('Temporary file: %s', img_tmp_file)
|
|
|
|
# we need to remember those files
|
|
# to be able to shrink them and move in the end
|
|
image.img_tmp_file = img_tmp_file
|
|
|
|
image.target_device.name = \
|
|
bu.attach_file_to_free_loop_device(
|
|
img_tmp_file,
|
|
max_loop_devices_count=CONF.max_loop_devices_count,
|
|
loop_device_major_number=CONF.loop_device_major_number,
|
|
max_attempts=CONF.max_allowed_attempts_attach_image)
|
|
|
|
# find fs with the same loop device object
|
|
# as image.target_device
|
|
fs = self.driver.partition_scheme.fs_by_device(
|
|
image.target_device)
|
|
|
|
LOG.debug('Creating file system on the image')
|
|
fu.make_fs(
|
|
fs_type=fs.type,
|
|
fs_options=fs.options,
|
|
fs_label=fs.label,
|
|
dev=six.text_type(fs.device))
|
|
if fs.type == 'ext4':
|
|
LOG.debug('Trying to disable journaling for ext4 '
|
|
'in order to speed up the build')
|
|
utils.execute('tune2fs', '-O', '^has_journal',
|
|
six.text_type(fs.device))
|
|
|
|
# mounting all images into chroot tree
|
|
self.mount_target(chroot, treat_mtab=False, pseudo=False)
|
|
LOG.info('Installing BASE operating system into image')
|
|
# FIXME(kozhukalov): !!! we need this part to be OS agnostic
|
|
|
|
# DEBOOTSTRAP
|
|
# we use first repo as the main mirror
|
|
uri = self.driver.operating_system.repos[0].uri
|
|
suite = self.driver.operating_system.repos[0].suite
|
|
proxies = self.driver.operating_system.proxies
|
|
|
|
LOG.debug('Preventing services from being get started')
|
|
bu.suppress_services_start(chroot)
|
|
LOG.debug('Installing base operating system using debootstrap')
|
|
bu.run_debootstrap(uri=uri, suite=suite, chroot=chroot,
|
|
attempts=CONF.fetch_packages_attempts,
|
|
proxies=proxies.proxies,
|
|
direct_repo_addr=proxies.direct_repo_addr_list)
|
|
|
|
# APT-GET
|
|
LOG.debug('Configuring apt inside chroot')
|
|
LOG.debug('Setting environment variables')
|
|
bu.set_apt_get_env()
|
|
LOG.debug('Allowing unauthenticated repos')
|
|
bu.pre_apt_get(chroot,
|
|
allow_unsigned_file=CONF.allow_unsigned_file,
|
|
force_ipv4_file=CONF.force_ipv4_file,
|
|
proxies=proxies.proxies,
|
|
direct_repo_addr=proxies.direct_repo_addr_list)
|
|
|
|
# we need /proc to be mounted for apt-get success
|
|
LOG.debug('Preventing services from being get started')
|
|
bu.suppress_services_start(chroot)
|
|
utils.makedirs_if_not_exists(os.path.join(chroot, 'proc'))
|
|
|
|
# we need /proc to be mounted for apt-get success
|
|
fu.mount_bind(chroot, '/proc')
|
|
bu.populate_basic_dev(chroot)
|
|
|
|
def destroy_chroot(self, chroot):
|
|
# Umount chroot tree and remove images tmp files
|
|
if not bu.stop_chrooted_processes(chroot, signal=signal.SIGTERM):
|
|
bu.stop_chrooted_processes(chroot, signal=signal.SIGKILL)
|
|
LOG.debug('Finally: umounting procfs %s', os.path.join(chroot, 'proc'))
|
|
fu.umount_fs(os.path.join(chroot, 'proc'))
|
|
LOG.debug('Finally: umounting chroot tree %s', chroot)
|
|
self.umount_target(chroot, pseudo=False)
|
|
for image in self.driver.image_scheme.images:
|
|
if image.target_device.name:
|
|
LOG.debug('Finally: detaching loop device: %s',
|
|
image.target_device.name)
|
|
try:
|
|
bu.deattach_loop(image.target_device.name)
|
|
except errors.ProcessExecutionError as e:
|
|
LOG.warning('Error occured while trying to detach '
|
|
'loop device %s. Error message: %s',
|
|
image.target_device.name, e)
|
|
if image.img_tmp_file:
|
|
LOG.debug('Finally: removing temporary file: %s',
|
|
image.img_tmp_file)
|
|
try:
|
|
os.unlink(image.img_tmp_file)
|
|
except OSError:
|
|
LOG.debug('Finally: file %s seems does not exist '
|
|
'or can not be removed', image.img_tmp_file)
|
|
try:
|
|
os.rmdir(chroot)
|
|
except OSError:
|
|
LOG.debug('Finally: directory %s seems does not exist '
|
|
'or can not be removed', chroot)
|
|
|
|
def dump_mkbootstrap_meta(self, metadata, c_dir, bootstrap_scheme):
|
|
"""Dump mkbootstrap metadata to yaml file
|
|
|
|
:param metadata: dict with meta
|
|
:param file:
|
|
:return:
|
|
|
|
1)Process module files
|
|
2)Collect data from do_mkbootstrap metadata
|
|
3)Collect somedata from driver
|
|
4_Drop result dict 'drop_data' to yaml file
|
|
"""
|
|
meta_file = os.path.join(
|
|
c_dir, bootstrap_scheme.container.meta_file)
|
|
drop_data = {'modules': {}}
|
|
for module in bootstrap_scheme.modules:
|
|
fname = os.path.basename(module.uri)
|
|
fs_file = os.path.join(c_dir, fname)
|
|
try:
|
|
raw_size = os.path.getsize(fs_file)
|
|
except IOError as exc:
|
|
LOG.error('There was an error while getting file'
|
|
' size: {0}'.format(exc))
|
|
raise
|
|
raw_md5 = utils.calculate_md5(fs_file, raw_size)
|
|
drop_data['modules'][module.name] = {
|
|
'raw_md5': raw_md5,
|
|
'raw_size': raw_size,
|
|
'file': fname,
|
|
'uri': module.uri
|
|
}
|
|
drop_data['uuid'] = bootstrap_scheme.uuid
|
|
drop_data['extend_kopts'] = bootstrap_scheme.extend_kopts
|
|
drop_data['os'] = metadata['os']
|
|
drop_data['all_packages'] = metadata['all_packages']
|
|
drop_data['repos'] = metadata['repos']
|
|
drop_data['label'] = bootstrap_scheme.label
|
|
|
|
LOG.debug('Image metadata: %s', drop_data)
|
|
with open(meta_file, 'wt') as f:
|
|
yaml.safe_dump(drop_data, stream=f, encoding='utf-8')
|
|
|
|
def do_reboot(self):
|
|
LOG.debug('--- Rebooting node (do_reboot) ---')
|
|
utils.execute('reboot')
|
|
|
|
def do_multiboot_bootloader(self):
|
|
pass
|
|
|
|
def do_install_os(self):
|
|
pass
|
|
|
|
def do_provisioning(self):
|
|
LOG.debug('--- Provisioning (do_provisioning) ---')
|
|
self.do_partitioning()
|
|
self.do_configdrive()
|
|
self.do_copyimage()
|
|
self.do_bootloader()
|
|
LOG.debug('--- Provisioning END (do_provisioning) ---')
|
|
|
|
def do_mkbootstrap(self):
|
|
"""Building bootstrap image
|
|
|
|
Currently supports only Ubuntu-Trusty
|
|
Includes the following steps
|
|
1) Allocate and configure debootstrap.
|
|
2) Install packages
|
|
3) Run user-post script(is defined)
|
|
4) populate squashfs\init\vmlinuz files
|
|
5) create metadata.yaml and pack thats all into tar.gz
|
|
"""
|
|
LOG.info('--- Building bootstrap image (do_mkbootstrap) ---')
|
|
driver_os = self.driver.operating_system
|
|
# c_dir = output container directory, where all builded files will
|
|
# be stored, before packaging into archive
|
|
LOG.debug('Creating bootstrap container folder')
|
|
c_dir = bu.mkdtemp_smart(CONF.image_build_dir,
|
|
CONF.image_build_suffix + '_container')
|
|
try:
|
|
chroot = bu.mkdtemp_smart(
|
|
CONF.image_build_dir, CONF.image_build_suffix)
|
|
self.install_base_os(chroot)
|
|
bs_scheme = self.driver.bootstrap_scheme
|
|
# init modules, needed for bootstrap. Currently
|
|
# we support only one scheme initrd + rootfs + kernel
|
|
initrd = filter(lambda x: x.name == 'initrd',
|
|
bs_scheme.modules)[0]
|
|
rootfs = filter(lambda x: x.name == 'rootfs',
|
|
bs_scheme.modules)[0]
|
|
metadata = {}
|
|
metadata['os'] = driver_os.to_dict()
|
|
packages = driver_os.packages
|
|
metadata['packages'] = packages
|
|
|
|
self._set_apt_repos(
|
|
chroot, driver_os.repos,
|
|
proxies=driver_os.proxies.proxies,
|
|
direct_repo_addrs=driver_os.proxies.direct_repo_addr_list)
|
|
self._update_metadata_with_repos(
|
|
metadata, driver_os.repos)
|
|
LOG.debug('Installing packages using apt-get: %s',
|
|
' '.join(packages))
|
|
# disable hosts/resolv files
|
|
bu.propagate_host_resolv_conf(chroot)
|
|
if hasattr(bs_scheme, 'certs') and bs_scheme.certs:
|
|
bu.copy_update_certs(bs_scheme.certs, chroot)
|
|
bu.run_apt_get(chroot, packages=packages,
|
|
attempts=CONF.fetch_packages_attempts)
|
|
LOG.debug('Post-install OS configuration')
|
|
if hasattr(bs_scheme, 'extra_files') and bs_scheme.extra_files:
|
|
for extra in bs_scheme.extra_files:
|
|
bu.rsync_inject(extra, chroot)
|
|
if (hasattr(bs_scheme, 'root_ssh_authorized_file') and
|
|
bs_scheme.root_ssh_authorized_file):
|
|
LOG.debug('Put ssh auth file %s',
|
|
bs_scheme.root_ssh_authorized_file)
|
|
auth_file = os.path.join(chroot, 'root/.ssh/authorized_keys')
|
|
utils.makedirs_if_not_exists(os.path.dirname(
|
|
auth_file), mode=0o700)
|
|
shutil.copy(
|
|
bs_scheme.root_ssh_authorized_file,
|
|
auth_file)
|
|
os.chmod(auth_file, 0o700)
|
|
# Allow user to drop and run script inside chroot:
|
|
if (hasattr(bs_scheme, 'post_script_file') and
|
|
bs_scheme.post_script_file):
|
|
bu.run_script_in_chroot(
|
|
chroot, bs_scheme.post_script_file)
|
|
# Save runtime_uuid into bootstrap
|
|
bu.dump_runtime_uuid(bs_scheme.uuid,
|
|
os.path.join(chroot,
|
|
'etc/nailgun-agent/config.yaml'))
|
|
# NOTE(sslypushenko) Preferred names in LVM config should updated
|
|
# due to point LVM to work only with /dev/mapper folder
|
|
bu.override_lvm_config(
|
|
chroot,
|
|
{'devices': {
|
|
'preferred_names': CONF.mpath_lvm_preferred_names}},
|
|
lvm_conf_path=CONF.lvm_conf_path)
|
|
root = driver_os.get_user_by_name('root')
|
|
bu.do_post_inst(chroot,
|
|
hashed_root_password=root.hashed_password,
|
|
allow_unsigned_file=CONF.allow_unsigned_file,
|
|
force_ipv4_file=CONF.force_ipv4_file)
|
|
# restore disabled hosts/resolv files
|
|
bu.restore_resolv_conf(chroot)
|
|
metadata['all_packages'] = bu.get_installed_packages(chroot)
|
|
# We need to recompress initramfs with new compression:
|
|
bu.recompress_initramfs(
|
|
chroot,
|
|
compress=initrd.compress_format)
|
|
# Bootstrap nodes load the kernel and initramfs via the network,
|
|
# therefore remove the kernel and initramfs located in root
|
|
# filesystem to make the image smaller (and save the network
|
|
# bandwidth and the boot time)
|
|
bu.copy_kernel_initramfs(chroot, c_dir, clean=True)
|
|
LOG.debug('Making sure there are no running processes '
|
|
'inside chroot before trying to umount chroot')
|
|
if not bu.stop_chrooted_processes(chroot, signal=signal.SIGTERM):
|
|
if not bu.stop_chrooted_processes(
|
|
chroot, signal=signal.SIGKILL):
|
|
raise errors.UnexpectedProcessError(
|
|
'Stopping chrooted processes failed. '
|
|
'There are some processes running in chroot %s',
|
|
chroot)
|
|
bu.run_mksquashfs(
|
|
chroot, os.path.join(c_dir, os.path.basename(rootfs.uri)),
|
|
rootfs.compress_format)
|
|
self.dump_mkbootstrap_meta(metadata, c_dir, bs_scheme)
|
|
output = bu.save_bs_container(self.driver.output, c_dir,
|
|
bs_scheme.container.format)
|
|
LOG.info('--- Building bootstrap image END (do_mkbootstrap) ---')
|
|
return output
|
|
except Exception as exc:
|
|
LOG.error('Failed to build bootstrap image: %s', exc)
|
|
raise
|
|
finally:
|
|
LOG.info('Cleanup chroot')
|
|
self.destroy_chroot(chroot)
|
|
try:
|
|
shutil.rmtree(c_dir)
|
|
except OSError:
|
|
LOG.debug('Finally: directory %s seems does not exist '
|
|
'or can not be removed', c_dir)
|
|
# TODO(kozhukalov): Split this huge method
|
|
|
|
# into a set of smaller ones
|
|
# https://bugs.launchpad.net/fuel/+bug/1444090
|
|
def do_build_image(self):
|
|
"""Building OS images
|
|
|
|
Includes the following steps
|
|
1) create temporary sparse files for all images (truncate)
|
|
2) attach temporary files to loop devices (losetup)
|
|
3) create file systems on these loop devices
|
|
4) create temporary chroot directory
|
|
5) install operating system (install_base_os)
|
|
6) configure apt-get sources,and perform package install.
|
|
7) configure OS (clean sources.list and preferences, etc.)
|
|
8) umount loop devices
|
|
9) resize file systems on loop devices
|
|
10) shrink temporary sparse files (images)
|
|
11) containerize (gzip) temporary sparse files
|
|
12) move temporary gzipped files to their final location
|
|
"""
|
|
LOG.info('--- Building image (do_build_image) ---')
|
|
driver_os = self.driver.operating_system
|
|
# TODO(kozhukalov): Implement metadata
|
|
# as a pluggable data driver to avoid any fixed format.
|
|
metadata = {}
|
|
|
|
metadata['os'] = driver_os.to_dict()
|
|
|
|
# TODO(kozhukalov): implement this using image metadata
|
|
# we need to compare list of packages and repos
|
|
LOG.info('*** Checking if image exists ***')
|
|
if all([os.path.exists(img.uri.split('file://', 1)[1])
|
|
for img in self.driver.image_scheme.images]):
|
|
LOG.debug('All necessary images are available. '
|
|
'Nothing needs to be done.')
|
|
return
|
|
LOG.debug('At least one of the necessary images is unavailable. '
|
|
'Starting build process.')
|
|
chroot = bu.mkdtemp_smart(
|
|
CONF.image_build_dir, CONF.image_build_suffix)
|
|
try:
|
|
self.install_base_os(chroot)
|
|
packages = driver_os.packages
|
|
metadata['packages'] = packages
|
|
|
|
self._set_apt_repos(
|
|
chroot, driver_os.repos,
|
|
proxies=driver_os.proxies.proxies,
|
|
direct_repo_addrs=driver_os.proxies.direct_repo_addr_list)
|
|
self._update_metadata_with_repos(
|
|
metadata, driver_os.repos)
|
|
|
|
LOG.debug('Installing packages using apt-get: %s',
|
|
' '.join(packages))
|
|
bu.run_apt_get(chroot, packages=packages,
|
|
attempts=CONF.fetch_packages_attempts)
|
|
|
|
LOG.debug('Post-install OS configuration')
|
|
root = driver_os.get_user_by_name('root')
|
|
bu.do_post_inst(chroot,
|
|
hashed_root_password=root.hashed_password,
|
|
allow_unsigned_file=CONF.allow_unsigned_file,
|
|
force_ipv4_file=CONF.force_ipv4_file)
|
|
|
|
LOG.debug('Making sure there are no running processes '
|
|
'inside chroot before trying to umount chroot')
|
|
if not bu.stop_chrooted_processes(chroot, signal=signal.SIGTERM):
|
|
if not bu.stop_chrooted_processes(
|
|
chroot, signal=signal.SIGKILL):
|
|
raise errors.UnexpectedProcessError(
|
|
'Stopping chrooted processes failed. '
|
|
'There are some processes running in chroot %s',
|
|
chroot)
|
|
|
|
LOG.info('*** Finalizing image space ***')
|
|
fu.umount_fs(os.path.join(chroot, 'proc'))
|
|
# umounting all loop devices
|
|
self.umount_target(chroot, pseudo=False)
|
|
|
|
for image in self.driver.image_scheme.images:
|
|
# find fs with the same loop device object
|
|
# as image.target_device
|
|
fs = self.driver.partition_scheme.fs_by_device(
|
|
image.target_device)
|
|
|
|
if fs.type == 'ext4':
|
|
LOG.debug('Trying to re-enable journaling for ext4')
|
|
utils.execute('tune2fs', '-O', 'has_journal',
|
|
str(fs.device))
|
|
|
|
if image.target_device.name:
|
|
LOG.debug('Finally: detaching loop device: {0}'.format(
|
|
image.target_device.name))
|
|
try:
|
|
bu.deattach_loop(image.target_device.name)
|
|
except errors.ProcessExecutionError as e:
|
|
LOG.warning('Error occured while trying to detach '
|
|
'loop device {0}. Error message: {1}'.
|
|
format(image.target_device.name, e))
|
|
|
|
LOG.debug('Shrinking temporary image file: %s',
|
|
image.img_tmp_file)
|
|
bu.shrink_sparse_file(image.img_tmp_file)
|
|
|
|
raw_size = os.path.getsize(image.img_tmp_file)
|
|
raw_md5 = utils.calculate_md5(image.img_tmp_file, raw_size)
|
|
|
|
LOG.debug('Containerizing temporary image file: %s',
|
|
image.img_tmp_file)
|
|
img_tmp_containerized = bu.containerize(
|
|
image.img_tmp_file, image.container,
|
|
chunk_size=CONF.data_chunk_size)
|
|
img_containerized = image.uri.split('file://', 1)[1]
|
|
|
|
# NOTE(kozhukalov): implement abstract publisher
|
|
LOG.debug('Moving image file to the final location: %s',
|
|
img_containerized)
|
|
shutil.move(img_tmp_containerized, img_containerized)
|
|
|
|
container_size = os.path.getsize(img_containerized)
|
|
container_md5 = utils.calculate_md5(
|
|
img_containerized, container_size)
|
|
|
|
metadata.setdefault('images', []).append({
|
|
'raw_md5': raw_md5,
|
|
'raw_size': raw_size,
|
|
'raw_name': None,
|
|
'container_name': os.path.basename(img_containerized),
|
|
'container_md5': container_md5,
|
|
'container_size': container_size,
|
|
'container': image.container,
|
|
'format': image.format})
|
|
|
|
# NOTE(kozhukalov): implement abstract publisher
|
|
LOG.debug('Image metadata: %s', metadata)
|
|
with open(self.driver.metadata_uri.split('file://', 1)[1],
|
|
'wt', encoding='utf-8') as f:
|
|
yaml.safe_dump(metadata, stream=f)
|
|
LOG.info('--- Building image END (do_build_image) ---')
|
|
except Exception as exc:
|
|
LOG.error('Failed to build image: %s', exc)
|
|
raise
|
|
finally:
|
|
LOG.info('Cleanup chroot')
|
|
self.destroy_chroot(chroot)
|