diff --git a/doc/mockrun.md b/doc/mockrun.md new file mode 100644 index 00000000..25de5e19 --- /dev/null +++ b/doc/mockrun.md @@ -0,0 +1,37 @@ +The `mockrun-installer` script allows to quickly check some of the TUI +changes locally, providing a big speedup of dev cycle as compared to +remastering an ISO image and testboot it. + +The execution environment has to be as close a possible as the one +used as dom0 on the install ISO, which in turn is very close to a +XCP-ng dom0 OS. + +The xcp-ng-build-env docker container is the best choice for a runtime +environment, as it has access to all required dependencies, which you +will still have to install. + +For XCP-ng 8.3: +``` +user $ xcp-ng-dev container shell 8.3 -d . +builder $ sudo yum install -y xcp-python-libs newt-python python2-simplejson python-six pyOpenSSL +builder $ cd /external/host-installer/ +builder $ ./mockrun-installer +``` + +## details of operation + +This script fakes many calls to lower-level python libraries, +currently in a hardcoded fashion, so it possibly has to be hand-edited +to check other use cases. + +It will attempt to run system commands, NEVER try to run it as root on +a real system. + +No specific way to interrupt the TUI is provided, but causing it to +"abort and reboot" will cause it to exit. As a last resort going to +the final dialog for confirmation of install (F12 is your friend) and +proceeeding will attempt execution of unstubbed commands +(e.g. `uuidgen`) and abort. Or you can kill the process externally +and `reset` your tty afterwards. + +`installer.log` is generated in current directory. diff --git a/mockrun-data/emptyfile b/mockrun-data/emptyfile new file mode 100644 index 00000000..e69de29b diff --git a/mockrun-data/etc/xensource/xapi-ssl.pem b/mockrun-data/etc/xensource/xapi-ssl.pem new file mode 100644 index 00000000..6fc525d9 --- /dev/null +++ b/mockrun-data/etc/xensource/xapi-ssl.pem @@ -0,0 +1,46 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA3rNjU4jJPu9hdovSXAOAIEwQvXmNTdWGLOYVPzy/u5ieWBLT +zL6zndElFdjmyG/srxRP8Q7uicEVCpX2Tj319dDUW877HU1hh84z1CaQpIPCIJcd +YyMnsCc648LkmqGGluhZiPdkbBq0H2D4T8DkyYwKHoo1V63RVvKdbgiRBAkJiIwi +zkj78feNl3hOBcWJXH3Du0oiwjiWLxRuV1v4n6V6XJV7nklZaxlWvlmtw9MonckM +LQgxzmWhEyRt82QP9S0IZImT0+KM6nZsgPwraic1fReTGDLCyi7sl2xOYJePEqiv +OK1uMHzWY2sZly3EZh4Zl8cVM1A0go3M72uYrQIDAQABAoIBAQC5wNcwbf+uRuz8 +fFPR+/l69vdfk2uRaHCqMp+2R22FhB9PRyTyun4HPNO6DVkyBmlc/4xOfS3qKOrs +EXmIeZYWm0EF8vbx0gFvzUbdxe0Py/a4SFNyxXftLkuofn8/m0svxE4qOG/C2+Mx +6Sg1Hae3lN876fvkTk82PtehV/PdcD0nj0y52QuBWodYnGZBHkazd4PZDVG6zST5 +TV2fxhWZaA+tKTi5vY9syVKnUS1gUmQ5WXe1hRRNwdhy9gUyraT7xJkjAhFz8I43 +eH1DRFR7U8lwVBVOn+oxUG76sC/5eWZoBFkmpG5a0zrpiUDyuh1eOwLqh7r83Hmz +L5MgIj7hAoGBAP6XRwj7M3R/jUOmF95m/ywEiV90ML7w9+gJcH05WjfVQUMzr6dL +nGg2sJ9SgYQDSMwsKEmjT5FhxTXdEySZBh5Wr3BXWWsUkkL115qQhlIunX7e/F7W +l57kbHOPq2cc3bE2/JDqgO7U98EMYzQUldO4nhLE7LaG0mV8mjbRjN1jAoGBAN/u +7RzHZ8cW0mbGwtzOONv3yivtZuR48815qfqSZNxnpOAagcUF9GuhUSdKG5Qb4b8I +7GW0LzITbjlI9Ql4s1QlZUGLzj3Zfg+ybOTqpVF1y44PmZ94F+7g4HsmtCcY+MV2 +KoVIhyPVWXJrNIGIwszIz3wtIcCy5ofg2t4Di1avAoGARbT15iehdwxViWg4sNRS +maLEWjZeAmnH9uhevGqvoQnqzJOiTJQPRxlyUq0seRDNJdoRTdhBOPUkIhNd4m9j +lXr2njPMb906DstxNQjNPkM1LD+xqeXnrcGtUBbOP3VP3e3ISYVrt/jnvRu/LctV +Gps57LmudmuDv/XV45yMjTECgYBFec5tCFzlQupiYrZgZUb00CsUd+Jdbj1N+bpH ++JV8YFeiA/dRFmjf37va8b4etIvUhkM5xq6B3c9rzHvhxWX17Gg+7s49GDZpTlVE +rJw/6skNuT9TX6IlRfW/n6FzI+dC9Ed69Kag8nd+7U3sBm9Rs9fT0fjP3XFypMui +5/yaFwKBgQC/pnhmuKyjft6CogaOFGwH6oeO4IXJgch9lB2oOEHyuxdGr5Q56eN5 +dsZ7umCFwWCn69vPblQTQLEzsk+uqnsB3q3PTYNScqmpWFM7+aJqE6twFIqvnvnv +qDIHUsvdkW2u8G8lPu3HRxiNyR4aKpXJFC/WWnFWmb26pGVPc7wzmg== +-----END RSA PRIVATE KEY----- + +-----BEGIN CERTIFICATE----- +MIIC2zCCAcOgAwIBAgIJAKgVvRU/OYMsMA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV +BAMMDTE3Mi4xNi4yMTAuMTEwHhcNMjUwMzI3MTAxMDQ5WhcNMzUwMzI1MTAxMDQ5 +WjAYMRYwFAYDVQQDDA0xNzIuMTYuMjEwLjExMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA3rNjU4jJPu9hdovSXAOAIEwQvXmNTdWGLOYVPzy/u5ieWBLT +zL6zndElFdjmyG/srxRP8Q7uicEVCpX2Tj319dDUW877HU1hh84z1CaQpIPCIJcd +YyMnsCc648LkmqGGluhZiPdkbBq0H2D4T8DkyYwKHoo1V63RVvKdbgiRBAkJiIwi +zkj78feNl3hOBcWJXH3Du0oiwjiWLxRuV1v4n6V6XJV7nklZaxlWvlmtw9MonckM +LQgxzmWhEyRt82QP9S0IZImT0+KM6nZsgPwraic1fReTGDLCyi7sl2xOYJePEqiv +OK1uMHzWY2sZly3EZh4Zl8cVM1A0go3M72uYrQIDAQABoygwJjAkBgNVHREEHTAb +ggdSNjIwLUwxggdSNjIwLUwxggdSNjIwLUwxMA0GCSqGSIb3DQEBCwUAA4IBAQAb +qDj4k/Vq4yomhkC2SYBAV0Xmrx7uhhgDcy5zGXjWwRHXpUVHRMxzjJvR/0G2If0J +Q1aNgNOwhM+IqfRVTZBJDQh/ddj792oTKTfY5tsnuPlhJ/Gm7Ba1v3ufkyGV02qe +0CB2EZTPvl0u410g4C0/GOJLkKJSB1uD9LWFyQ4KYX3S47HGX2Aiu+NiXyMf36Z6 +5ZUp0HQnIYeQ20T0rqjXmZ+Gu516l2rnT+MtIVZeN3J6/5S/hNtHebtGDqOY5/hZ +SbbtyIMhgJvil+z2XyNrp+29XD//ejhoXXExD+3UQvg5i4ONDolls5KRomIVygj2 +1zz9g1wH6FEebsIeIdka +-----END CERTIFICATE----- diff --git a/mockrun-data/ifcfg-eth98 b/mockrun-data/ifcfg-eth98 new file mode 100644 index 00000000..5bf3d2f6 --- /dev/null +++ b/mockrun-data/ifcfg-eth98 @@ -0,0 +1,4 @@ +DEVICE=eth98 +ONBOOT=yes +BOOTPROTO=dhcp +PERSISTENT_DHCLIENT=1 diff --git a/mockrun-data/ifcfg-eth99 b/mockrun-data/ifcfg-eth99 new file mode 100644 index 00000000..0d5f1a81 --- /dev/null +++ b/mockrun-data/ifcfg-eth99 @@ -0,0 +1,2 @@ +DEVICE=eth99 +ONBOOT=yes diff --git a/mockrun-installer b/mockrun-installer new file mode 100755 index 00000000..7addb5ad --- /dev/null +++ b/mockrun-installer @@ -0,0 +1,622 @@ +#!/usr/bin/env python2 + +# Runs in XCP-ng 8.3 build env container +# Requires packages: python2-newt xcp-python-libs python2-simplejson pyOpenSSL python-six + +import os +import sys +import tempfile +from types import ModuleType + +MOUNTPOINT_PRIMARY = MOUNTPOINT_BACKUP = MOUNTPOINT_ROOT = MOUNTPOINT_STATE = None + +### install stubs before imports get a chance to use them + +# python-xen is not easily available everywhere + +mocked_xen = ModuleType("xen") +sys.modules["xen"] = mocked_xen +mocked_xen.lowlevel = ModuleType("lowlevel") +sys.modules["xen.lowlevel"] = mocked_xen.lowlevel +mocked_xen.lowlevel.xc = ModuleType("xc") +sys.modules["xen.lowlevel.xc"] = mocked_xen.lowlevel.xc + +class mocked_xc(object): + def physinfo(self): + return dict( + total_memory=1024*1024, # KB + nr_cpus=1, + ) + def xeninfo(self): + return None + +mocked_xen.lowlevel.xc.xc = mocked_xc + +# the (in)famous branding + +mocked_branding = ModuleType("branding") +mocked_branding.PRODUCT_NAME = 'xenenterprise' +mocked_branding.PRODUCT_BRAND = 'Mocked XCP-ng' +mocked_branding.BRAND_SERVER = 'Mocked XCP-ng' +mocked_branding.PLATFORM_NAME = "XCP" +mocked_branding.COPYRIGHT_YEARS = "1970-2023" +mocked_branding.COMPANY_NAME_LEGAL = 'Guys in a Garage' +mocked_branding.BRAND_GUEST = 'Virtual Machine' +mocked_branding.BRAND_GUESTS = 'Virtual Machines' +mocked_branding.BRAND_CONSOLE = "Admin Console" + +# mocked_branding.PRODUCT_VERSION_TEXT = "8.2" +# mocked_branding.PLATFORM_VERSION = '3.2.1' + +mocked_branding.PRODUCT_VERSION_TEXT = "8.3" +mocked_branding.PLATFORM_VERSION = '3.4.1' + +# for installer proper +sys.modules["version"] = mocked_branding +# for xcp.* +sys.modules["branding"] = mocked_branding + +# + +import constants + +constants.EULA_PATH = "/etc/motd" +constants.UEFI_INSTALLER = True + +# + +import util + +# mock running individual commands, for those functions where other +# side-effects besides running the command makes it hard to properly +# stub them, or when stubbing once here avoids stubbing many other +# places +def mocked_runCmd2(command, with_stdout=False, with_stderr=False, inputtext=None): + if command[0] in ("lsmod", "/sbin/udevadm", "ifdown", "ifup"): + assert not with_stdout and not with_stderr + logger.log("Mocking {}".format(command)) + return 0 + + if command[0:2] in (['sgdisk', '--zap-all'], + ['mdadm', '--zero-superblock']): + assert not with_stdout and not with_stderr + logger.log("Mocking {}".format(command)) + return 0 + + if command[0:2] == ['mdadm', '--stop']: + # runned because we unconditionally fake presence of md device + assert not with_stdout and not with_stderr + logger.log("Mocking {}".format(command)) + return 0 + + if command[0:2] == ['mdadm', '--create']: + assert not with_stdout and not with_stderr + logger.log("Mocking {}".format(command)) + for arg in reversed(command): + if arg == "missing": # last arg, support for degraded raids + continue + if arg.startswith("--"): # seen all disks already + break + assert arg.startswith("/dev"), "%r does not start with /dev/" % (arg,) + MOCKED_DISKLIST.remove(os.path.basename(arg)) + MOCKED_DISKLIST.append("md127") + return 0 + + if command[0:2] == ['mdadm', '--manage']: + assert not with_stdout and not with_stderr + logger.log("Mocking {}".format(command)) + for arg in reversed(command): + if arg == "--run": # last arg + continue + if arg.startswith("--"): # seen all disks already + break + assert arg.startswith("/dev"), "%r does not start with /dev/" % (arg,) + MOCKED_DISKLIST.remove(os.path.basename(arg)) + return 0 + + if command[0] == '/bin/mount': + if command[1:3] == ['-o', 'ro']: + device, mountpoint = command[3:5] + global MOUNTPOINT_PRIMARY + if mountpoint.startswith('/tmp/primary-'): + MOUNTPOINT_PRIMARY = mountpoint + logger.log("faking mount of /dev/sda1 on %s" % (mountpoint,)) + return (0, "mocked stdout", "mocked stderr") + + device, mountpoint = command[1:3] + if device == '/dev/sda1': + global MOUNTPOINT_ROOT, MOUNTPOINT_STATE + if mountpoint.startswith('/tmp/mocked-root-'): + MOUNTPOINT_ROOT = mountpoint + logger.log("faking mount of %s on %s" % (device, mountpoint)) + return (0, "mocked stdout", "mocked stderr") + if mountpoint.startswith('/tmp/mocked-state-'): + MOUNTPOINT_STATE = mountpoint + logger.log("faking mount of %s on %s" % (device, mountpoint)) + return (0, "mocked stdout", "mocked stderr") + if mountpoint == '/tmp/root': + #MOUNTPOINT_ROOT = mountpoint + logger.log("faking mount of %s on %s" % (device, mountpoint)) + return (0, "mocked stdout", "mocked stderr") + + if device == '/dev/sda2': + global MOUNTPOINT_BACKUP + if mountpoint.startswith('/tmp/backup-'): + MOUNTPOINT_BACKUP = mountpoint + logger.log("faking mount of %s on %s" % (device, mountpoint)) + return (0, "mocked stdout", "mocked stderr") + + raise RuntimeError("unhandled mount command %s", command) + + if command[0:2] == ['/bin/umount', '-d']: + if command[2] == MOUNTPOINT_ROOT: + logger.log("faking umount of %s" % (command[2],)) + return (0, "mocked stdout", "mocked stderr") + if command[2] == MOUNTPOINT_STATE: + logger.log("faking umount of %s" % (command[2],)) + return (0, "mocked stdout", "mocked stderr") + if command[2] == MOUNTPOINT_PRIMARY: + logger.log("faking umount of %s" % (command[2],)) + return (0, "mocked stdout", "mocked stderr") + if command[2] == MOUNTPOINT_BACKUP: + logger.log("faking umount of %s" % (command[2],)) + return (0, "mocked stdout", "mocked stderr") + + if command[0:2] == ['wipefs', '-a']: + logger.log("faking wipefs of %s" % (command[2],)) + return (0, "mocked stderr") + + if command[0] in ['mkfs.ext3', 'mkfs.vfat']: + logger.log("faking mkfs of %s" % (command[1],)) + return (0, "mocked stderr") + + if command[0:2] == ['sgdisk', '--mbrtogpt']: + logger.log("faking %s" % (command,)) + if not with_stdout and not with_stderr: + return 0 + if with_stdout and with_stderr: + return (0, "mocked stdout", "mocked stderr") + assert False, "unhandled with_stdout=%s with_stderr=%s" % (with_stdout, with_stderr) + + if command[0:2] == ['sgdisk', '--set-alignment=1']: + logger.log("faking %s" % (command,)) + assert with_stdout and with_stderr + return (0, "mocked stdout", "mocked stderr") + + if command[0:3] == ['sfdisk', '--no-reread', '-A1']: + logger.log("faking %s" % (command,)) + assert with_stdout and with_stderr + return (0, "mocked stdout", "mocked stderr") + + if command == ['uuidgen']: + return (0, "40987587-a9d0-44ae-a7bc-d9fd21850b7c") + + if command == ['repoquery', '-c', '/root/yum.conf', '--qf', '%{evr}', 'linstor-satellite']: + logger.log("faking %s" % (command,)) + # return (0, "") # standard ISO without linstor + # return (0, "10:1.21.0-1.el7\n0:1.29.0-1.el7_9\n") + # return (0, "0:1.21.0-1.el7\n") + return (0, "0:1.29.0-2.el7_9\n") + + if command[0:2] == ['blockdev', '--getsize64']: + logger.log("faking %s" % (command,)) + return (0, "107374182400\n") + + raise RuntimeError("mockrun runCmd2 command not specified: {!r}{}{}".format( + command, " with_stdout" if with_stdout else "", " with_stderr" if with_stderr else "")) + +util.runCmd2 = mocked_runCmd2 + +# + +import xcp.net.biosdevname +import disktools + +def mocked_all_devices_all_names(): + devs = [ + {"Kernel name": "eth99", + "Assigned MAC": "00:11:22:33:44:55", + }, + {"Kernel name": "eth98", + "Assigned MAC": "00:11:22:33:44:66", + }, + ] + return {dev["Kernel name"]: dev + for dev in devs} + +def mocked_lvmtool_readInfo(self, info): + return [] + +def mocked_getMpathNodes(): + return [] + +def mocked_gpt_readDiskDetails(self): + self.sectorSize = 512 + self.sectorExtent = 1024**3 + self.sectorFirstUsable = 34 + self.sectorLastUsable = self.sectorExtent - 34 + +def mocked_probePartitioningScheme(device): + return constants.PARTITION_GPT + +def mocked_gpt_partitionTable(self): + # logger.log('Stubbing no partition table on disk %s.' % self.device) + # return {} + + return { + 1: dict(start=0, size=18874368, partlabel="root foo", + id=disktools.GPTPartitionTool.ID_LINUX), + 2: dict(start=18874368, size=18874368, partlabel="backup", + id=disktools.GPTPartitionTool.ID_LINUX), + # 3: local SR + 4: dict(start=0, size=524288, partlabel="boot", + id=disktools.GPTPartitionTool.ID_EFI_BOOT), + 5: dict(start=0, size=4194304, partlabel="logs foo", + id=disktools.GPTPartitionTool.ID_LINUX), + 6: dict(start=0, size=1048576, partlabel="swap", + id=disktools.GPTPartitionTool.ID_LINUX_SWAP), + } + +xcp.net.biosdevname.all_devices_all_names = mocked_all_devices_all_names +disktools.LVMTool.readInfo = mocked_lvmtool_readInfo +disktools.getMpathNodes = mocked_getMpathNodes +disktools.probePartitioningScheme = mocked_probePartitioningScheme +disktools.GPTPartitionTool.readDiskDetails = mocked_gpt_readDiskDetails +disktools.GPTPartitionTool.partitionTable = mocked_gpt_partitionTable + +# + +import diskutil + +# modifiable by RAID building +MOCKED_DISKLIST = ['sda', + 'sdb', 'sdc', + ] + +def mocked_getDiskList(): + return MOCKED_DISKLIST + +def mocked_getPartitionList(): + return [] + +def mocked_getDiskDeviceVendor(dev): + return "Vendor of {}".format(dev) +def mocked_getDiskDeviceModel(dev): + return "Model of {}".format(dev) +def mocked_getDiskSerialNumber(dev): + return "12345" +def mocked_getDiskDeviceSize(dev): + return 1024**3 + +def mocked_log_available_disks(): + logger.log("not logging disks, yw") + +def mocked_readExtPartitionLabel(partition): + return { + "/dev/sda1": "root-foo", + "/dev/sda5": "logs-foo", + }.get(partition, None) +def mocked_idFromPartition(partition): + logger.log("faking idFromPartition") + return None + +def mocked_diskutil__readOneLineFile__(filename): + assert filename.startswith("/sys/block/") + if filename.endswith("/device/block/size"): + return "512" + if filename.endswith("/queue/logical_block_size"): + return "512" + # FIXME should be a better ENOENT + raise RuntimeError("No such file %r" % (filename,)) + +diskutil.getDiskList = mocked_getDiskList +diskutil.getPartitionList = mocked_getPartitionList + +diskutil.getDiskDeviceVendor = mocked_getDiskDeviceVendor +diskutil.getDiskDeviceModel = mocked_getDiskDeviceModel +diskutil.getDiskSerialNumber = mocked_getDiskSerialNumber +diskutil.getDiskDeviceSize = mocked_getDiskDeviceSize + +diskutil.log_available_disks = mocked_log_available_disks + +diskutil.readExtPartitionLabel = mocked_readExtPartitionLabel +diskutil.idFromPartition = mocked_idFromPartition + +diskutil.__readOneLineFile__ = mocked_diskutil__readOneLineFile__ + +# + +import product + +def mock_mount_root(self, ro=True, boot_device=None): + # FIXME need boot_device? + self.root_fs = util.TempMount(device="/dev/sda1", + tmp_prefix="/tmp/mocked-root-") + +def mock_mount_state(self, ro=True, boot_device=None): + # FIXME need boot_device? + self.state_fs = util.TempMount(device="/dev/sda1", + tmp_prefix="/tmp/mocked-state-") + +def mock_readSettings(self): + return { + 'master': True, + 'ntp-config-method': 'dhcp', + 'host-config': {}, + 'repo-list': [], + + # 'linstor-version': None, + 'linstor-version': "1.29.0-1.el7_9", + } + +def mock_is_rootfs_uefi(mount_point): + return True + +def mock_product_readKeyValueFile(filename, allowed_keys=None, strip_quotes=True): + if filename.startswith("/tmp/mocked-root-") and filename.endswith("/etc/xensource-inventory"): + return dict( + PRODUCT_NAME='xenenterprise', + PRODUCT_BRAND='Mocked XCP-ng', + PRODUCT_VERSION_TEXT="8.2", + PRODUCT_VERSION="8.2.1", + PLATFORM_VERSION="3.2.1", + + INSTALLATION_UUID="cab6b1c5-ffb5-491e-897d-9606437c1699", + CONTROL_DOMAIN_UUID="3620ec67-d002-41b2-a5a5-c12abe7c3ad3", + ) + raise RuntimeError("readKeyValueFile on unknown filename %r" % (filename,)) + +product.ExistingRetailInstallation.mount_root = mock_mount_root +product.ExistingInstallation.mount_state = mock_mount_state +product.ExistingInstallation._readSettings = mock_readSettings +product.is_rootfs_uefi = mock_is_rootfs_uefi +product.util.readKeyValueFile = mock_product_readKeyValueFile + +# + +import upgrade + +def mocked_upgrade_open(fname, mode='r', *args, **kwargs): + logger.debug("open({!r}, mode={!r})".format(fname, mode)) + + realfname = fname # default + + if MOUNTPOINT_PRIMARY and fname.startswith(MOUNTPOINT_PRIMARY + '/'): + if fname == MOUNTPOINT_PRIMARY + "/etc/xensource/xapi-ssl.pem": + realfname = MOCKDATA + "/etc/xensource/xapi-ssl.pem" + + if MOUNTPOINT_BACKUP and fname.startswith(MOUNTPOINT_BACKUP + '/'): + if fname == MOUNTPOINT_BACKUP + "/.xen-backup-partition": + realfname = MOCKDATA + "/emptyfile" + + if realfname != fname: + logger.debug("redirecting open() to %r" % (realfname,)) + + return open(realfname, mode, *args, **kwargs) + +upgrade.open = mocked_upgrade_open + +# + +import repository + +def mocked_repository_open(fname, mode='r', *args, **kwargs): + logger.debug("open({!r}, mode={!r})".format(fname, mode)) + + realfname = fname # default + + if fname == "/root/yum.conf": + tmpfile = tempfile.NamedTemporaryFile(mode=mode, delete=False, + suffix=".conf", prefix="yum-") + logger.debug("redirecting open() to %r" % (tmpfile.name,)) + return tmpfile + if fname == "/root/RPM-GPG-KEY-xcpng": + tmpfile = tempfile.NamedTemporaryFile(mode=mode, delete=False, + prefix="RPM-GPG-KEY-") + logger.debug("redirecting open() to %r" % (tmpfile.name,)) + return tmpfile + + return open(realfname, mode, *args, **kwargs) + +def mocked_disableInitrdCreation(self, root): + logger.log("faking disableInitrdCreation") +def mocked_enableInitrdCreation(self): + logger.log("faking disableInitrdCreation") + +def mocked_installFromYum(yum_conf_path, targets, mounts, progress_callback, cachedir): + logger.log("faking installFromYum") + +repository.open = mocked_repository_open +repository.MainYumRepository.disableInitrdCreation = mocked_disableInitrdCreation +repository.MainYumRepository.enableInitrdCreation = mocked_enableInitrdCreation +repository.installFromYum = mocked_installFromYum + +# + +import netutil + +def mocked_networkingUp(): + return False + +def mocked_getNetifList(include_vlan=False): + return mocked_all_devices_all_names().keys() + +def mocked_interfaceUp(interface): + logger.debug("mocked_interfaceUp({}): telling YES".format(interface)) + return True + +def mocked_netutil_open(fname, mode='r', *args, **kwargs): + logger.debug("open({!r}, mode={!r})".format(fname, mode)) + if fname == '/etc/resolv.conf': + fname = 'resolv.conf' + + return open(fname, mode, *args, **kwargs) + +netutil.networkingUp = mocked_networkingUp +netutil.getNetifList = mocked_getNetifList +netutil.interfaceUp = mocked_interfaceUp + +netutil.open = mocked_netutil_open + +# + +import netinterface + +# Prevent patching of netinterface.open to later contaminate modules +# that blindly import everything from there including our patched open, +# by making sure those blind "import *" run before we patch. Yuck. +import answerfile, product, upgrade, tui.network + +def mocked_netinterface_open(fname, mode='r', *args, **kwargs): + logger.debug("open({!r}, mode={!r})".format(fname, mode)) + + realfname = fname # default + + if MOUNTPOINT_PRIMARY and fname.startswith(MOUNTPOINT_PRIMARY + '/'): + if fname == MOUNTPOINT_PRIMARY + "/etc/xensource/xapi-ssl.pem": + realfname = MOCKDATA + "/etc/xensource/xapi-ssl.pem" + + elif fname.startswith('/etc/sysconfig/network-scripts/ifcfg-'): + realfname = os.path.join(MOCKDATA, os.path.basename(fname)) + + if realfname != fname: + logger.debug("redirecting open() to %r" % (realfname,)) + + return open(realfname, mode, *args, **kwargs) + +netinterface.open = mocked_netinterface_open + +# + +import generalui + +def mocked_getTimeZoneRegions(): + return ['Europe', 'Mars'] +def mocked_getTimeZoneCities(desired_region): + return ['Paris', 'Tokyo'] + +generalui.getTimeZoneRegions = mocked_getTimeZoneRegions +generalui.getTimeZoneCities = mocked_getTimeZoneCities + +# + +import backend + +def mocked_backend_open(fname, mode='r', *args, **kwargs): + logger.debug("open({!r}, mode={!r})".format(fname, mode)) + + realfname = fname # default + + if fname == '/proc/sys/dev/raid/speed_limit_max': + realfname = '/dev/null' + + if fname == '/tmp/root/etc/firstboot.d/data/default-storage.conf': + realfname = 'mocked-default-storage.conf' + + if realfname != fname: + logger.debug("redirecting open() to %r" % (realfname,)) + + return open(realfname, mode, *args, **kwargs) + +backend.open = mocked_backend_open + +def mocked_setTimeNTP(ntp_servers, ntp_config_method): + logger.log("faking setTimeNTP") +def mocked_createDom0DiskFilesystems(install_type, disk, target_boot_mode, boot_partnum, primary_partnum, logs_partnum, disk_label_suffix): + logger.log("faking createDom0DiskFilesystems") + +def mocked_mountVolumes(primary_disk, physical_disks, boot_partnum, primary_partnum, logs_partnum, cleanup, target_boot_mode, swraid): + logger.log("faking mountVolumes") + mounts = {'root': '/tmp/root', + 'boot': '/tmp/root/boot', + 'esp': '/tmp/root/boot/efi', + 'logs': '/tmp/root/var/log', + } + return mounts, list(cleanup) + +def mocked_umountVolumes(disk, mounts, cleanup, force=False): + logger.log("faking umountVolumes") + +def mocked_getFinalisationSequence(ans): + logger.log("faking getFinalisationSequenceumountVolumes") + seq = [ + # cannot return empty list, so return one entry we mock + backend.Task(backend.importYumAndRpmGpgKeys, backend.A(ans, 'mounts'), []), + ] + if ans['install-type'] == backend.INSTALL_TYPE_FRESH: + seq += [ + backend.Task(backend.prepareStorageRepositories, backend.A(ans, 'mounts', 'primary-disk', 'storage-partnum', 'guest-disks', 'sr-type'), []), + ] + + return seq + +def mocked_importYumAndRpmGpgKeys(mounts): + logger.log("faking importYumAndRpmGpgKeys") + +#def mocked_writeResolvConf(mounts, hn_conf, ns_conf): +# logger.log("faking writeResolvConf") + +backend.setTimeNTP = mocked_setTimeNTP +backend.createDom0DiskFilesystems = mocked_createDom0DiskFilesystems +backend.mountVolumes = mocked_mountVolumes +backend.umountVolumes = mocked_umountVolumes +backend.getFinalisationSequence = mocked_getFinalisationSequence +backend.importYumAndRpmGpgKeys = mocked_importYumAndRpmGpgKeys +#backend.writeResolvConf = mocked_writeResolvConf + +# + +original_os_path_exists = os.path.exists; + +def mock_os_path_exists(path): + exists = None + if MOUNTPOINT_STATE and path in ( # force exists + # MOUNTPOINT_STATE + "/etc/firstboot.d/state", + MOUNTPOINT_STATE + "/var/lib/misc/ran-network-init", + MOUNTPOINT_STATE + "/var/lib/misc/ran-storage-init", + ): + exists = True + elif path in ( + "/dev/sda", + ): + exists = True + elif path.startswith("/dev/md/xs-"): + return True + elif path in ( # force NOT exists + '/titi', + ): + exists = False + if exists is not None: + logger.debug("faking os.path.exists(%s) == %s" % (path, exists)) + return exists + else: + logger.debug("NOT faking os.path.exists(%s)" % (path,)) + return original_os_path_exists(path) + +os.path.exists = mock_os_path_exists + +#### + +from xcp import logger + +import install +import tui +import util + +def main(args): + logger.openLog("installer.log", logger.logging.DEBUG) + global MOCKDATA + MOCKDATA = os.path.join(os.path.dirname(sys.argv[0]), "mockrun-data") + logger.log("Starting user interface") + tui.init_ui() + + # answerfile = "file:///data/src/host-installer/doc/answerfile.xml" + answerfile = None + + status = install.go(tui, args, answerfile, None) + logger.log("Shutting down user interface") + tui.end_ui() + return status + +if __name__ == "__main__": + sys.exit(main(util.splitArgs(sys.argv[1:], array_args=('--extrarepo'))))