VMware-Photon-OS/installer/ostreeinstaller.py

219 lines
11 KiB
Python

#!/usr/bin/python2
#
# Copyright (C) 2015 vmware inc.
#
# Author: Touseef Liaqat <tliaqat@vmware.com>
import subprocess
import os
import re
import glob
import modules.commons
from installer import Installer
from commandutils import CommandUtils
class OstreeInstaller(object):
def __init__(self, installer):
self.repo_config = {}
self.repo_read_conf()
# simulate inheritance
self.install_config = installer.install_config
self.logger = installer.logger
if self.install_config['ui']:
self.progress_bar = installer.progress_bar
self.photon_root = installer.photon_root
self.installer_path = installer.installer_path
self._create_fstab = installer._create_fstab
self.exit_gracefully = installer.exit_gracefully
self._get_uuid = installer._get_uuid
self._get_partuuid = installer._get_partuuid
self.cmd = CommandUtils(self.logger)
def get_ostree_repo_url(self):
self.default_repo = self.install_config['ostree'].get('default_repo', False);
if not self.default_repo:
self.ostree_repo_url = self.install_config['ostree']['repo_url']
self.ostree_ref = self.install_config['ostree']['repo_ref']
def repo_read_conf(self):
with open("ostree-release-repo.conf") as repo_conf:
for line in repo_conf:
name, value = line.partition("=")[::2]
self.repo_config[name] = value.strip(' \n\t\r')
def pull_repo(self, repo_url, repo_ref):
self.run([['ostree', 'remote', 'add', '--repo={}/ostree/repo'.format(self.photon_root), '--set=gpg-verify=false', 'photon', '{}'.format(repo_url)]], "Adding OSTree remote")
if self.default_repo:
self.run([['ostree', 'pull-local', '--repo={}/ostree/repo'.format(self.photon_root), '{}'.format(self.local_repo_path)]], "Pulling OSTree repo")
cmd = []
cmd.append(['mv', '{}/ostree/repo/refs/heads'.format(self.photon_root), '{}/ostree/repo/refs/remotes/photon'.format(self.photon_root)])
cmd.append(['mkdir', '-p', '{}/ostree/repo/refs/heads'.format(self.photon_root)])
self.run(cmd)
else:
self.run([['ostree', 'pull', '--repo={}/ostree/repo'.format(self.photon_root), 'photon', '{}'.format(repo_ref)]], "Pulling OSTree remote repo")
def deploy_ostree(self, repo_url, repo_ref):
self.run([['ostree', 'admin', '--sysroot={}'.format(self.photon_root), 'init-fs', '{}'.format(self.photon_root)]], "Initializing OSTree filesystem")
self.pull_repo(repo_url, repo_ref)
self.run([['ostree', 'admin', '--sysroot={}'.format(self.photon_root), 'os-init', 'photon']], "OSTree OS Initializing")
self.run([['ostree', 'admin', '--sysroot={}'.format(self.photon_root), 'deploy', '--os=photon', 'photon:{}'.format(repo_ref)]], "Deploying")
def do_systemd_tmpfiles_commands(self, commit_number):
prefixes = ["/var/home",
"/var/roothome",
"/var/lib/rpm",
"/var/opt",
"/var/srv",
"/var/userlocal",
"/var/mnt",
"/var/spool/mail"]
for prefix in prefixes:
command = ['systemd-tmpfiles', '--create', '--boot', '--root={}/ostree/deploy/photon/deploy/{}.0'.format(self.photon_root, commit_number), '--prefix={}'.format(prefix)]
self.run([command], "systemd-tmpfiles command done")
def create_symlink_directory(self, deployment):
command = []
command.append(['mkdir', '-p', '{}/sysroot/tmp'.format(deployment)])
command.append(['mkdir', '-p', '{}/sysroot/ostree'.format(deployment)])
command.append(['mkdir', '-p', '{}/run/media'.format(deployment)])
self.run(command, "symlink directory created")
def mount_devices_in_deployment(self, commit_number):
command = []
command.append(['mount', '-t', 'bind', '-o', 'bind,defaults', '/dev', '{}/ostree/deploy/photon/deploy/{}.0/dev'.format(self.photon_root, commit_number)])
command.append(['mount', '-t', 'devpts', '-o', 'gid=5,mode=620', 'devpts', '{}/ostree/deploy/photon/deploy/{}.0/dev/pts'.format(self.photon_root, commit_number)])
command.append(['mount', '-t', 'tmpfs', '-o', 'defaults', 'tmpfs', '{}/ostree/deploy/photon/deploy/{}.0/dev/shm'.format(self.photon_root, commit_number)])
command.append(['mount', '-t', 'proc', '-o', 'defaults', 'proc', '{}/ostree/deploy/photon/deploy/{}.0/proc'.format(self.photon_root, commit_number)])
command.append(['mount', '-t', 'bind', '-o', 'bind,defaults', '/run', '{}/ostree/deploy/photon/deploy/{}.0/run'.format(self.photon_root, commit_number)])
command.append(['mount', '-t', 'sysfs', '-o', 'defaults', 'sysfs', '{}/ostree/deploy/photon/deploy/{}.0/sys'.format(self.photon_root, commit_number)])
self.run(command, "mounting done")
def get_commit_number(self, ref):
fileName = os.path.join(self.photon_root, "ostree/repo/refs/remotes/photon/{}".format(ref))
commit_number = None
with open (fileName, "r") as file:
commit_number = file.read().replace('\n', '')
return commit_number
def install(self):
"""
Ostree Installer Main
"""
sysroot_boot = self.photon_root
sysroot_bootefi = self.photon_root
for partition in self.install_config['partitions']:
if partition.get('mountpoint', '') == '/':
root_partition = partition
if partition.get('mountpoint', '') == '/boot':
boot_partition = partition
sysroot_boot = sysroot_boot + partition['mountpoint']
if partition.get('mountpoint', '') == '/boot/efi' and partition['filesystem'] == 'vfat':
sysroot_bootefi = sysroot_bootefi + partition['mountpoint']
sysroot_ostree = os.path.join(self.photon_root, "ostree")
loader0 = os.path.join(sysroot_boot, "loader.0")
loader1 = os.path.join(sysroot_boot, "loader.1")
boot0 = os.path.join(sysroot_ostree, "boot.0")
boot1 = os.path.join(sysroot_ostree, "boot.1")
boot01 = os.path.join(sysroot_ostree, "boot.0.1")
boot11 = os.path.join(sysroot_ostree, "boot.1.1")
self.get_ostree_repo_url()
bootmode = self.install_config['bootmode']
if bootmode == 'dualboot' or bootmode == 'efi':
mount_efi = []
mount_efi.append(['mkdir', '-p', '{}/EFI/Boot'.format(sysroot_bootefi)])
mount_efi.append(['mkdir', '-p', '{}/boot/grub2/fonts'.format(sysroot_bootefi)])
mount_efi.append(['cp', '-r', '{}/boot/.'.format(self.installer_path), '{}/boot/grub2/fonts/'.format(sysroot_bootefi)])
self.run(mount_efi, "Mount and setup efi partition")
if self.default_repo:
self.run([['mkdir', '-p', '{}/repo'.format(self.photon_root)]])
if self.install_config['ui']:
self.progress_bar.show_loading("Unpacking local OSTree repo")
self.run([['tar', '--warning=none', '-xf', '/mnt/media/ostree-repo.tar.gz', '-C' '{}/repo'.format(self.photon_root)]])
self.local_repo_path = "{}/repo".format(self.photon_root)
self.ostree_repo_url = self.repo_config['OSTREEREPOURL']
self.ostree_ref = self.repo_config['OSTREEREFS']
if self.install_config['ui']:
self.progress_bar.update_loading_message("Unpacking done")
self.deploy_ostree(self.ostree_repo_url, self.ostree_ref)
commit_number = self.get_commit_number(self.ostree_ref)
self.do_systemd_tmpfiles_commands(commit_number)
self.mount_devices_in_deployment(commit_number)
deployment = os.path.join(self.photon_root, "ostree/deploy/photon/deploy/" + commit_number + ".0/")
self.create_symlink_directory(deployment)
deployment_boot = os.path.join(deployment, "boot")
deployment_sysroot = os.path.join(deployment, "sysroot")
if os.path.exists(loader1):
cmd = []
cmd.append(['mv', '{}'.format(loader1), '{}'.format(loader0)])
cmd.append(['mv', '{}'.format(boot1), '{}'.format(boot0)])
cmd.append(['mv', '{}'.format(boot11), '{}'.format(boot01)])
self.run(cmd)
mount_bind = []
mount_bind.append(['mount', '--bind', '{}'.format(sysroot_boot), '{}'.format(deployment_boot)])
mount_bind.append(['mount', '--bind', '{}'.format(self.photon_root), '{}'.format(deployment_sysroot)])
self.run(mount_bind)
if bootmode == 'dualboot' or bootmode == 'bios':
self.run([['chroot', '{}'.format(deployment), 'bash', '-c', 'grub2-install --target=i386-pc --force --boot-directory=/boot {};'.format(self.install_config['disk'])]], "Generating Grub binaries for BIOS mode")
if bootmode == 'dualboot' or bootmode == 'efi':
self.run([['cp', '-r', '{}/EFI_x86_64/BOOT/.'.format(self.installer_path), '{}/EFI/Boot/'.format(sysroot_bootefi)]], "Generating grub.cfg for efi boot")
with open(os.path.join(sysroot_bootefi, 'boot/grub2/grub.cfg'), "w") as grub_cfg:
grub_cfg.write("search -n -u {} -s\n".format(self._get_uuid(boot_partition['path'])))
grub_cfg.write("configfile /grub2/grub.cfg\n")
self.run([['chroot', '{}'.format(deployment), 'bash', '-c', 'mkdir -p /boot/grub2;']])
self.run([['chroot', '{}'.format(deployment), 'bash', '-c', 'grub2-mkconfig -o /boot/grub2/grub.cfg;']])
if os.path.exists(loader0):
cmd = []
cmd.append(['mv', '{}'.format(loader0), '{}'.format(loader1)])
cmd.append(['mv', '{}'.format(boot0), '{}'.format(boot1)])
cmd.append(['mv', '{}'.format(boot01), '{}'.format(boot11)])
self.run(cmd)
partuuid=self._get_partuuid(root_partition['path'])
if partuuid == "" :
self.run([['chroot', '{}'.format(deployment), 'bash', '-c', "ostree admin instutil set-kargs '$photon_cmdline' '$systemd_cmdline' root={};".format(root_partition['path'])]], "Add ostree menu entry in grub.cfg")
else:
self.run([['chroot', '{}'.format(deployment), 'bash', '-c', "ostree admin instutil set-kargs '$photon_cmdline' '$systemd_cmdline' root=PARTUUID={};".format(partuuid)]], "Add ostree menu entry in grub.cfg")
sysroot_grub2_grub_cfg = os.path.join(self.photon_root, "boot/grub2/grub.cfg")
self.run([['ln', '-sf', '../loader/grub.cfg', '{}'.format(sysroot_grub2_grub_cfg)]])
if os.path.exists(loader1):
cmd = []
cmd.append(['mv', '{}'.format(loader1), '{}'.format(loader0)])
cmd.append(['mv', '{}'.format(boot1), '{}'.format(boot0)])
cmd.append(['mv', '{}'.format(boot11), '{}'.format(boot01)])
self.run(cmd)
deployment_fstab = os.path.join(deployment, "etc/fstab")
self._create_fstab(deployment_fstab)
self.run([['mount', '--bind', '{}'.format(deployment), '{}'.format(self.photon_root)]], "Bind deployment to photon root")
def run(self, commands, comment = None):
if comment != None:
self.logger.info("Installer: {} ".format(comment))
if self.install_config['ui']:
self.progress_bar.update_loading_message(comment)
for command in commands:
retval = self.cmd.run(command)
if retval != 0 and "systemd-tmpfiles" not in command:
self.logger.error("Installer: failed in {} with error code {}".format(command, retval))
self.exit_gracefully()
return retval