toolkit/iso/empanadas/empanadas/util/iso_utils.py

408 lines
12 KiB
Python
Raw Normal View History

"""
Builds ISO's for Rocky Linux.
Louis Abel <label AT rockylinux.org>
"""
import logging
import sys
import os
import os.path
import subprocess
import shlex
import time
import re
2022-06-17 03:31:33 +00:00
2022-06-17 18:49:22 +00:00
# This is for treeinfo
from configparser import ConfigParser
2022-06-13 14:37:50 +00:00
from productmd.common import SortedConfigParser
2022-06-17 18:49:22 +00:00
from productmd.images import Image
from productmd.extra_files import ExtraFiles
import productmd.treeinfo
# End treeinfo
2022-06-17 03:31:33 +00:00
2022-06-19 14:29:01 +00:00
# lazy person's s3 parser
import urllib
2022-06-19 17:57:05 +00:00
import json
import xmltodict
# if we can access s3
import boto3
2022-06-19 14:29:01 +00:00
2022-06-15 20:53:12 +00:00
from jinja2 import Environment, FileSystemLoader
from empanadas.common import Color, _rootdir
2022-06-17 03:31:33 +00:00
class IsoBuild:
"""
This helps us build the generic ISO's for a Rocky Linux release. In
2022-06-15 20:53:12 +00:00
particular, this is for the boot images.
2022-06-15 20:53:12 +00:00
While there is a function for building the DVD and live images, this not
the main design of this class. The other functions can be called on their
own to facilitate those particular builds.
"""
def __init__(
self,
rlvars,
config,
major,
2022-06-19 14:29:01 +00:00
arch=None,
2022-06-16 19:24:19 +00:00
rc: bool = False,
2022-06-19 14:29:01 +00:00
s3: bool = False,
2022-06-17 18:49:22 +00:00
force_unpack: bool = False,
2022-06-15 20:53:12 +00:00
isolation: str = 'auto',
compose_dir_is_here: bool = False,
image=None,
logger=None
):
self.image = image
2022-06-13 14:37:50 +00:00
self.fullname = rlvars['fullname']
2022-06-15 20:53:12 +00:00
self.distname = config['distname']
self.shortname = config['shortname']
# Relevant config items
self.major_version = major
2022-06-15 20:53:12 +00:00
self.compose_dir_is_here = compose_dir_is_here
2022-06-13 14:37:50 +00:00
self.disttag = config['dist']
self.date_stamp = config['date_stamp']
2022-06-13 14:37:50 +00:00
self.timestamp = time.time()
self.compose_root = config['compose_root']
self.compose_base = config['compose_root'] + "/" + major
2022-06-13 14:37:50 +00:00
self.iso_drop = config['compose_root'] + "/" + major + "/isos"
self.current_arch = config['arch']
2022-06-15 20:53:12 +00:00
self.required_pkgs = rlvars['iso_map']['required_pkgs']
self.mock_work_root = config['mock_work_root']
self.lorax_result_root = config['mock_work_root'] + "/" + "lorax"
self.mock_isolation = isolation
self.iso_map = rlvars['iso_map']
2022-06-16 19:24:19 +00:00
self.release_candidate = rc
2022-06-19 14:29:01 +00:00
self.s3 = s3
2022-06-17 18:49:22 +00:00
self.force_unpack = force_unpack
# Relevant major version items
2022-06-19 14:29:01 +00:00
self.arch = arch
self.arches = rlvars['allowed_arches']
2022-06-15 20:53:12 +00:00
self.release = rlvars['revision']
2022-06-17 06:25:17 +00:00
self.minor_version = rlvars['minor']
self.revision = rlvars['revision'] + "-" + rlvars['rclvl']
2022-06-16 19:24:19 +00:00
self.rclvl = rlvars['rclvl']
2022-06-15 20:53:12 +00:00
self.repos = rlvars['iso_map']['repos']
self.repo_base_url = config['repo_base_url']
self.project_id = rlvars['project_id']
self.extra_files = rlvars['extra_files']
self.staging_dir = os.path.join(
config['staging_root'],
config['category_stub'],
self.revision
)
2022-06-19 14:29:01 +00:00
# all bucket related info
self.s3_region = config['aws_region']
self.s3_bucket = config['bucket']
self.s3_bucket_url = config['bucket_url']
2022-06-15 20:53:12 +00:00
# Templates
file_loader = FileSystemLoader(f"{_rootdir}/templates")
2022-06-15 20:53:12 +00:00
self.tmplenv = Environment(loader=file_loader)
self.compose_latest_dir = os.path.join(
config['compose_root'],
major,
"latest-Rocky-{}".format(major)
)
self.compose_latest_sync = os.path.join(
self.compose_latest_dir,
"compose"
)
self.compose_log_dir = os.path.join(
self.compose_latest_dir,
"work/logs"
)
2022-06-15 20:53:12 +00:00
self.iso_work_dir = os.path.join(
self.compose_latest_dir,
"work/iso",
config['arch']
)
2022-06-19 14:29:01 +00:00
self.lorax_work_dir = os.path.join(
self.compose_latest_dir,
"work/lorax"
)
# This is temporary for now.
if logger is None:
self.log = logging.getLogger("iso")
self.log.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)
formatter = logging.Formatter(
'%(asctime)s :: %(name)s :: %(message)s',
'%Y-%m-%d %H:%M:%S'
)
handler.setFormatter(formatter)
self.log.addHandler(handler)
self.log.info('iso build init')
2022-06-15 20:53:12 +00:00
self.repolist = self.build_repo_list()
self.log.info(self.revision)
def run(self):
work_root = os.path.join(
self.compose_latest_dir,
'work'
)
sync_root = self.compose_latest_sync
log_root = os.path.join(
work_root,
"logs"
)
2022-06-15 20:53:12 +00:00
self.iso_build()
self.log.info('Compose repo directory: %s' % sync_root)
self.log.info('ISO Build Logs: /var/lib/mock/{}-{}-{}/result'.format(
self.shortname, self.major_version, self.current_arch)
)
self.log.info('ISO Build completed.')
2022-06-15 20:53:12 +00:00
def build_repo_list(self):
"""
2022-06-15 20:53:12 +00:00
Builds the repo dictionary
"""
2022-06-15 20:53:12 +00:00
repolist = []
for name in self.repos:
if not self.compose_dir_is_here:
constructed_url = '{}/{}/repo/hashed-{}/{}'.format(
self.repo_base_url,
self.project_id,
name,
self.current_arch
)
else:
constructed_url = 'file://{}/{}/{}/os'.format(
self.compose_latest_sync,
name,
self.current_arch
)
2022-06-15 20:53:12 +00:00
repodata = {
'name': name,
'url': constructed_url
}
repolist.append(repodata)
return repolist
2022-06-15 20:53:12 +00:00
def iso_build(self):
"""
2022-06-15 20:53:12 +00:00
This does the general ISO building for the current running
architecture. This generates the mock config and the general script
needed to get this part running.
"""
2022-06-15 20:53:12 +00:00
# Check for local build, build accordingly
# Check for arch specific build, build accordingly
# local AND arch cannot be used together, local supersedes. print
# warning.
self.generate_iso_scripts()
2022-06-17 05:55:07 +00:00
self.run_lorax()
2022-06-13 14:37:50 +00:00
def generate_iso_scripts(self):
"""
2022-06-17 05:55:07 +00:00
Generates the scripts needed to be ran to run lorax in mock as well as
package up the results.
2022-06-13 14:37:50 +00:00
"""
2022-06-16 19:24:19 +00:00
self.log.info('Generating ISO configuration and scripts')
2022-06-15 20:53:12 +00:00
mock_iso_template = self.tmplenv.get_template('isomock.tmpl.cfg')
mock_sh_template = self.tmplenv.get_template('isobuild.tmpl.sh')
iso_template = self.tmplenv.get_template('buildImage.tmpl.sh')
mock_iso_path = '/var/tmp/lorax-' + self.major_version + '.cfg'
mock_sh_path = '/var/tmp/isobuild.sh'
iso_template_path = '/var/tmp/buildImage.sh'
2022-06-16 19:24:19 +00:00
rclevel = ''
if self.release_candidate:
rclevel = '-' + self.rclvl
2022-06-15 20:53:12 +00:00
mock_iso_template_output = mock_iso_template.render(
arch=self.current_arch,
major=self.major_version,
fullname=self.fullname,
2022-06-17 05:55:07 +00:00
shortname=self.shortname,
2022-06-15 20:53:12 +00:00
required_pkgs=self.required_pkgs,
dist=self.disttag,
repos=self.repolist,
user_agent='{{ user_agent }}',
)
mock_sh_template_output = mock_sh_template.render(
arch=self.current_arch,
major=self.major_version,
isolation=self.mock_isolation,
builddir=self.mock_work_root,
shortname=self.shortname,
)
iso_template_output = iso_template.render(
arch=self.current_arch,
major=self.major_version,
minor=self.minor_version,
shortname=self.shortname,
repos=self.repolist,
variant=self.iso_map['variant'],
lorax=self.iso_map['lorax_removes'],
distname=self.distname,
revision=self.release,
2022-06-16 19:24:19 +00:00
rc=rclevel,
2022-06-17 05:55:07 +00:00
builddir=self.mock_work_root,
lorax_work_root=self.lorax_result_root,
2022-06-15 20:53:12 +00:00
)
mock_iso_entry = open(mock_iso_path, "w+")
mock_iso_entry.write(mock_iso_template_output)
mock_iso_entry.close()
mock_sh_entry = open(mock_sh_path, "w+")
mock_sh_entry.write(mock_sh_template_output)
mock_sh_entry.close()
iso_template_entry = open(iso_template_path, "w+")
iso_template_entry.write(iso_template_output)
iso_template_entry.close()
2022-06-13 14:37:50 +00:00
2022-06-17 05:55:07 +00:00
os.chmod(mock_sh_path, 0o755)
os.chmod(iso_template_path, 0o755)
def run_lorax(self):
"""
This actually runs lorax on this system. It will call the right scripts
to do so.
"""
lorax_cmd = '/bin/bash /var/tmp/isobuild.sh'
self.log.info('Starting lorax...')
p = subprocess.call(shlex.split(lorax_cmd))
if p != 0:
2022-06-17 05:55:07 +00:00
self.log.error('An error occured during execution.')
self.log.error('See the logs for more information.')
raise SystemExit()
2022-06-17 18:49:22 +00:00
def run_image_build(self, arch):
"""
Builds the other images
"""
print()
2022-06-19 14:29:01 +00:00
def run_pull_lorax_artifacts(self):
"""
Pulls the required artifacts and unacps it to work/lorax/$arch
"""
self.log.info('Determining the latest pull...')
print()
def _download_artifacts(self, force_unpack, arch=None):
"""
Download the requested artifact(s)
"""
print()
def _unpack_artifacts(self, force_unpack, arch=None):
"""
Unpack the requested artifacts(s)
"""
print()
def run_boot_sync(self):
2022-06-17 18:49:22 +00:00
"""
This unpacks into BaseOS/$arch/os, assuming there's no data actually
there. There should be checks.
2022-06-19 14:29:01 +00:00
1. Sync from work/lorax/$arch to work/lorax/$arch/dvd
2. Sync from work/lorax/$arch to work/lorax/$arch/minimal
3. Sync from work/lorax/$arch to BaseOS/$arch/os
2022-06-17 18:49:22 +00:00
4. Modify (3) .treeinfo
5. Modify (1) .treeinfo, keep out boot.iso checksum
6. Create a .treeinfo for AppStream
"""
2022-06-19 14:29:01 +00:00
unpack_single_arch = False
arches_to_unpack = self.arches
if self.arch:
unpack_single_arch = True
arches_to_unpack = [self.arch]
2022-06-17 18:49:22 +00:00
2022-06-19 14:29:01 +00:00
self.sync_boot(force_unpack=self.force_unpack, arch=self.arch)
self.treeinfo_write(arch=self.arch)
def sync_boot(self, force_unpack, arch):
2022-06-17 18:49:22 +00:00
"""
2022-06-19 14:29:01 +00:00
Syncs whatever
2022-06-17 18:49:22 +00:00
"""
self.log.info('Syncing lorax to dvd directory...')
2022-06-19 14:29:01 +00:00
# checks here, report that it already exists
2022-06-17 18:49:22 +00:00
self.log.info('Syncing lorax to %s directory...' % self.iso_map['variant'])
2022-06-19 14:29:01 +00:00
# checks here, report that it already exists
2022-06-17 05:55:07 +00:00
2022-06-19 14:29:01 +00:00
def treeinfo_write(self, arch):
2022-06-13 14:37:50 +00:00
"""
Ensure treeinfo is written correctly
"""
2022-06-19 14:29:01 +00:00
self.log.info('Starting treeinfo work...')
def _treeinfo_from_lorax(self, arch, force_unpack):
"""
Fixes lorax treeinfo
"""
self.log.info('Fixing up lorax treeinfo...')
2022-06-13 14:37:50 +00:00
def discinfo_write(self):
"""
Ensure discinfo is written correctly
"""
#with open(file_path, "w") as f:
# f.write("%s\n" % self.timestamp)
# f.write("%s\n" % self.fullname)
# f.write("%s\n" % self.arch)
# if disc_numbers:
# f.write("%s\n" % ",".join([str(i) for i in disc_numbers]))
print()
def write_media_repo(self):
"""
Ensure media.repo exists
"""
data = [
"[InstallMedia]",
"name=%s" % self.fullname,
"mediaid=%s" % self.timestamp,
"metadata_expire=-1",
"gpgcheck=0",
"cost=500",
"",
]
2022-06-15 20:53:12 +00:00
def build_extra_iso(self):
"""
Builds DVD images based on the data created from the initial lorax on
each arch. This should NOT be called during the usual run() section.
"""
print()
2022-06-13 14:37:50 +00:00
def generate_graft_points(self):
"""
2022-06-15 20:53:12 +00:00
Get a list of packages for an extras ISO. This should NOT be called
during the usual run() section.
2022-06-13 14:37:50 +00:00
"""
print()
class LiveBuild:
"""
This helps us build the live images for Rocky Linux.
"""