|
| 1 | +# Automated XCP-ng installation tests |
| 2 | + |
| 3 | +Note: this is a first iteration, which is bound to evolve a lot, when |
| 4 | +we find the time. |
| 5 | + |
| 6 | +Those tests cover installation, upgrade, and restore, using the |
| 7 | +official installation ISO. There is a small number of actual tests, |
| 8 | +but a large number of parameters to apply the same testing steps to |
| 9 | +various situations (UEFI or BIOS, choice of a release to upgrade from, |
| 10 | +install purely from ISO or from a network pacakge repository, local SR |
| 11 | +type to create). |
| 12 | + |
| 13 | +## Terminology |
| 14 | + |
| 15 | +In their current state, those tests can only install XCP-ng nested |
| 16 | +inside a pre-existing XCP-ng pool. In standard terminology of |
| 17 | +hypervisor nesting, we have: |
| 18 | + |
| 19 | +* L0: the pre-existing XCP-ng pool |
| 20 | +* L1: the XCP-ng host(s) exercised by the tests |
| 21 | +* L2: VMs launched by the hosts under test |
| 22 | + |
| 23 | +As far as those install tests are concerned, L2 is out of scope, L0 is |
| 24 | +simply refered to as "host" and L1 as "guest". |
| 25 | + |
| 26 | +When it comes to running tests against our L1, the perspective is |
| 27 | +shifting, with L1 being the "host", and L0 is then refered to as the |
| 28 | +"nest" (which for the test itself is a hidden detail of the execution |
| 29 | +environment). |
| 30 | + |
| 31 | +## Prerequisites |
| 32 | + |
| 33 | +L0 host must have |
| 34 | +- a default SR (where the disk images for L1 hosts will be stored) |
| 35 | +- one ISO SR (where the install images will be copied during install) |
| 36 | + |
| 37 | +## Quick start |
| 38 | + |
| 39 | +Use `data.py-dist` as usual as a reference to craft your `data.py`. |
| 40 | +Especially important here are: |
| 41 | +* `NETWORKS['MGMT']`, which should match the name of the network in |
| 42 | + your L0, which you want to use for the L1's management networks |
| 43 | +* `TEST_SSH_PUBKEY`, a multiline string to be used as a |
| 44 | + `.ssh/authorized_keys` in the hosts to be installed (you must have |
| 45 | + access to one of the matching private keys) |
| 46 | +* `ARP_SERVER`, a machine on the `NETWORKS['MGMT']` local network, |
| 47 | + with root access for one of the keys in `TEST_SSH_PUBKEY`, for use |
| 48 | + to determine the IP address of guests |
| 49 | +* `TOOLS['iso-remaster']`, as local path to |
| 50 | + `scripts/iso-remaster/iso-remaster.sh` from the `xcp` repo |
| 51 | +* `ISO_IMAGES_CACHE`, to specify where to cache the official ISO |
| 52 | + images |
| 53 | +* `ISO_IMAGES_BASE`, to specify the base URL under which the images in |
| 54 | + `IMAGES_ISO` can be downloaded (defaults to the official XCP-ng |
| 55 | + public download server) |
| 56 | +* `OBJECTS_NAME_PREFIX` (optional) will allow you to use a unique |
| 57 | + prefix to easily filter out *your* VMs in a shared lab |
| 58 | + |
| 59 | +``` |
| 60 | + XCPNG83_NIGHTLY=xcp-ng-8.3-ci-nightly-20250311.iso \ |
| 61 | + pytest \ |
| 62 | + --log-file-level=DEBUG --log-file=test-install.log \ |
| 63 | + @tests/install/test-sequences/inst{,+upg,+upg+rst}.lst \ |
| 64 | + --hosts=<L0-host-IP> |
| 65 | +``` |
| 66 | + |
| 67 | +The above command instructs `pytest` to: |
| 68 | +* run the test sequences as defined by the specified `.lst` files |
| 69 | + (which were specially written to chain an installation, an upgrade |
| 70 | + to same version, and a restore, all using a single nightly image) |
| 71 | +* specify |
| 72 | +* to save detailed logs in a file, while during execution only |
| 73 | + high-level progress messages are shown to avoid flooding |
| 74 | + |
| 75 | +Tests to be executed can always be listed by adding to `pytest` |
| 76 | +options `--co -q`, which for the above command should show: |
| 77 | + |
| 78 | +``` |
| 79 | +tests/install/test.py::TestNested::test_install[uefi-83nightly-iso-ext] |
| 80 | +tests/install/test.py::TestNested::test_tune_firstboot[None-uefi-83nightly-host1-iso-ext] |
| 81 | +tests/install/test.py::TestNested::test_boot_inst[uefi-83nightly-host1-iso-ext] |
| 82 | +tests/install/test.py::TestNested::test_upgrade[uefi-83nightly-83nightly-host1-iso-ext] |
| 83 | +tests/install/test.py::TestNested::test_boot_upg[uefi-83nightly-83nightly-host1-iso-ext] |
| 84 | +tests/install/test.py::TestNested::test_restore[uefi-83nightly-83nightly-83nightly-iso-ext] |
| 85 | +tests/install/test.py::TestNested::test_boot_rst[uefi-83nightly-83nightly-83nightly-iso-ext] |
| 86 | +``` |
| 87 | + |
| 88 | +In order those are: |
| 89 | +* running an XCP-ng installation in a UEFI guest, using a "8.3 |
| 90 | + nightly" ISO (which when using the provided `data.py-dist` will take |
| 91 | + a path or URL to an ISO from envvar `XCPNG83_NIGHTLY`), using the |
| 92 | + ISO itself as RPM source, and creating an `EXT` local SR. The ISO |
| 93 | + is first remastered to include an answerfile, as well as enable ssh |
| 94 | + access using the test key, and to shutdown instead of rebooting once |
| 95 | + installation is done. |
| 96 | +* running a helper VM to modify the firstboot configuration set by the |
| 97 | + installer, so we can select hostname, and set unique UUIDs for our |
| 98 | + L1 host |
| 99 | +* booting the installed host for the first time, check that XAPI |
| 100 | + properly starts up, that all firstboot services are starting OK, and |
| 101 | + that host is running the expected product version |
| 102 | +* running an upgrade, similarly to install test, also specifying in |
| 103 | + parameters the version of the installed host we're upgrading |
| 104 | +* booting the upgraded host for the first time, similarly to install test |
| 105 | +* running a restore, similarly to install test, also specifying in |
| 106 | + parameters the version of the upgraded host we're upgrading |
| 107 | + ("version" which includes the version it was upgraded from, which |
| 108 | + still lives in the backup partition, and which is the one getting |
| 109 | + restored) |
| 110 | +* booting the restored host for the first time, similarly to install test |
| 111 | + |
| 112 | +Note: the selected management network configuration uses DHCP. |
| 113 | + |
| 114 | +## Caching installs and chaining tests |
| 115 | + |
| 116 | +Note: some of this section applies to tests that need to manipulate |
| 117 | +the L0 host, but the cache concepts is also useful to those that just |
| 118 | +mean to launch a test against an install host from the cache. This |
| 119 | +section can possibly been split to separate those concerns. |
| 120 | + |
| 121 | +To allow launching all those install steps one by one, and applying |
| 122 | +tests on the resulting L1 hosts, the state of those hosts are cached |
| 123 | +in L0 as a clone of the L1 (seen from L0 as a guest). |
| 124 | + |
| 125 | +The test-chaining mechanism uses the `name-description` field to |
| 126 | +identify the output of a given test, for example `[Cache for |
| 127 | +install.test::Nested::upgrade[uefi-83nightly-83nightly-host1-iso-ext]-vm1-1857a3f0ef69640d10348e9f0adef09f6e9a7c5d]`. |
| 128 | +This includes a the shortened test name with its full test arguments, |
| 129 | +the ID of the L1 "host VM" (as a test can launch more than one), and |
| 130 | +the git revision of the test repo. |
| 131 | + |
| 132 | +Currently the tests producing the image necessary for a given test are |
| 133 | +recorded using `pytest-dependencies`. As a consequence, when running |
| 134 | +a test that needs the image produced by an earlier test, the |
| 135 | +`--ignore-unknown-dependency` flag must be used, or the test will be |
| 136 | +skipped, as in: |
| 137 | +``` |
| 138 | +$ XCPNG83_NIGHTLY=~/iso/xcp-ng-8.3.0.iso pytest --hosts=172.16.210.11 tests/install/test.py::TestNested::test_boot_inst[uefi-83nightly-host1-iso-ext] |
| 139 | +... |
| 140 | +SKIPPED (test_boot_inst[uefi-83nightly-host1-iso-ext] depends on TestNested::test_tune_firstboot[None-uefi-83nightly-host1-iso-ext]) |
| 141 | +``` |
| 142 | + |
| 143 | +This git revision ensures consistency of the test runs, avoiding |
| 144 | +during test development the inadvertent use of the output of an |
| 145 | +outdated version of a given test. It thus creates a strong |
| 146 | +constraint, that all changes be committed before the test is launched. |
| 147 | +As a consequence when working on a test that needs a VM from the |
| 148 | +cache, as we don't want to rerun all the preceding tests, it requires |
| 149 | +an explicit waiver to use an image with a different revision; this is |
| 150 | +done in `data.py` in a dict specifying an equivalent cache ID to be |
| 151 | +used when one is not found: |
| 152 | + |
| 153 | +IMAGE_EQUIVS = { |
| 154 | + 'install.test::Nested::upgrade[bios-75-821.1-host1-iso-nosr]-vm1-6ab478747815979b22e0d0555aa2782bf33850ed': |
| 155 | + 'install.test::Nested::upgrade[bios-75-821.1-host1-iso-nosr]-vm1-17ba75d798499b563bfadca98f8d22a2cb81efdc', |
| 156 | +} |
| 157 | + |
| 158 | +Note: the git revision is read at the start of the test run, so you |
| 159 | +can safely go on working with your codebase while you have a test |
| 160 | +running. |
| 161 | + |
| 162 | +If you're not sure of the cache IDs to put in there: |
| 163 | +* attempt to run your test, it will disclose the missing cache ID, you |
| 164 | + can paste it as a key in `IMAGE_EQUIVS`: |
| 165 | + ``` |
| 166 | + Mar 17 14:53:51.599 INFO Could not find a VM in cache for 'install.test::Nested::tune_firstboot[None-uefi-83nightly-host1-iso-ext]-vm1-ce73023e06d680355dbfb0b726aae8eeee0c07ff' |
| 167 | + Mar 17 14:53:51.600 ERROR exception caught... |
| 168 | + ``` |
| 169 | +* for the value, use the same string and just replace the git revision |
| 170 | + with the one you want to use, which must exist in your cache. |
| 171 | + Depending on the situation, you may find the revision using `git |
| 172 | + log` or `git revlog`, or possibly list the ones available in your |
| 173 | + cache with something like: |
| 174 | + ``` |
| 175 | + xe vm-list params=name-description | grep -F 'install.test::Nested::upgrade[bios-75-821.1-host1-iso-nosr]-vm1-' |
| 176 | + ``` |
| 177 | + |
| 178 | +Note: this mechanism can surely be improved, suggestions welcomed. |
| 179 | +Note that a pending PR introduces the notion of "default git revision |
| 180 | +to try for a given list of images". |
| 181 | + |
| 182 | +## Running classical XCP-ng tests |
| 183 | + |
| 184 | +Running classical XCP-ng tests, which in our case are meant to run |
| 185 | +against the L1 host, uses a specific syntax for the `--hosts` flag, |
| 186 | +along with the `--nest=<L0-host>` flag, as in: |
| 187 | + |
| 188 | +``` |
| 189 | +pytest --nest=172.16.210.11 \ |
| 190 | + --hosts=cache://install.test::Nested::boot_inst[uefi-83nightly-host1-iso-ext]-vm1-1857a3f0ef69640d10348e9f0adef09f6e9a7c5d \ |
| 191 | + tests/misc/test_basic_without_ssh.py::test_vm_start_stop |
| 192 | +``` |
0 commit comments