From e781ed32a2633cd0135372b9d9a0efad4cad77d0 Mon Sep 17 00:00:00 2001 From: Mark Raynsford Date: Sat, 11 Jul 2020 18:48:23 +0000 Subject: [PATCH 1/9] Begin next development iteration --- com.io7m.waxmill.boot/pom.xml | 2 +- com.io7m.waxmill.client.api/pom.xml | 2 +- com.io7m.waxmill.client.vanilla/pom.xml | 2 +- com.io7m.waxmill.cmdline/pom.xml | 2 +- com.io7m.waxmill.database.api/pom.xml | 2 +- com.io7m.waxmill.database.vanilla/pom.xml | 2 +- com.io7m.waxmill.documentation/pom.xml | 2 +- com.io7m.waxmill.exceptions/pom.xml | 2 +- com.io7m.waxmill.locks/pom.xml | 2 +- com.io7m.waxmill.machines/pom.xml | 2 +- com.io7m.waxmill.parser.api/pom.xml | 2 +- com.io7m.waxmill.process.api/pom.xml | 2 +- com.io7m.waxmill.process.posix/pom.xml | 2 +- com.io7m.waxmill.realize/pom.xml | 2 +- com.io7m.waxmill.serializer.api/pom.xml | 2 +- com.io7m.waxmill.strings.api/pom.xml | 2 +- com.io7m.waxmill.tests/pom.xml | 2 +- com.io7m.waxmill.xml/pom.xml | 2 +- pom.xml | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/com.io7m.waxmill.boot/pom.xml b/com.io7m.waxmill.boot/pom.xml index e6e712d..0918afc 100644 --- a/com.io7m.waxmill.boot/pom.xml +++ b/com.io7m.waxmill.boot/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.boot diff --git a/com.io7m.waxmill.client.api/pom.xml b/com.io7m.waxmill.client.api/pom.xml index 21f8867..8a853ae 100644 --- a/com.io7m.waxmill.client.api/pom.xml +++ b/com.io7m.waxmill.client.api/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.client.api diff --git a/com.io7m.waxmill.client.vanilla/pom.xml b/com.io7m.waxmill.client.vanilla/pom.xml index cfae9ac..c8d4591 100644 --- a/com.io7m.waxmill.client.vanilla/pom.xml +++ b/com.io7m.waxmill.client.vanilla/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.client.vanilla diff --git a/com.io7m.waxmill.cmdline/pom.xml b/com.io7m.waxmill.cmdline/pom.xml index 0740045..c801e04 100644 --- a/com.io7m.waxmill.cmdline/pom.xml +++ b/com.io7m.waxmill.cmdline/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.cmdline diff --git a/com.io7m.waxmill.database.api/pom.xml b/com.io7m.waxmill.database.api/pom.xml index 10405d7..e21279a 100644 --- a/com.io7m.waxmill.database.api/pom.xml +++ b/com.io7m.waxmill.database.api/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.database.api diff --git a/com.io7m.waxmill.database.vanilla/pom.xml b/com.io7m.waxmill.database.vanilla/pom.xml index 8e4c2e7..ae21320 100644 --- a/com.io7m.waxmill.database.vanilla/pom.xml +++ b/com.io7m.waxmill.database.vanilla/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.database.vanilla diff --git a/com.io7m.waxmill.documentation/pom.xml b/com.io7m.waxmill.documentation/pom.xml index 092f615..0a78338 100644 --- a/com.io7m.waxmill.documentation/pom.xml +++ b/com.io7m.waxmill.documentation/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.documentation diff --git a/com.io7m.waxmill.exceptions/pom.xml b/com.io7m.waxmill.exceptions/pom.xml index ebbd3bd..c0d7948 100644 --- a/com.io7m.waxmill.exceptions/pom.xml +++ b/com.io7m.waxmill.exceptions/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.exceptions diff --git a/com.io7m.waxmill.locks/pom.xml b/com.io7m.waxmill.locks/pom.xml index 076be6c..c958caf 100644 --- a/com.io7m.waxmill.locks/pom.xml +++ b/com.io7m.waxmill.locks/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.locks diff --git a/com.io7m.waxmill.machines/pom.xml b/com.io7m.waxmill.machines/pom.xml index 93a1de6..8c829a3 100644 --- a/com.io7m.waxmill.machines/pom.xml +++ b/com.io7m.waxmill.machines/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.machines diff --git a/com.io7m.waxmill.parser.api/pom.xml b/com.io7m.waxmill.parser.api/pom.xml index 4c2f280..5685aa1 100644 --- a/com.io7m.waxmill.parser.api/pom.xml +++ b/com.io7m.waxmill.parser.api/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.parser.api diff --git a/com.io7m.waxmill.process.api/pom.xml b/com.io7m.waxmill.process.api/pom.xml index d5c74e1..9266579 100644 --- a/com.io7m.waxmill.process.api/pom.xml +++ b/com.io7m.waxmill.process.api/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.process.api diff --git a/com.io7m.waxmill.process.posix/pom.xml b/com.io7m.waxmill.process.posix/pom.xml index e58efd5..e57dd54 100644 --- a/com.io7m.waxmill.process.posix/pom.xml +++ b/com.io7m.waxmill.process.posix/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.process.posix diff --git a/com.io7m.waxmill.realize/pom.xml b/com.io7m.waxmill.realize/pom.xml index 986a2d2..3db512c 100644 --- a/com.io7m.waxmill.realize/pom.xml +++ b/com.io7m.waxmill.realize/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.realize diff --git a/com.io7m.waxmill.serializer.api/pom.xml b/com.io7m.waxmill.serializer.api/pom.xml index d22520b..73343e7 100644 --- a/com.io7m.waxmill.serializer.api/pom.xml +++ b/com.io7m.waxmill.serializer.api/pom.xml @@ -8,7 +8,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.serializer.api diff --git a/com.io7m.waxmill.strings.api/pom.xml b/com.io7m.waxmill.strings.api/pom.xml index 3a6d452..7c6542f 100644 --- a/com.io7m.waxmill.strings.api/pom.xml +++ b/com.io7m.waxmill.strings.api/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.strings.api diff --git a/com.io7m.waxmill.tests/pom.xml b/com.io7m.waxmill.tests/pom.xml index 61b86fb..8e84cd9 100644 --- a/com.io7m.waxmill.tests/pom.xml +++ b/com.io7m.waxmill.tests/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.tests diff --git a/com.io7m.waxmill.xml/pom.xml b/com.io7m.waxmill.xml/pom.xml index e58ec06..6f5d314 100644 --- a/com.io7m.waxmill.xml/pom.xml +++ b/com.io7m.waxmill.xml/pom.xml @@ -8,7 +8,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT com.io7m.waxmill.xml diff --git a/pom.xml b/pom.xml index dd67124..020fc77 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.2 + 0.0.3-SNAPSHOT pom FreeBSD BHyve Manager From 6c0f426d48eff4ad45ff85dc4ad2dcebd2fc3c3e Mon Sep 17 00:00:00 2001 From: Mark Raynsford Date: Sun, 19 Jul 2020 11:07:21 +0000 Subject: [PATCH 2/9] Add interface groups to the data model This adds support for interface groups to the data model, but does not yet provide any commands to manipulate them. Affects: https://github.com/io7m/waxmill/issues/24 --- .../boot/WXMBootConfigurationEvaluator.java | 3 +- .../WXMCommandVMAddE1000NetworkDevice.java | 3 +- .../WXMCommandVMAddVirtioNetworkDevice.java | 3 +- .../internal/WXMNetworkBackendConverter.java | 2 +- .../machines/WXMBootConfigurationType.java | 12 +- .../io7m/waxmill/machines/WXMDeviceType.java | 154 +++--------------- .../machines/WXMInterfaceGroupNameType.java | 46 ++++++ .../machines/WXMInterfaceGroupNames.java | 70 ++++++++ .../machines/WXMNetworkDeviceBackendType.java | 69 ++++++++ .../com/io7m/waxmill/machines/WXMTapType.java | 61 +++++++ .../io7m/waxmill/machines/WXMVMNetType.java | 61 +++++++ .../machines/WXMVirtualMachineSets.java | 2 +- .../com/io7m/waxmill/tests/WXMEqualsTest.java | 4 +- .../WXMVirtualMachineParserContract.java | 5 + .../resources/com/io7m/waxmill/tests/vm0.xml | 3 + .../com/io7m/waxmill/tests/vmSet0.xml | 3 + .../vm/v1/WXM1E1000NetworkDeviceParser.java | 2 +- .../xml/vm/v1/WXM1InterfaceGroupParser.java | 54 ++++++ .../xml/vm/v1/WXM1InterfaceGroups.java | 45 +++++ .../io7m/waxmill/xml/vm/v1/WXM1TapParser.java | 13 +- .../waxmill/xml/vm/v1/WXM1VMNetParser.java | 13 +- .../vm/v1/WXM1VirtioNetworkDeviceParser.java | 2 +- .../vm/v1/WXM1VirtualMachineSerializer.java | 2 + .../com/io7m/waxmill/xml/vm/v1/vm-1.0.xsd | 50 +++++- spotbugs-filter.xml | 1 + 25 files changed, 523 insertions(+), 160 deletions(-) create mode 100644 com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMInterfaceGroupNameType.java create mode 100644 com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMInterfaceGroupNames.java create mode 100644 com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMNetworkDeviceBackendType.java create mode 100644 com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMTapType.java create mode 100644 com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMVMNetType.java create mode 100644 com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1InterfaceGroupParser.java create mode 100644 com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1InterfaceGroups.java diff --git a/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationEvaluator.java b/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationEvaluator.java index 2ead41a..12a7605 100644 --- a/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationEvaluator.java +++ b/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationEvaluator.java @@ -70,7 +70,8 @@ import java.util.stream.Collectors; import static com.io7m.waxmill.machines.WXMBootConfigurationType.WXMEvaluatedBootConfigurationType; -import static com.io7m.waxmill.machines.WXMDeviceType.WXMDeviceVirtioNetworkType.WXMNetworkDeviceBackendType; + +import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; import static com.io7m.waxmill.machines.WXMDeviceType.WXMDeviceVirtioNetworkType.WXMTTYBackendType; import static com.io7m.waxmill.machines.WXMDeviceType.WXMStorageBackendType; import static com.io7m.waxmill.machines.WXMTTYBackends.NMDMSide.NMDM_HOST; diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddE1000NetworkDevice.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddE1000NetworkDevice.java index bffc8fe..0206ad2 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddE1000NetworkDevice.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddE1000NetworkDevice.java @@ -34,7 +34,8 @@ import java.util.UUID; import static com.io7m.claypot.core.CLPCommandType.Status.SUCCESS; -import static com.io7m.waxmill.machines.WXMDeviceType.WXMNetworkDeviceBackendType; + +import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; @Parameters(commandDescription = "Add an e1000 network device to a virtual machine.") public final class WXMCommandVMAddE1000NetworkDevice diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddVirtioNetworkDevice.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddVirtioNetworkDevice.java index c18cc07..d4b77d0 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddVirtioNetworkDevice.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddVirtioNetworkDevice.java @@ -34,7 +34,8 @@ import java.util.UUID; import static com.io7m.claypot.core.CLPCommandType.Status.SUCCESS; -import static com.io7m.waxmill.machines.WXMDeviceType.WXMNetworkDeviceBackendType; + +import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; @Parameters(commandDescription = "Add a virtio network device to a virtual machine.") public final class WXMCommandVMAddVirtioNetworkDevice diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMNetworkBackendConverter.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMNetworkBackendConverter.java index d026d5a..e835ea1 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMNetworkBackendConverter.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMNetworkBackendConverter.java @@ -23,7 +23,7 @@ import com.io7m.waxmill.machines.WXMVMNet; import com.io7m.waxmill.machines.WXMVMNetDeviceName; -import static com.io7m.waxmill.machines.WXMDeviceType.WXMNetworkDeviceBackendType; +import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; public final class WXMNetworkBackendConverter implements IStringConverter diff --git a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMBootConfigurationType.java b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMBootConfigurationType.java index 9c4c85a..884b5f4 100644 --- a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMBootConfigurationType.java +++ b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMBootConfigurationType.java @@ -232,6 +232,12 @@ interface WXMGRUBKernelInstructionsType Kind kind(); + /** + * @return The slots required to have assigned storage + */ + + Set requiredDevices(); + /** * The kind of kernels. */ @@ -250,12 +256,6 @@ enum Kind KERNEL_LINUX } - - /** - * @return The slots required to have assigned storage - */ - - Set requiredDevices(); } /** diff --git a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMDeviceType.java b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMDeviceType.java index a13fa57..c710cfb 100644 --- a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMDeviceType.java +++ b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMDeviceType.java @@ -42,8 +42,6 @@ import static com.io7m.waxmill.machines.WXMDeviceType.Kind.WXM_PASSTHRU; import static com.io7m.waxmill.machines.WXMDeviceType.Kind.WXM_VIRTIO_BLOCK; import static com.io7m.waxmill.machines.WXMDeviceType.Kind.WXM_VIRTIO_NETWORK; -import static com.io7m.waxmill.machines.WXMDeviceType.WXMNetworkDeviceBackendType.Kind.WXM_TAP; -import static com.io7m.waxmill.machines.WXMDeviceType.WXMNetworkDeviceBackendType.Kind.WXM_VMNET; import static com.io7m.waxmill.machines.WXMDeviceType.WXMStorageBackendType.Kind.WXM_STORAGE_FILE; import static com.io7m.waxmill.machines.WXMDeviceType.WXMStorageBackendType.Kind.WXM_STORAGE_ZFS_VOLUME; import static com.io7m.waxmill.machines.WXMDeviceType.WXMTTYBackendType.Kind.WXM_FILE; @@ -189,6 +187,27 @@ enum Kind WXM_FRAMEBUFFER } + enum WXMLPCTTYNames + { + WXM_COM1("com1"), + WXM_COM2("com2"), + WXM_BOOTROM("bootrom"); + + private final String deviceName; + + WXMLPCTTYNames( + final String inDeviceName) + { + this.deviceName = + Objects.requireNonNull(inDeviceName, "deviceName"); + } + + public String deviceName() + { + return this.deviceName; + } + } + /** * A PCI passthru device. */ @@ -491,116 +510,6 @@ default String comment() WXMNetworkDeviceBackendType backend(); } - /** - * The type of network device backends. - */ - - interface WXMNetworkDeviceBackendType - { - /** - * @return The backend kind - */ - - Kind kind(); - - /** - * @return A descriptive comment - */ - - @Value.Default - default String comment() - { - return ""; - } - - /** - * The kind of backend. - */ - - enum Kind - { - /** - * The device is backed by a tap device. - */ - - WXM_TAP, - - /** - * The device is backed by a vmnet device. - */ - - WXM_VMNET - } - } - - /** - * A TAP device. - */ - - @ImmutablesStyleType - @Value.Immutable - interface WXMTapType extends WXMNetworkDeviceBackendType - { - @Override - default Kind kind() - { - return WXM_TAP; - } - - /** - * @return The underlying device name - */ - - WXMTAPDeviceName name(); - - /** - * @return The underlying device MAC address - */ - - WXMMACAddress address(); - - @Override - @Value.Default - default String comment() - { - return ""; - } - } - - /** - * A vmnet device. - */ - - @ImmutablesStyleType - @Value.Immutable - interface WXMVMNetType extends WXMNetworkDeviceBackendType - { - @Override - default Kind kind() - { - return WXM_VMNET; - } - - /** - * @return The underlying device name - */ - - WXMVMNetDeviceName name(); - - /** - * @return The underlying device MAC address - */ - - WXMMACAddress address(); - - @Override - @Value.Default - default String comment() - { - return ""; - } - } - interface WXMStorageBackendType { Kind kind(); @@ -890,27 +799,6 @@ default String comment() } } - enum WXMLPCTTYNames - { - WXM_COM1("com1"), - WXM_COM2("com2"), - WXM_BOOTROM("bootrom"); - - private final String deviceName; - - public String deviceName() - { - return this.deviceName; - } - - WXMLPCTTYNames( - final String inDeviceName) - { - this.deviceName = - Objects.requireNonNull(inDeviceName, "deviceName"); - } - } - @ImmutablesStyleType @Value.Immutable interface WXMDeviceLPCType extends WXMDeviceType diff --git a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMInterfaceGroupNameType.java b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMInterfaceGroupNameType.java new file mode 100644 index 0000000..200e961 --- /dev/null +++ b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMInterfaceGroupNameType.java @@ -0,0 +1,46 @@ +/* + * Copyright © 2020 Mark Raynsford http://io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package com.io7m.waxmill.machines; + +import com.io7m.immutables.styles.ImmutablesStyleType; +import org.immutables.value.Value; + +/** + * A TAP device name, such as {@code tap23}. + */ + +@Value.Immutable +@ImmutablesStyleType +public interface WXMInterfaceGroupNameType +{ + /** + * @return The device name + */ + + @Value.Parameter + String value(); + + /** + * Check preconditions for the type. + */ + + @Value.Check + default void checkPreconditions() + { + WXMInterfaceGroupNames.checkValid(this.value()); + } +} diff --git a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMInterfaceGroupNames.java b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMInterfaceGroupNames.java new file mode 100644 index 0000000..1e2b41e --- /dev/null +++ b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMInterfaceGroupNames.java @@ -0,0 +1,70 @@ +/* + * Copyright © 2020 Mark Raynsford http://io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package com.io7m.waxmill.machines; + +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * Functions over interface group names. + */ + +public final class WXMInterfaceGroupNames +{ + private static final Pattern VALID_NAME = + Pattern.compile("[A-Za-z_]{1,15}"); + + private WXMInterfaceGroupNames() + { + + } + + /** + * @param name The raw name string + * + * @return {@code true} if the given string represents a valid interface group name + */ + + public static boolean isValid( + final String name) + { + return VALID_NAME.matcher( + Objects.requireNonNull(name, "name") + ).matches(); + } + + /** + * Check that a string represents a valid interface group name. + * + * @param name The raw name string + * + * @return {@code name} + * + * @throws IllegalArgumentException If the string is not a interface group name + */ + + public static String checkValid( + final String name) + { + if (isValid(name)) { + return name; + } + throw new IllegalArgumentException( + String.format("Invalid name '%s': Must match %s", name, VALID_NAME) + ); + } +} diff --git a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMNetworkDeviceBackendType.java b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMNetworkDeviceBackendType.java new file mode 100644 index 0000000..557b73d --- /dev/null +++ b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMNetworkDeviceBackendType.java @@ -0,0 +1,69 @@ +/* + * Copyright © 2020 Mark Raynsford http://io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package com.io7m.waxmill.machines; + +import org.immutables.value.Value; + +import java.util.List; + +/** + * The type of network device backends. + */ + +public interface WXMNetworkDeviceBackendType +{ + /** + * @return The backend kind + */ + + Kind kind(); + + /** + * @return A descriptive comment + */ + + @Value.Default + default String comment() + { + return ""; + } + + /** + * @return The groups to which this device backend belongs + */ + + List groups(); + + /** + * The kind of backend. + */ + + enum Kind + { + /** + * The device is backed by a tap device. + */ + + WXM_TAP, + + /** + * The device is backed by a vmnet device. + */ + + WXM_VMNET + } +} diff --git a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMTapType.java b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMTapType.java new file mode 100644 index 0000000..6690cd6 --- /dev/null +++ b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMTapType.java @@ -0,0 +1,61 @@ +/* + * Copyright © 2020 Mark Raynsford http://io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package com.io7m.waxmill.machines; + +import com.io7m.immutables.styles.ImmutablesStyleType; +import org.immutables.value.Value; + +import java.util.List; + +import static com.io7m.waxmill.machines.WXMNetworkDeviceBackendType.Kind.WXM_TAP; + +/** + * A TAP device. + */ + +@ImmutablesStyleType +@Value.Immutable +public interface WXMTapType extends WXMNetworkDeviceBackendType +{ + @Override + default Kind kind() + { + return WXM_TAP; + } + + /** + * @return The underlying device name + */ + + WXMTAPDeviceName name(); + + /** + * @return The underlying device MAC address + */ + + WXMMACAddress address(); + + @Override + List groups(); + + @Override + @Value.Default + default String comment() + { + return ""; + } +} diff --git a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMVMNetType.java b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMVMNetType.java new file mode 100644 index 0000000..075a002 --- /dev/null +++ b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMVMNetType.java @@ -0,0 +1,61 @@ +/* + * Copyright © 2020 Mark Raynsford http://io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package com.io7m.waxmill.machines; + +import com.io7m.immutables.styles.ImmutablesStyleType; +import org.immutables.value.Value; + +import java.util.List; + +import static com.io7m.waxmill.machines.WXMNetworkDeviceBackendType.Kind.WXM_VMNET; + +/** + * A vmnet device. + */ + +@ImmutablesStyleType +@Value.Immutable +public interface WXMVMNetType extends WXMNetworkDeviceBackendType +{ + @Override + default Kind kind() + { + return WXM_VMNET; + } + + /** + * @return The underlying device name + */ + + WXMVMNetDeviceName name(); + + /** + * @return The underlying device MAC address + */ + + WXMMACAddress address(); + + @Override + List groups(); + + @Override + @Value.Default + default String comment() + { + return ""; + } +} diff --git a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMVirtualMachineSets.java b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMVirtualMachineSets.java index 63ab30e..1d9b160 100644 --- a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMVirtualMachineSets.java +++ b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMVirtualMachineSets.java @@ -59,7 +59,7 @@ public static WXMVirtualMachineSet one( /** * Create a set consisting of the union of all of the given sets. * - * @param messages The machine messages + * @param messages The machine messages * @param machineSets The sets of machines * * @return A set consisting of all of the machines in all sets diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/WXMEqualsTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/WXMEqualsTest.java index 5edac26..d79ab02 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/WXMEqualsTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/WXMEqualsTest.java @@ -146,7 +146,7 @@ public final class WXMEqualsTest Set.of("logical", "physical")), new WXMClassUnderTest( WXMTap.class, - Set.of("comment", "name", "address")), + Set.of("comment", "name", "address", "groups")), new WXMClassUnderTest( WXMTAPDeviceName.class, Set.of("value")), @@ -172,7 +172,7 @@ public final class WXMEqualsTest Set.of("machines")), new WXMClassUnderTest( WXMVMNet.class, - Set.of("comment", "name", "address")), + Set.of("comment", "name", "address", "groups")), new WXMClassUnderTest( WXMVMNetDeviceName.class, Set.of("value")) diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/WXMVirtualMachineParserContract.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/WXMVirtualMachineParserContract.java index c99264a..de42f4e 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/WXMVirtualMachineParserContract.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/WXMVirtualMachineParserContract.java @@ -129,6 +129,8 @@ public void exampleParses() assertEquals("A TAP device.", tap.comment()); assertEquals("d7:94:b5:60:0d:ac", tap.address().value()); assertEquals("tap23", tap.name().value()); + assertEquals("highSec", tap.groups().get(0).value()); + assertEquals(1, tap.groups().size()); final var net1 = (WXMDeviceVirtioNetwork) devices.get(2); assertEquals("0:2:0", net1.deviceSlot().toString()); @@ -137,6 +139,9 @@ public void exampleParses() assertEquals("A VMNet device.", vmnet.comment()); assertEquals("d7:92:b5:60:0d:ac", vmnet.address().value()); assertEquals("vmnet23", vmnet.name().value()); + assertEquals("lowSec", vmnet.groups().get(0).value()); + assertEquals("medSec", vmnet.groups().get(1).value()); + assertEquals(2, vmnet.groups().size()); final var hd0 = (WXMDeviceAHCIDisk) devices.get(3); assertEquals("0:3:0", hd0.deviceSlot().toString()); diff --git a/com.io7m.waxmill.tests/src/test/resources/com/io7m/waxmill/tests/vm0.xml b/com.io7m.waxmill.tests/src/test/resources/com/io7m/waxmill/tests/vm0.xml index 39ed1b8..9dffba6 100644 --- a/com.io7m.waxmill.tests/src/test/resources/com/io7m/waxmill/tests/vm0.xml +++ b/com.io7m.waxmill.tests/src/test/resources/com/io7m/waxmill/tests/vm0.xml @@ -41,6 +41,7 @@ A TAP device. + @@ -52,6 +53,8 @@ A VMNet device. + + diff --git a/com.io7m.waxmill.tests/src/test/resources/com/io7m/waxmill/tests/vmSet0.xml b/com.io7m.waxmill.tests/src/test/resources/com/io7m/waxmill/tests/vmSet0.xml index 01fe84e..7fa4ab1 100644 --- a/com.io7m.waxmill.tests/src/test/resources/com/io7m/waxmill/tests/vmSet0.xml +++ b/com.io7m.waxmill.tests/src/test/resources/com/io7m/waxmill/tests/vmSet0.xml @@ -41,6 +41,7 @@ A TAP device. + @@ -52,6 +53,8 @@ A VMNet device. + + diff --git a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1E1000NetworkDeviceParser.java b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1E1000NetworkDeviceParser.java index 94ef524..231676e 100644 --- a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1E1000NetworkDeviceParser.java +++ b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1E1000NetworkDeviceParser.java @@ -26,7 +26,7 @@ import java.util.Map; -import static com.io7m.waxmill.machines.WXMDeviceType.WXMDeviceVirtioNetworkType.WXMNetworkDeviceBackendType; +import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; import static com.io7m.waxmill.xml.vm.v1.WXM1Names.element; public final class WXM1E1000NetworkDeviceParser diff --git a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1InterfaceGroupParser.java b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1InterfaceGroupParser.java new file mode 100644 index 0000000..643fe7a --- /dev/null +++ b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1InterfaceGroupParser.java @@ -0,0 +1,54 @@ +/* + * Copyright © 2020 Mark Raynsford http://io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package com.io7m.waxmill.xml.vm.v1; + +import com.io7m.blackthorne.api.BTElementHandlerType; +import com.io7m.blackthorne.api.BTElementParsingContextType; +import com.io7m.waxmill.machines.WXMInterfaceGroupName; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +public final class WXM1InterfaceGroupParser + implements BTElementHandlerType +{ + private final WXMInterfaceGroupName.Builder builder; + + public WXM1InterfaceGroupParser() + { + this.builder = WXMInterfaceGroupName.builder(); + } + + @Override + public void onElementStart( + final BTElementParsingContextType context, + final Attributes attributes) + throws SAXException + { + try { + this.builder.setValue(attributes.getValue("name")); + } catch (final Exception e) { + throw context.parseException(e); + } + } + + @Override + public WXMInterfaceGroupName onElementFinished( + final BTElementParsingContextType context) + { + return this.builder.build(); + } +} diff --git a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1InterfaceGroups.java b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1InterfaceGroups.java new file mode 100644 index 0000000..7e5fbff --- /dev/null +++ b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1InterfaceGroups.java @@ -0,0 +1,45 @@ +/* + * Copyright © 2020 Mark Raynsford http://io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package com.io7m.waxmill.xml.vm.v1; + +import com.io7m.waxmill.machines.WXMInterfaceGroupName; +import com.io7m.waxmill.xml.WXMSchemas; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import java.util.List; + +public final class WXM1InterfaceGroups +{ + private WXM1InterfaceGroups() + { + + } + + public static void serializeGroups( + final List groups, + final XMLStreamWriter writer) + throws XMLStreamException + { + for (final var group : groups) { + final var namespaceURI = WXMSchemas.vmSchemaV1p0NamespaceText(); + writer.writeStartElement(namespaceURI, "InterfaceGroup"); + writer.writeAttribute("name", group.value()); + writer.writeEndElement(); + } + } +} diff --git a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1TapParser.java b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1TapParser.java index 8a14703..cdfa23a 100644 --- a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1TapParser.java +++ b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1TapParser.java @@ -21,6 +21,7 @@ import com.io7m.blackthorne.api.BTElementParsingContextType; import com.io7m.blackthorne.api.BTQualifiedName; import com.io7m.junreachable.UnreachableCodeException; +import com.io7m.waxmill.machines.WXMInterfaceGroupName; import com.io7m.waxmill.machines.WXMMACAddress; import com.io7m.waxmill.machines.WXMTAPDeviceName; import com.io7m.waxmill.machines.WXMTap; @@ -38,8 +39,7 @@ public final class WXM1TapParser public WXM1TapParser() { - this.builder = - WXMTap.builder(); + this.builder = WXMTap.builder(); } @Override @@ -49,7 +49,12 @@ public WXM1TapParser() { return Map.ofEntries( Map.entry( - element("Comment"), c -> new WXM1CommentParser() + element("Comment"), + c -> new WXM1CommentParser() + ), + Map.entry( + element("InterfaceGroup"), + c -> new WXM1InterfaceGroupParser() ) ); } @@ -61,6 +66,8 @@ public void onChildValueProduced( { if (result instanceof WXM1Comment) { this.builder.setComment(((WXM1Comment) result).text()); + } else if (result instanceof WXMInterfaceGroupName) { + this.builder.addGroups((WXMInterfaceGroupName) result); } else { throw new UnreachableCodeException(); } diff --git a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1VMNetParser.java b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1VMNetParser.java index c180abc..a4468ef 100644 --- a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1VMNetParser.java +++ b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1VMNetParser.java @@ -21,6 +21,7 @@ import com.io7m.blackthorne.api.BTElementParsingContextType; import com.io7m.blackthorne.api.BTQualifiedName; import com.io7m.junreachable.UnreachableCodeException; +import com.io7m.waxmill.machines.WXMInterfaceGroupName; import com.io7m.waxmill.machines.WXMMACAddress; import com.io7m.waxmill.machines.WXMVMNet; import com.io7m.waxmill.machines.WXMVMNetDeviceName; @@ -38,8 +39,7 @@ public final class WXM1VMNetParser public WXM1VMNetParser() { - this.builder = - WXMVMNet.builder(); + this.builder = WXMVMNet.builder(); } @Override @@ -49,7 +49,12 @@ public WXM1VMNetParser() { return Map.ofEntries( Map.entry( - element("Comment"), c -> new WXM1CommentParser() + element("Comment"), + c -> new WXM1CommentParser() + ), + Map.entry( + element("InterfaceGroup"), + c -> new WXM1InterfaceGroupParser() ) ); } @@ -61,6 +66,8 @@ public void onChildValueProduced( { if (result instanceof WXM1Comment) { this.builder.setComment(((WXM1Comment) result).text()); + } else if (result instanceof WXMInterfaceGroupName) { + this.builder.addGroups((WXMInterfaceGroupName) result); } else { throw new UnreachableCodeException(); } diff --git a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1VirtioNetworkDeviceParser.java b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1VirtioNetworkDeviceParser.java index a0f7957..f1b16e7 100644 --- a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1VirtioNetworkDeviceParser.java +++ b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1VirtioNetworkDeviceParser.java @@ -26,7 +26,7 @@ import java.util.Map; -import static com.io7m.waxmill.machines.WXMDeviceType.WXMDeviceVirtioNetworkType.WXMNetworkDeviceBackendType; +import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; import static com.io7m.waxmill.xml.vm.v1.WXM1Names.element; public final class WXM1VirtioNetworkDeviceParser diff --git a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1VirtualMachineSerializer.java b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1VirtualMachineSerializer.java index dfceff9..025c388 100644 --- a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1VirtualMachineSerializer.java +++ b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/vm/v1/WXM1VirtualMachineSerializer.java @@ -409,6 +409,7 @@ private void serializeVMNet( this.writer.writeAttribute("name", backend.name().value()); this.writer.writeAttribute("address", backend.address().value()); WXM1Comments.serializeComment(backend.comment(), this.writer); + WXM1InterfaceGroups.serializeGroups(backend.groups(), this.writer); this.writer.writeEndElement(); } @@ -421,6 +422,7 @@ private void serializeTAP( this.writer.writeAttribute("name", backend.name().value()); this.writer.writeAttribute("address", backend.address().value()); WXM1Comments.serializeComment(backend.comment(), this.writer); + WXM1InterfaceGroups.serializeGroups(backend.groups(), this.writer); this.writer.writeEndElement(); } diff --git a/com.io7m.waxmill.xml/src/main/resources/com/io7m/waxmill/xml/vm/v1/vm-1.0.xsd b/com.io7m.waxmill.xml/src/main/resources/com/io7m/waxmill/xml/vm/v1/vm-1.0.xsd index a022ced..d50bfd4 100644 --- a/com.io7m.waxmill.xml/src/main/resources/com/io7m/waxmill/xml/vm/v1/vm-1.0.xsd +++ b/com.io7m.waxmill.xml/src/main/resources/com/io7m/waxmill/xml/vm/v1/vm-1.0.xsd @@ -43,6 +43,17 @@ + + + + An interface group name. + + + + + + + @@ -388,6 +399,21 @@ + + + + An interface group, in ifconfig(8) terms. A network device can be in any number of groups, and these groups can + be referred to in packet filter rules. + + + + + + + + @@ -396,9 +422,15 @@ - - + + + + + + + - - + + + + + + + + From ec25eb3c9ca2b0b6a1543053f2d2bd193c731c54 Mon Sep 17 00:00:00 2001 From: Mark Raynsford Date: Sun, 19 Jul 2020 12:02:37 +0000 Subject: [PATCH 3/9] Convert grammars to EBNF, for better or worse Affects: https://github.com/io7m/waxmill/issues/24 --- .../waxmill/cmdline/internal/Messages.xml | 52 +++++----- .../waxmill/documentation/address-syntax.txt | 11 +-- .../io7m/waxmill/documentation/cmdline.xml | 95 +++++++++++-------- .../documentation/device-slot-syntax.txt | 10 +- .../io7m/waxmill/documentation/lpc-syntax.txt | 14 +-- .../documentation/network-backend-syntax.txt | 17 ++-- .../documentation/storage-backend-syntax.txt | 12 +-- 7 files changed, 111 insertions(+), 100 deletions(-) diff --git a/com.io7m.waxmill.cmdline/src/main/resources/com/io7m/waxmill/cmdline/internal/Messages.xml b/com.io7m.waxmill.cmdline/src/main/resources/com/io7m/waxmill/cmdline/internal/Messages.xml index 5efd040..0b83d82 100644 --- a/com.io7m.waxmill.cmdline/src/main/resources/com/io7m/waxmill/cmdline/internal/Messages.xml +++ b/com.io7m.waxmill.cmdline/src/main/resources/com/io7m/waxmill/cmdline/internal/Messages.xml @@ -5,13 +5,13 @@ - | "file" ";" ";" - | "nmdm" ";" +EBNF syntax for TTY backends: -Where: - = "com1" | "com2" | "bootrom" + port = "com1" | "com2" | "bootrom" ; + stdioBackend = "stdio" , ";" , port ; + fileBackend = "file" , ";" , port , ";" , path ; + nmdmBackend = "nmdm" , ";" , port ; + ttyBackend = stdioBackend | fileBackend | nmdmBackend ; Examples: stdio;com1 @@ -20,14 +20,13 @@ Examples: ]]> - | "zfs-volume" - | "zfs-volume" ";" +EBNF syntax for storage backends: -Where: - = Any UNIX-like path - = Any non-negative multiple of 128000 (bytes) + path = ? Any UNIX-like path ? ; + size = ? Any non-negative multiple of 128000 (bytes) ? ; + fileBackend = "file" , ";" , path ; + zfsBackend = "zfs-volume" , [ ";" , size ] ; + storageBackend = fileBackend | zfsBackend ; Examples: file;/tmp/xyz @@ -36,14 +35,16 @@ Examples: ]]> ";" - | "vmnet" ";" ";" +EBNF syntax for Virtio network backends: -Where: - = "tap[0-9]'{'1,13'}'" - = "vmnet[0-9]'{'1,11'}'" - = "[a-f0-9]'{'2'}':[a-f0-9]'{'2'}':[a-f0-9]'{'2'}':[a-f0-9]'{'2'}':[a-f0-9]'{'2'}':[a-f0-9]'{'2'}'" + digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; + hexDigit = "a" | "b" | "c" | "d" | "e" | "f" ; + macPart = hexDigit , hexDigit ; + macAddress = macPart , ":" , macPart , ":" , macPart , ":" , macPart , ":" , macPart , ":" , macPart ; + tapDeviceName = "tap" , '{' digit '}' ; + vmnetDeviceName = "vmnet" , '{' digit '}' ; + tapDevice = "tap" , ";" , tapDeviceName , ";" , macAddress ; + vmnetDevice = "vmnet" , ";" , vmnetDeviceName , ";" , macAddress ; Examples: tap;tap23;d9:63:c0:d9:09:e8 @@ -53,13 +54,12 @@ Examples: ]]> = ":" ":" +EBNF syntax for device slots: -Where: - = [0 .. 255] - = [0 .. 31] - = [0 .. 7] + bus = ? [0 .. 255] ? ; + slot = ? [0 .. 31] ? ; + function = ? [0 .. 7] ? ; + deviceSlot = bus , ":" , slot , ":" , function ; Examples: 0:0:0 diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/address-syntax.txt b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/address-syntax.txt index 6dc4876..005b55b 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/address-syntax.txt +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/address-syntax.txt @@ -1,9 +1,8 @@ - | | - -Where: - = Any valid domain/host name - = Any valid IPv4 address - = Any valid IPv6 address +ipv4Part = ? [0 .. 255]{1,3} ? ; +ipv4 = ipv4Part , ".", ipv4Part , "." , ipv4Part , "." , ipv4Part , ; +ipv6 = ? Any https://tools.ietf.org/html/rfc4291 IPv6 address ? ; +name = ? Any host name ? ; +address = ipv4 | ipv6 | name ; Examples: 127.0.0.1 diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline.xml b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline.xml index b886124..3e555ff 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline.xml +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline.xml @@ -6,46 +6,61 @@ tableOfContentsDepth="1">
- - The waxmill package provides a command-line interface and API - for creating, configuring, and running bhyve - virtual machines. The base waxmill command is broken into a number of subcommands - which are documented over the following sections. - - - - - - - - All subcommands accept a --verbose parameter that may be set to one of - trace, debug, info, - warn, or error. This parameter sets the lower bound for - the severity of messages that will be logged. For example, at debug verbosity, only - messages of severity debug and above will be logged. Setting the verbosity to - trace - level effectively causes everything to be logged, and will produce large volumes of debugging output. - - - - - - The waxmill command-line tool uses - jcommander - to parse command-line arguments, and therefore supports placing command-line arguments into a file, - one argument per line, and then referencing that file with @. For example: - - - - - - - - All subcommands, unless otherwise specified, yield an exit code of 0 on success, and - a non-zero exit code on failure. - + + + The waxmill package provides a command-line interface and API + for creating, configuring, and running bhyve + virtual machines. The base waxmill command is broken into a number of subcommands + which are documented over the following sections. + + + + + + + + + + All subcommands accept a --verbose parameter that may be set to one of + trace, debug, info, + warn, or error. This parameter sets the lower bound for + the severity of messages that will be logged. For example, at debug verbosity, only + messages of severity debug and above will be logged. Setting the verbosity to + trace + level effectively causes everything to be logged, and will produce large volumes of debugging output. + + + + + + + + The waxmill command-line tool uses + jcommander + to parse command-line arguments, and therefore supports placing command-line arguments into a file, + one argument per line, and then referencing that file with @. For example: + + + + + + + + + + All subcommands, unless otherwise specified, yield an exit code of 0 on success, and + a non-zero exit code on failure. + + + + + The specification gives grammar definitions in + ISO/IEC 14977:1996 Extended Backus-Naur + form. + +
diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/device-slot-syntax.txt b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/device-slot-syntax.txt index 1d7cb4f..92b6d46 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/device-slot-syntax.txt +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/device-slot-syntax.txt @@ -1,9 +1,7 @@ - = ":" ":" - -Where: - = [0 .. 255] - = [0 .. 31] - = [0 .. 7] +bus = ? [0 .. 255] ? ; +slot = ? [0 .. 31] ? ; +function = ? [0 .. 7] ? ; +deviceSlot = bus , ":" , slot , ":" , function ; Examples: 0:0:0 diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/lpc-syntax.txt b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/lpc-syntax.txt index c135ed9..3890f18 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/lpc-syntax.txt +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/lpc-syntax.txt @@ -1,11 +1,11 @@ - "stdio" ";" -| "file" ";" ";" -| "nmdm" ";" - -Where: - = "com1" | "com2" | "bootrom" +path = ? Any unix-like path ? ; +port = "com1" | "com2" | "bootrom" ; +stdioBackend = "stdio" , ";" , port ; +fileBackend = "file" , ";" , port , ";" , path ; +nmdmBackend = "nmdm" , ";" , port ; +ttyBackend = stdioBackend | fileBackend | nmdmBackend ; Examples: stdio;com1 file;bootrom;/tmp/xyz - nmdm;com2 \ No newline at end of file + nmdm;com2 diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/network-backend-syntax.txt b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/network-backend-syntax.txt index 00e6d0e..4f404ef 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/network-backend-syntax.txt +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/network-backend-syntax.txt @@ -1,13 +1,14 @@ - "tap" ";" ";" -| "vmnet" ";" ";" - -Where: - = "tap[0-9]'{'1,13'}'" - = "vmnet[0-9]'{'1,11'}'" - = "[a-f0-9]'{'2'}':[a-f0-9]'{'2'}':[a-f0-9]'{'2'}':[a-f0-9]'{'2'}':[a-f0-9]'{'2'}':[a-f0-9]'{'2'}'" +digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; +hexDigit = "a" | "b" | "c" | "d" | "e" | "f" ; +macPart = hexDigit , hexDigit ; +macAddress = macPart , ":" , macPart , ":" , macPart , ":" , macPart , ":" , macPart , ":" , macPart ; +tapDeviceName = "tap" , { digit } ; +vmnetDeviceName = "vmnet" , { digit } ; +tapDevice = "tap" , ";" , tapDeviceName , ";" , macAddress ; +vmnetDevice = "vmnet" , ";" , vmnetDeviceName , ";" , macAddress ; Examples: tap;tap23;d9:63:c0:d9:09:e8 tap;tap0000000000001;d9:63:c0:d9:09:e8 vmnet;vmnet23;d9:63:c0:d9:09:e8 - vmnet;vmnet00000000001;d9:63:c0:d9:09:e8 \ No newline at end of file + vmnet;vmnet00000000001;d9:63:c0:d9:09:e8 diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/storage-backend-syntax.txt b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/storage-backend-syntax.txt index 81421e8..5f2f63b 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/storage-backend-syntax.txt +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/storage-backend-syntax.txt @@ -1,10 +1,8 @@ - "file" ";" -| "zfs-volume" -| "zfs-volume" ";" - -Where: - = Any UNIX-like path - = Any non-negative multiple of 128000 (bytes) +path = ? Any UNIX-like path ? ; +size = ? Any non-negative multiple of 128000 (bytes) ? ; +fileBackend = "file" , ";" , path ; +zfsBackend = "zfs-volume" , [ ";" , size ] ; +storageBackend = fileBackend | zfsBackend ; Examples: file;/tmp/xyz From 3ef9a2f4bca722311c5c57134f53aaecfb99aad5 Mon Sep 17 00:00:00 2001 From: Mark Raynsford Date: Sun, 19 Jul 2020 14:35:24 +0000 Subject: [PATCH 4/9] Execute ifconfig commands to set up tap/vmnet devices This execute various ifconfig commands prior to running a virtual machine. Firstly, the tap/vmnet device is created with "ifconfig create". This step is allowed to fail; the device may already exists. Secondly, the MAC address of the device is configured. This step is not allowed to fail; if it fails, the device probably does not exist. Lastly, the device is added to all of the groups defined, if any are defined. These steps aren't allowed to fail. Affects: https://github.com/io7m/waxmill/issues/24 --- .../boot/WXMBootConfigurationEvaluator.java | 131 +++++- .../boot/WXMBootConfigurationExecutor.java | 8 +- .../api/WXMClientConfigurationType.java | 18 + .../internal/WXMNetworkBackendConverter.java | 81 +++- .../waxmill/cmdline/internal/Messages.xml | 12 +- .../waxmill/documentation/config-example.xml | 2 + .../documentation/network-backend-syntax.txt | 16 +- .../machines/WXMCommandExecutionType.java | 10 + .../tests/WXMNetworkBackendConverterTest.java | 68 +++- ...WXMBootConfigurationEvaluatorGRUBTest.java | 372 +++++++++++++++++- .../WXMCommandVMAddVirtioNetworkTest.java | 64 ++- .../v1/WXM1ClientConfigurationParser.java | 4 + .../v1/WXM1ClientConfigurationSerializer.java | 4 + .../io7m/waxmill/xml/config/v1/config-1.0.xsd | 7 + spotbugs-filter.xml | 21 + 15 files changed, 780 insertions(+), 38 deletions(-) diff --git a/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationEvaluator.java b/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationEvaluator.java index 12a7605..9eaa7cc 100644 --- a/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationEvaluator.java +++ b/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationEvaluator.java @@ -47,6 +47,7 @@ import com.io7m.waxmill.machines.WXMEvaluatedBootConfigurationUEFI; import com.io7m.waxmill.machines.WXMGRUBKernelLinux; import com.io7m.waxmill.machines.WXMGRUBKernelOpenBSD; +import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; import com.io7m.waxmill.machines.WXMOpenOption; import com.io7m.waxmill.machines.WXMStorageBackendFile; import com.io7m.waxmill.machines.WXMStorageBackends; @@ -70,8 +71,6 @@ import java.util.stream.Collectors; import static com.io7m.waxmill.machines.WXMBootConfigurationType.WXMEvaluatedBootConfigurationType; - -import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; import static com.io7m.waxmill.machines.WXMDeviceType.WXMDeviceVirtioNetworkType.WXMTTYBackendType; import static com.io7m.waxmill.machines.WXMDeviceType.WXMStorageBackendType; import static com.io7m.waxmill.machines.WXMTTYBackends.NMDMSide.NMDM_HOST; @@ -369,11 +368,119 @@ private WXMEvaluatedBootCommands generateGRUBBhyveCommands( this.generateBhyveCommand(bootConfiguration, attachments); return WXMEvaluatedBootCommands.builder() + .addAllConfigurationCommands(this.networkDeviceCommands()) .addConfigurationCommands(grubBhyveCommand) .setLastExecution(bhyve) .build(); } + private Iterable networkDeviceCommands() + { + final var commands = new ArrayList(); + + for (final var device : this.machine.devices()) { + switch (device.kind()) { + case WXM_HOSTBRIDGE: + case WXM_VIRTIO_BLOCK: + case WXM_AHCI_HD: + case WXM_AHCI_CD: + case WXM_LPC: + case WXM_PASSTHRU: + case WXM_FRAMEBUFFER: + break; + + case WXM_E1000: { + final var e1000 = (WXMDeviceE1000) device; + this.networkDeviceBackendCommands(commands, e1000.backend()); + break; + } + case WXM_VIRTIO_NETWORK: { + final var vio = (WXMDeviceVirtioNetwork) device; + this.networkDeviceBackendCommands(commands, vio.backend()); + break; + } + } + } + return commands; + } + + private void networkDeviceBackendCommands( + final ArrayList commands, + final WXMNetworkDeviceBackendType backend) + { + final var ifconfig = this.clientConfiguration.ifconfigExecutable(); + + switch (backend.kind()) { + case WXM_TAP: { + final var tap = (WXMTap) backend; + final var tapName = tap.name().value(); + commands.add( + WXMCommandExecution.builder() + .setExecutable(ifconfig) + .addArguments(tapName) + .addArguments("create") + .setIgnoreFailure(true) + .build() + ); + + commands.add( + WXMCommandExecution.builder() + .setExecutable(ifconfig) + .addArguments(tapName) + .addArguments("ether") + .addArguments(tap.address().value()) + .build() + ); + + for (final var group : tap.groups()) { + commands.add( + WXMCommandExecution.builder() + .setExecutable(ifconfig) + .addArguments(tapName) + .addArguments("group") + .addArguments(group.value()) + .build() + ); + } + break; + } + case WXM_VMNET: { + final var vmNet = (WXMVMNet) backend; + final String vmNetName = vmNet.name().value(); + + commands.add( + WXMCommandExecution.builder() + .setExecutable(ifconfig) + .addArguments(vmNetName) + .addArguments("create") + .setIgnoreFailure(true) + .build() + ); + + commands.add( + WXMCommandExecution.builder() + .setExecutable(ifconfig) + .addArguments(vmNetName) + .addArguments("ether") + .addArguments(vmNet.address().value()) + .build() + ); + + for (final var group : vmNet.groups()) { + commands.add( + WXMCommandExecution.builder() + .setExecutable(ifconfig) + .addArguments(vmNetName) + .addArguments("group") + .addArguments(group.value()) + .build() + ); + } + break; + } + } + } + private WXMCommandExecution generateGRUBBhyveCommand() { final var machineId = @@ -750,7 +857,7 @@ private void configureBhyveFlags( } private WXMEvaluatedBootConfigurationGRUBBhyve evaluateGRUBConfigurationOpenBSD( - final WXMBootConfigurationType bootConfiguration, + final WXMBootConfigurationType config, final WXMDeviceMap deviceMap, final WXMGRUBKernelOpenBSD openBSD) { @@ -758,11 +865,11 @@ private WXMEvaluatedBootConfigurationGRUBBhyve evaluateGRUBConfigurationOpenBSD( final var configLines = generateGRUBConfigLinesOpenBSD(deviceMap, openBSD); + final var commands = + this.generateGRUBBhyveCommands(config, deviceMap.attachments()); return WXMEvaluatedBootConfigurationGRUBBhyve.builder() - .setCommands(this.generateGRUBBhyveCommands( - bootConfiguration, - deviceMap.attachments())) + .setCommands(commands) .setRequiredPaths(deviceMap.paths()) .setDeviceMap(deviceMap.serialize()) .setDeviceMapFile(this.grubDeviceMapPath()) @@ -882,24 +989,26 @@ private WXMEvaluatedBootCommands generateUEFICommands( this.generateBhyveCommand(bootConfiguration, attachments); return WXMEvaluatedBootCommands.builder() + .addAllConfigurationCommands(this.networkDeviceCommands()) .setLastExecution(bhyve) .build(); } private WXMEvaluatedBootConfigurationUEFI evaluateUEFIConfiguration( - final WXMBootConfigurationUEFI configuration, + final WXMBootConfigurationUEFI config, final WXMDeviceMap deviceMap) { final var requiredPaths = new ArrayList(); - requiredPaths.add(configuration.firmware()); + requiredPaths.add(config.firmware()); requiredPaths.addAll(deviceMap.paths()); + final var commands = + this.generateUEFICommands(config, config.diskAttachmentMap()); + return WXMEvaluatedBootConfigurationUEFI.builder() .setRequiredPaths(requiredPaths) .setRequiredNMDMs(deviceMap.nmdmPaths()) - .setCommands(this.generateUEFICommands( - configuration, - configuration.diskAttachmentMap())) + .setCommands(commands) .build(); } diff --git a/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationExecutor.java b/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationExecutor.java index 5fd1b19..bbefa4d 100644 --- a/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationExecutor.java +++ b/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationExecutor.java @@ -301,7 +301,13 @@ private void executeAndWait( .addAllArguments(command.arguments()) .build(); - this.processes.processStartAndWait(processDescription); + try { + this.processes.processStartAndWait(processDescription); + } catch (final Exception e) { + if (!command.ignoreFailure()) { + throw e; + } + } } private String errorRequiredPathsMissing( diff --git a/com.io7m.waxmill.client.api/src/main/java/com/io7m/waxmill/client/api/WXMClientConfigurationType.java b/com.io7m.waxmill.client.api/src/main/java/com/io7m/waxmill/client/api/WXMClientConfigurationType.java index 7f8cb04..7a155ed 100644 --- a/com.io7m.waxmill.client.api/src/main/java/com/io7m/waxmill/client/api/WXMClientConfigurationType.java +++ b/com.io7m.waxmill.client.api/src/main/java/com/io7m/waxmill/client/api/WXMClientConfigurationType.java @@ -97,6 +97,18 @@ default Path zfsExecutable() .getPath("/sbin/zfs"); } + /** + * @return The "ifconfig" executable path, such as {@code /sbin/ifconfig} + */ + + @Value.Default + default Path ifconfigExecutable() + { + return this.virtualMachineConfigurationDirectory() + .getFileSystem() + .getPath("/sbin/ifconfig"); + } + /** * @return The "cu" executable path, such as {@code /usr/bin/cu} */ @@ -152,6 +164,12 @@ default void checkPreconditions() path -> "zfs executable path must be absolute" ); + Preconditions.checkPrecondition( + this.ifconfigExecutable(), + Path::isAbsolute, + path -> "ifconfig executable path must be absolute" + ); + Preconditions.checkPrecondition( this.virtualMachineConfigurationDirectory(), Path::isAbsolute, diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMNetworkBackendConverter.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMNetworkBackendConverter.java index e835ea1..664eb70 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMNetworkBackendConverter.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMNetworkBackendConverter.java @@ -17,6 +17,7 @@ package com.io7m.waxmill.cmdline.internal; import com.beust.jcommander.IStringConverter; +import com.io7m.waxmill.machines.WXMInterfaceGroupName; import com.io7m.waxmill.machines.WXMMACAddress; import com.io7m.waxmill.machines.WXMTAPDeviceName; import com.io7m.waxmill.machines.WXMTap; @@ -25,6 +26,9 @@ import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; +import java.util.List; +import java.util.stream.Collectors; + public final class WXMNetworkBackendConverter implements IStringConverter { @@ -49,23 +53,90 @@ public WXMNetworkDeviceBackendType convert( } switch (segments[0]) { - case "tap": { - return WXMTap.builder() - .setName(WXMTAPDeviceName.of(segments[1])) + case "tap": + return this.convertTAP(value, segments); + case "vmnet": + return this.convertVMNet(value, segments); + default: + throw this.syntaxError(value); + } + } + + private WXMNetworkDeviceBackendType convertVMNet( + final String value, + final String[] segments) + { + switch (segments.length) { + case 3: { + return WXMVMNet.builder() + .setName(WXMVMNetDeviceName.of(segments[1])) .setAddress(WXMMACAddress.of(segments[2])) .build(); } - case "vmnet": { + case 4: { return WXMVMNet.builder() .setName(WXMVMNetDeviceName.of(segments[1])) .setAddress(WXMMACAddress.of(segments[2])) + .addAllGroups(this.groupsOf(segments[3])) .build(); } - default: + default: { throw this.syntaxError(value); + } + } + } + + private Iterable groupsOf( + final String text) + { + try { + return List.of(text.split(",")) + .stream() + .map(WXMInterfaceGroupName::of) + .collect(Collectors.toList()); + } catch (final Exception e) { + throw this.syntaxErrorWith(e, text); } } + private WXMNetworkDeviceBackendType convertTAP( + final String value, + final String[] segments) + { + switch (segments.length) { + case 3: { + return WXMTap.builder() + .setName(WXMTAPDeviceName.of(segments[1])) + .setAddress(WXMMACAddress.of(segments[2])) + .build(); + } + case 4: { + return WXMTap.builder() + .setName(WXMTAPDeviceName.of(segments[1])) + .setAddress(WXMMACAddress.of(segments[2])) + .addAllGroups(this.groupsOf(segments[3])) + .build(); + } + default: { + throw this.syntaxError(value); + } + } + } + + private IllegalArgumentException syntaxErrorWith( + final Exception exception, + final String value) + { + return new IllegalArgumentException( + this.messages.format( + "errorInvalidVirtioNetworkBackend", + this.messages.format("networkBackendSpec"), + value + ), + exception + ); + } + private IllegalArgumentException syntaxError( final String value) { diff --git a/com.io7m.waxmill.cmdline/src/main/resources/com/io7m/waxmill/cmdline/internal/Messages.xml b/com.io7m.waxmill.cmdline/src/main/resources/com/io7m/waxmill/cmdline/internal/Messages.xml index 0b83d82..3bca1b1 100644 --- a/com.io7m.waxmill.cmdline/src/main/resources/com/io7m/waxmill/cmdline/internal/Messages.xml +++ b/com.io7m.waxmill.cmdline/src/main/resources/com/io7m/waxmill/cmdline/internal/Messages.xml @@ -39,18 +39,26 @@ EBNF syntax for Virtio network backends: digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; hexDigit = "a" | "b" | "c" | "d" | "e" | "f" ; + lowerAlpha = ? [a-z] ? ; + upperAlpha = ? [A-Z] ? ; + groupCharacter = lowerAlpha | upperAlpha | "_" ; + groupName = '{' groupCharacter '}' ; + groupList = groupName , '{' "," , groupName '}' ; + macPart = hexDigit , hexDigit ; macAddress = macPart , ":" , macPart , ":" , macPart , ":" , macPart , ":" , macPart , ":" , macPart ; tapDeviceName = "tap" , '{' digit '}' ; vmnetDeviceName = "vmnet" , '{' digit '}' ; - tapDevice = "tap" , ";" , tapDeviceName , ";" , macAddress ; - vmnetDevice = "vmnet" , ";" , vmnetDeviceName , ";" , macAddress ; + tapDevice = "tap" , ";" , tapDeviceName , ";" , macAddress , [ ";" groupList ] ; + vmnetDevice = "vmnet" , ";" , vmnetDeviceName , ";" , macAddress , [ ";" groupList ] ; Examples: tap;tap23;d9:63:c0:d9:09:e8 tap;tap0000000000001;d9:63:c0:d9:09:e8 + tap;tap23;d9:63:c0:d9:09:e8;wwwUsers,mailUsers,ntpdUsers vmnet;vmnet23;d9:63:c0:d9:09:e8 vmnet;vmnet00000000001;d9:63:c0:d9:09:e8 + vmnet;vmnet23;d9:63:c0:d9:09:e8;wwwUsers,mailUsers,ntpdUsers ]]>
+ \ No newline at end of file diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/network-backend-syntax.txt b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/network-backend-syntax.txt index 4f404ef..39dd9d9 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/network-backend-syntax.txt +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/network-backend-syntax.txt @@ -1,14 +1,22 @@ digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; hexDigit = "a" | "b" | "c" | "d" | "e" | "f" ; +lowerAlpha = ? [a-z] ? ; +upperAlpha = ? [A-Z] ? ; +groupCharacter = lowerAlpha | upperAlpha | "_" ; +groupName = { groupCharacter } ; +groupList = groupName , { "," , groupName } ; + macPart = hexDigit , hexDigit ; macAddress = macPart , ":" , macPart , ":" , macPart , ":" , macPart , ":" , macPart , ":" , macPart ; -tapDeviceName = "tap" , { digit } ; -vmnetDeviceName = "vmnet" , { digit } ; -tapDevice = "tap" , ";" , tapDeviceName , ";" , macAddress ; -vmnetDevice = "vmnet" , ";" , vmnetDeviceName , ";" , macAddress ; +tapDeviceName = "tap" , '{' digit '}' ; +vmnetDeviceName = "vmnet" , '{' digit '}' ; +tapDevice = "tap" , ";" , tapDeviceName , ";" , macAddress , [ ";" groupList ] ; +vmnetDevice = "vmnet" , ";" , vmnetDeviceName , ";" , macAddress , [ ";" groupList ] ; Examples: tap;tap23;d9:63:c0:d9:09:e8 tap;tap0000000000001;d9:63:c0:d9:09:e8 + tap;tap23;d9:63:c0:d9:09:e8;wwwUsers,mailUsers,ntpdUsers vmnet;vmnet23;d9:63:c0:d9:09:e8 vmnet;vmnet00000000001;d9:63:c0:d9:09:e8 + vmnet;vmnet23;d9:63:c0:d9:09:e8;wwwUsers,mailUsers,ntpdUsers diff --git a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMCommandExecutionType.java b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMCommandExecutionType.java index 5baf710..7388b0b 100644 --- a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMCommandExecutionType.java +++ b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMCommandExecutionType.java @@ -43,6 +43,16 @@ public abstract class WXMCommandExecutionType abstract List arguments(); + /** + * @return If the failure of this command should be ignored + */ + + @Value.Default + boolean ignoreFailure() + { + return false; + } + /** * Check preconditions for the type. */ diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/WXMNetworkBackendConverterTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/WXMNetworkBackendConverterTest.java index 4da40ca..5b46944 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/WXMNetworkBackendConverterTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/WXMNetworkBackendConverterTest.java @@ -27,7 +27,7 @@ public final class WXMNetworkBackendConverterTest { @Test - public void tapIsOK() + public void tapIsOK0() { final var result = (WXMTap) new WXMNetworkBackendConverter() @@ -38,7 +38,21 @@ public void tapIsOK() } @Test - public void vmnetIsOK() + public void tapIsOK1() + { + final var result = + (WXMTap) new WXMNetworkBackendConverter() + .convert("tap;tap23;f8:e1:e1:79:c9:7e;x,y,z"); + + assertEquals("tap23", result.name().value()); + assertEquals("f8:e1:e1:79:c9:7e", result.address().value()); + assertEquals("x", result.groups().get(0).value()); + assertEquals("y", result.groups().get(1).value()); + assertEquals("z", result.groups().get(2).value()); + } + + @Test + public void vmnetIsOK0() { final var result = (WXMVMNet) new WXMNetworkBackendConverter() @@ -48,6 +62,20 @@ public void vmnetIsOK() assertEquals("f8:e1:e1:79:c9:7e", result.address().value()); } + @Test + public void vmnetIsOK1() + { + final var result = + (WXMVMNet) new WXMNetworkBackendConverter() + .convert("vmnet;vmnet23;f8:e1:e1:79:c9:7e;x,y,z"); + + assertEquals("vmnet23", result.name().value()); + assertEquals("f8:e1:e1:79:c9:7e", result.address().value()); + assertEquals("x", result.groups().get(0).value()); + assertEquals("y", result.groups().get(1).value()); + assertEquals("z", result.groups().get(2).value()); + } + @Test public void syntaxError0() { @@ -84,6 +112,24 @@ public void syntaxErrorTap1() }); } + @Test + public void syntaxErrorTap2() + { + assertThrows(IllegalArgumentException.class, () -> { + new WXMNetworkBackendConverter() + .convert("tap;tap23;f8:e1:e1:79:c9:7e;w0"); + }); + } + + @Test + public void syntaxErrorTap3() + { + assertThrows(IllegalArgumentException.class, () -> { + new WXMNetworkBackendConverter() + .convert("tap;tap23;f8:e1:e1:79:c9:7e;w;x"); + }); + } + @Test public void syntaxErrorVMNet0() { @@ -101,4 +147,22 @@ public void syntaxErrorVMNet1() .convert("vmnet;vmnet23"); }); } + + @Test + public void syntaxErrorVMNet2() + { + assertThrows(IllegalArgumentException.class, () -> { + new WXMNetworkBackendConverter() + .convert("vmnet;vmnet23;f8:e1:e1:79:c9:7e;w0"); + }); + } + + @Test + public void syntaxErrorVMNet3() + { + assertThrows(IllegalArgumentException.class, () -> { + new WXMNetworkBackendConverter() + .convert("vmnet;vmnet23;f8:e1:e1:79:c9:7e;w;x"); + }); + } } diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/boot/WXMBootConfigurationEvaluatorGRUBTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/boot/WXMBootConfigurationEvaluatorGRUBTest.java index 5743d93..11b6d8a 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/boot/WXMBootConfigurationEvaluatorGRUBTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/boot/WXMBootConfigurationEvaluatorGRUBTest.java @@ -36,6 +36,7 @@ import com.io7m.waxmill.machines.WXMFlags; import com.io7m.waxmill.machines.WXMGRUBKernelLinux; import com.io7m.waxmill.machines.WXMGRUBKernelOpenBSD; +import com.io7m.waxmill.machines.WXMInterfaceGroupName; import com.io7m.waxmill.machines.WXMMACAddress; import com.io7m.waxmill.machines.WXMMachineName; import com.io7m.waxmill.machines.WXMSectorSizes; @@ -59,6 +60,8 @@ import java.math.BigInteger; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.UUID; @@ -747,10 +750,171 @@ public void linuxVirtioNetTAP() assertEquals(3, grub.size()); final var commands = evaluated.commands(); - final var configs = commands.configurationCommands(); - final var cmd0 = configs.get(0); - assertEquals("/usr/local/sbin/grub-bhyve", cmd0.executable().toString()); - assertEquals(1, configs.size()); + final var configs = new ArrayList<>(commands.configurationCommands()); + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("tap23", arguments.get(0)); + assertEquals("create", arguments.get(1)); + assertEquals(2, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("tap23", arguments.get(0)); + assertEquals("ether", arguments.get(1)); + assertEquals("1b:61:cb:ba:c0:12", arguments.get(2)); + assertEquals(3, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/usr/local/sbin/grub-bhyve", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals(5, arguments.size()); + } + + final var lastExec = commands.lastExecution().orElseThrow(); + assertEquals( + String.format( + "/usr/sbin/bhyve -P -A -H -c cpus=1,sockets=1,cores=1,threads=1 -m 512M -s 0:0:0,hostbridge -s 0:1:0,ahci-hd,/tmp/file -s 0:2:0,virtio-net,tap23,mac=1b:61:cb:ba:c0:12 %s", + machine.id()), + lastExec.toString() + ); + } + + @Test + public void linuxVirtioNetTAPGroups() + throws WXMException + { + final var machine = + WXMVirtualMachine.builder() + .setId(UUID.randomUUID()) + .setName(WXMMachineName.of("vm")) + .addBootConfigurations( + WXMBootConfigurationGRUBBhyve.builder() + .setName(WXMBootConfigurationName.of("install")) + .setKernelInstructions( + WXMGRUBKernelLinux.builder() + .setKernelDevice(convert("0:1:0")) + .setKernelPath(Paths.get("/vmlinuz")) + .addKernelArguments("root=/dev/sda1") + .addKernelArguments("init=/sbin/runit-init") + .setInitRDDevice(convert("0:1:0")) + .setInitRDPath(Paths.get("/initrd.img")) + .build()) + .build() + ) + .addDevices( + WXMDeviceHostBridge.builder() + .setDeviceSlot(convert("0:0:0")) + .setVendor(WXM_UNSPECIFIED) + .build() + ) + .addDevices( + WXMDeviceAHCIDisk.builder() + .setDeviceSlot(convert("0:1:0")) + .setBackend( + WXMStorageBackendFile.builder() + .setFile(Path.of("/tmp/file")) + .build() + ).build() + ) + .addDevices( + WXMDeviceVirtioNetwork.builder() + .setDeviceSlot(convert("0:2:0")) + .setBackend( + WXMTap.builder() + .setAddress(WXMMACAddress.of("1b:61:cb:ba:c0:12")) + .setName(WXMTAPDeviceName.of("tap23")) + .addGroups(WXMInterfaceGroupName.of("wwwUsers")) + .addGroups(WXMInterfaceGroupName.of("ntpUsers")) + .build()) + .build() + ).build(); + + final var clientConfiguration = + WXMClientConfiguration.builder() + .setVirtualMachineConfigurationDirectory(this.configs) + .setVirtualMachineRuntimeDirectory(this.vms) + .build(); + + final var evaluator = + new WXMBootConfigurationEvaluator( + clientConfiguration, + machine, + WXMBootConfigurationName.of("install") + ); + + final var evaluated = + (WXMEvaluatedBootConfigurationGRUBBhyve) evaluator.evaluate(); + LOG.debug("evaluated: {}", evaluated); + + final var mapLines = evaluated.deviceMap(); + assertTrue(mapLines.get(0).contains("/tmp/file")); + assertEquals(1, mapLines.size()); + + final var grub = evaluated.grubConfiguration(); + assertEquals( + "linux (hd0)/vmlinuz root=/dev/sda1 init=/sbin/runit-init", + grub.get(0) + ); + assertEquals("initrd (hd0)/initrd.img", grub.get(1)); + assertEquals("boot", grub.get(2)); + assertEquals(3, grub.size()); + + final var commands = evaluated.commands(); + final var configs = new ArrayList<>(commands.configurationCommands()); + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("tap23", arguments.get(0)); + assertEquals("create", arguments.get(1)); + assertEquals(2, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("tap23", arguments.get(0)); + assertEquals("ether", arguments.get(1)); + assertEquals("1b:61:cb:ba:c0:12", arguments.get(2)); + assertEquals(3, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("tap23", arguments.get(0)); + assertEquals("group", arguments.get(1)); + assertEquals("wwwUsers", arguments.get(2)); + assertEquals(3, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("tap23", arguments.get(0)); + assertEquals("group", arguments.get(1)); + assertEquals("ntpUsers", arguments.get(2)); + assertEquals(3, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/usr/local/sbin/grub-bhyve", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals(5, arguments.size()); + } final var lastExec = commands.lastExecution().orElseThrow(); assertEquals( @@ -840,10 +1004,171 @@ public void linuxVirtioNetVMNet() assertEquals(3, grub.size()); final var commands = evaluated.commands(); - final var configs = commands.configurationCommands(); - final var cmd0 = configs.get(0); - assertEquals("/usr/local/sbin/grub-bhyve", cmd0.executable().toString()); - assertEquals(1, configs.size()); + final var configs = new ArrayList<>(commands.configurationCommands()); + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("vmnet23", arguments.get(0)); + assertEquals("create", arguments.get(1)); + assertEquals(2, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("vmnet23", arguments.get(0)); + assertEquals("ether", arguments.get(1)); + assertEquals("1b:61:cb:ba:c0:12", arguments.get(2)); + assertEquals(3, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/usr/local/sbin/grub-bhyve", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals(5, arguments.size()); + } + + final var lastExec = commands.lastExecution().orElseThrow(); + assertEquals( + String.format( + "/usr/sbin/bhyve -P -A -H -c cpus=1,sockets=1,cores=1,threads=1 -m 512M -s 0:0:0,hostbridge -s 0:1:0,ahci-hd,/tmp/file -s 0:2:0,virtio-net,vmnet23,mac=1b:61:cb:ba:c0:12 %s", + machine.id()), + lastExec.toString() + ); + } + + @Test + public void linuxVirtioNetVMNetGroups() + throws WXMException + { + final var machine = + WXMVirtualMachine.builder() + .setId(UUID.randomUUID()) + .setName(WXMMachineName.of("vm")) + .addBootConfigurations( + WXMBootConfigurationGRUBBhyve.builder() + .setName(WXMBootConfigurationName.of("install")) + .setKernelInstructions( + WXMGRUBKernelLinux.builder() + .setKernelDevice(convert("0:1:0")) + .setKernelPath(Paths.get("/vmlinuz")) + .addKernelArguments("root=/dev/sda1") + .addKernelArguments("init=/sbin/runit-init") + .setInitRDDevice(convert("0:1:0")) + .setInitRDPath(Paths.get("/initrd.img")) + .build()) + .build() + ) + .addDevices( + WXMDeviceHostBridge.builder() + .setDeviceSlot(convert("0:0:0")) + .setVendor(WXM_UNSPECIFIED) + .build() + ) + .addDevices( + WXMDeviceAHCIDisk.builder() + .setDeviceSlot(convert("0:1:0")) + .setBackend( + WXMStorageBackendFile.builder() + .setFile(Path.of("/tmp/file")) + .build() + ).build() + ) + .addDevices( + WXMDeviceVirtioNetwork.builder() + .setDeviceSlot(convert("0:2:0")) + .setBackend( + WXMVMNet.builder() + .setAddress(WXMMACAddress.of("1b:61:cb:ba:c0:12")) + .setName(WXMVMNetDeviceName.of("vmnet23")) + .addGroups(WXMInterfaceGroupName.of("wwwUsers")) + .addGroups(WXMInterfaceGroupName.of("ntpUsers")) + .build()) + .build() + ).build(); + + final var clientConfiguration = + WXMClientConfiguration.builder() + .setVirtualMachineConfigurationDirectory(this.configs) + .setVirtualMachineRuntimeDirectory(this.vms) + .build(); + + final var evaluator = + new WXMBootConfigurationEvaluator( + clientConfiguration, + machine, + WXMBootConfigurationName.of("install") + ); + + final var evaluated = + (WXMEvaluatedBootConfigurationGRUBBhyve) evaluator.evaluate(); + LOG.debug("evaluated: {}", evaluated); + + final var mapLines = evaluated.deviceMap(); + assertTrue(mapLines.get(0).contains("/tmp/file")); + assertEquals(1, mapLines.size()); + + final var grub = evaluated.grubConfiguration(); + assertEquals( + "linux (hd0)/vmlinuz root=/dev/sda1 init=/sbin/runit-init", + grub.get(0) + ); + assertEquals("initrd (hd0)/initrd.img", grub.get(1)); + assertEquals("boot", grub.get(2)); + assertEquals(3, grub.size()); + + final var commands = evaluated.commands(); + final var configs = new ArrayList<>(commands.configurationCommands()); + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("vmnet23", arguments.get(0)); + assertEquals("create", arguments.get(1)); + assertEquals(2, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("vmnet23", arguments.get(0)); + assertEquals("ether", arguments.get(1)); + assertEquals("1b:61:cb:ba:c0:12", arguments.get(2)); + assertEquals(3, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("vmnet23", arguments.get(0)); + assertEquals("group", arguments.get(1)); + assertEquals("wwwUsers", arguments.get(2)); + assertEquals(3, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("vmnet23", arguments.get(0)); + assertEquals("group", arguments.get(1)); + assertEquals("ntpUsers", arguments.get(2)); + assertEquals(3, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/usr/local/sbin/grub-bhyve", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals(5, arguments.size()); + } final var lastExec = commands.lastExecution().orElseThrow(); assertEquals( @@ -933,10 +1258,33 @@ public void linuxE1000Net() assertEquals(3, grub.size()); final var commands = evaluated.commands(); - final var configs = commands.configurationCommands(); - final var cmd0 = configs.get(0); - assertEquals("/usr/local/sbin/grub-bhyve", cmd0.executable().toString()); - assertEquals(1, configs.size()); + final var configs = new ArrayList<>(commands.configurationCommands()); + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("vmnet23", arguments.get(0)); + assertEquals("create", arguments.get(1)); + assertEquals(2, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/sbin/ifconfig", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals("vmnet23", arguments.get(0)); + assertEquals("ether", arguments.get(1)); + assertEquals("1b:61:cb:ba:c0:12", arguments.get(2)); + assertEquals(3, arguments.size()); + } + + { + final var cmd = configs.remove(0); + assertEquals("/usr/local/sbin/grub-bhyve", cmd.executable().toString()); + final var arguments = cmd.arguments(); + assertEquals(5, arguments.size()); + } final var lastExec = commands.lastExecution().orElseThrow(); assertEquals( diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddVirtioNetworkTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddVirtioNetworkTest.java index c95d844..f62a6f2 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddVirtioNetworkTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddVirtioNetworkTest.java @@ -152,7 +152,7 @@ public void addVirtioNetworkAlreadyUsed() } @Test - public void addVirtioNetworkOKTap() + public void addVirtioNetworkOKTap0() throws Exception { final var id = UUID.randomUUID(); @@ -211,6 +211,68 @@ public void addVirtioNetworkOKTap() assertEquals("a3:26:9c:74:79:34", tap.address().value()); } + @Test + public void addVirtioNetworkOKTap1() + throws Exception + { + final var id = UUID.randomUUID(); + + MainExitless.main( + new String[]{ + "vm-define", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--name", + "com.io7m.example", + "--memory-gigabytes", + "1", + "--memory-megabytes", + "128", + "--cpu-count", + "2", + "--machine", + id.toString() + } + ); + + MainExitless.main( + new String[]{ + "vm-add-virtio-network-device", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--backend", + "tap;tap23;a3:26:9c:74:79:34;wwwUsers,ntpdUsers", + "--device-slot", + "0:1:0" + } + ); + + final var machineSet = + parseFirst(this.vmDirectory); + + final var machine = + machineSet.machines() + .values() + .iterator() + .next(); + + final var net = + (WXMDeviceVirtioNetwork) machine.devices().get(1); + final var tap = + (WXMTap) net.backend(); + + assertEquals("tap23", tap.name().value()); + assertEquals("a3:26:9c:74:79:34", tap.address().value()); + assertEquals("wwwUsers", tap.groups().get(0).value()); + assertEquals("ntpdUsers", tap.groups().get(1).value()); + } + @Test public void addVirtioNetworkOKVMNet() throws Exception diff --git a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/config/v1/WXM1ClientConfigurationParser.java b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/config/v1/WXM1ClientConfigurationParser.java index 3646eab..a19deec 100644 --- a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/config/v1/WXM1ClientConfigurationParser.java +++ b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/config/v1/WXM1ClientConfigurationParser.java @@ -104,6 +104,10 @@ private void onPathsReceived( this.builder.setZfsExecutable(path.path()); break; } + case "IfconfigExecutable": { + this.builder.setIfconfigExecutable(path.path()); + break; + } case "CuExecutable": { this.builder.setCuExecutable(path.path()); break; diff --git a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/config/v1/WXM1ClientConfigurationSerializer.java b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/config/v1/WXM1ClientConfigurationSerializer.java index 6878f95..916cd30 100644 --- a/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/config/v1/WXM1ClientConfigurationSerializer.java +++ b/com.io7m.waxmill.xml/src/main/java/com/io7m/waxmill/xml/config/v1/WXM1ClientConfigurationSerializer.java @@ -108,6 +108,10 @@ private void serializePaths() "ZFSExecutable", this.clientConfiguration.zfsExecutable() ); + this.serializePath( + "IfconfigExecutable", + this.clientConfiguration.ifconfigExecutable() + ); this.serializePath( "CuExecutable", this.clientConfiguration.cuExecutable() diff --git a/com.io7m.waxmill.xml/src/main/resources/com/io7m/waxmill/xml/config/v1/config-1.0.xsd b/com.io7m.waxmill.xml/src/main/resources/com/io7m/waxmill/xml/config/v1/config-1.0.xsd index 179bf7e..1b83dce 100644 --- a/com.io7m.waxmill.xml/src/main/resources/com/io7m/waxmill/xml/config/v1/config-1.0.xsd +++ b/com.io7m.waxmill.xml/src/main/resources/com/io7m/waxmill/xml/config/v1/config-1.0.xsd @@ -60,6 +60,13 @@
+ + + + The path to the ifconfig executable. On FreeBSD, this is /sbin/ifconfig by default. + + + diff --git a/spotbugs-filter.xml b/spotbugs-filter.xml index 26a7695..c4e0759 100644 --- a/spotbugs-filter.xml +++ b/spotbugs-filter.xml @@ -188,6 +188,27 @@ + + + + + + + + + + + + + + + + + + + + + From 667d39382f71dcf329ad78559e8e2c81264adcaf Mon Sep 17 00:00:00 2001 From: Mark Raynsford Date: Sun, 19 Jul 2020 15:36:21 +0000 Subject: [PATCH 5/9] Add a --replace option This adds a --replace option to the various device addition commands to allow for "modifying" devices. Affects: https://github.com/io7m/waxmill/issues/25 --- .../internal/WXMCommandVMAddAHCIDisk.java | 26 ++++--- .../internal/WXMCommandVMAddAHCIOptical.java | 27 ++++--- .../WXMCommandVMAddE1000NetworkDevice.java | 30 ++++---- .../WXMCommandVMAddFramebufferDevice.java | 27 ++++--- .../cmdline/internal/WXMCommandVMAddLPC.java | 27 ++++--- .../internal/WXMCommandVMAddPassthru.java | 27 ++++--- .../internal/WXMCommandVMAddVirtioDisk.java | 26 ++++--- .../WXMCommandVMAddVirtioNetworkDevice.java | 30 ++++---- .../internal/WXMNetworkBackendConverter.java | 3 +- .../cmdline-vm-add-ahci-disk.xml | 12 +++ .../cmdline-vm-add-ahci-optical.xml | 12 +++ .../cmdline-vm-add-e1000-network-device.xml | 12 +++ .../cmdline-vm-add-framebuffer-device.xml | 12 +++ .../documentation/cmdline-vm-add-lpc.xml | 12 +++ .../documentation/cmdline-vm-add-passthru.xml | 12 +++ .../cmdline-vm-add-virtio-disk.xml | 12 +++ .../cmdline-vm-add-virtio-network-device.xml | 12 +++ .../io7m/waxmill/machines/WXMDeviceSlots.java | 57 --------------- .../waxmill/machines/WXMVirtualMachines.java | 35 +++++++++ .../cmdline/WXMCommandVMAddAHCIDiskTest.java | 61 ++++++++++++++++ .../WXMCommandVMAddAHCIOpticalTest.java | 57 +++++++++++++++ .../WXMCommandVMAddE1000NetworkTest.java | 61 ++++++++++++++++ .../WXMCommandVMAddFramebufferTest.java | 57 +++++++++++++++ .../tests/cmdline/WXMCommandVMAddLPCTest.java | 61 ++++++++++++++++ .../cmdline/WXMCommandVMAddPassthruTest.java | 73 +++++++++++++++++++ .../WXMCommandVMAddVirtioDiskTest.java | 62 +++++++++++++++- .../WXMCommandVMAddVirtioNetworkTest.java | 61 ++++++++++++++++ 27 files changed, 746 insertions(+), 158 deletions(-) delete mode 100644 com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMDeviceSlots.java diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddAHCIDisk.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddAHCIDisk.java index dc22555..d302757 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddAHCIDisk.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddAHCIDisk.java @@ -23,13 +23,13 @@ import com.io7m.waxmill.client.api.WXMClientType; import com.io7m.waxmill.machines.WXMDeviceAHCIDisk; import com.io7m.waxmill.machines.WXMDeviceSlot; -import com.io7m.waxmill.machines.WXMDeviceSlots; import com.io7m.waxmill.machines.WXMDeviceType; import com.io7m.waxmill.machines.WXMMachineMessages; import com.io7m.waxmill.machines.WXMOpenOption; import com.io7m.waxmill.machines.WXMStorageBackendFile; import com.io7m.waxmill.machines.WXMStorageBackendZFSVolume; import com.io7m.waxmill.machines.WXMVirtualMachine; +import com.io7m.waxmill.machines.WXMVirtualMachines; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -87,6 +87,14 @@ public final class WXMCommandVMAddAHCIDisk extends ) private WXMStorageBackendType backend; + @Parameter( + names = "--replace", + description = "Replace an existing device, if one exists", + required = false, + arity = 1 + ) + private boolean replace; + /** * Construct a command. * @@ -163,12 +171,6 @@ protected Status executeActualWithConfiguration( { try (var client = WXMServices.clients().open(configurationPath)) { final var machine = client.vmFind(this.id); - this.deviceSlot = - WXMDeviceSlots.checkDeviceSlotNotUsed( - WXMMachineMessages.create(), - machine, - this.deviceSlot - ); switch (this.backend.kind()) { case WXM_STORAGE_FILE: @@ -198,10 +200,12 @@ protected Status executeActualWithConfiguration( .build(); final var updatedMachine = - WXMVirtualMachine.builder() - .from(machine) - .addDevices(disk) - .build(); + WXMVirtualMachines.updateWithDevice( + WXMMachineMessages.create(), + machine, + disk, + this.replace + ); client.vmUpdate(updatedMachine); this.showCreated(client, machine); diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddAHCIOptical.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddAHCIOptical.java index 44e94c5..8160d24 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddAHCIOptical.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddAHCIOptical.java @@ -21,10 +21,9 @@ import com.io7m.claypot.core.CLPCommandContextType; import com.io7m.waxmill.machines.WXMDeviceAHCIOpticalDisk; import com.io7m.waxmill.machines.WXMDeviceSlot; -import com.io7m.waxmill.machines.WXMDeviceSlots; import com.io7m.waxmill.machines.WXMDeviceType; import com.io7m.waxmill.machines.WXMMachineMessages; -import com.io7m.waxmill.machines.WXMVirtualMachine; +import com.io7m.waxmill.machines.WXMVirtualMachines; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,6 +63,14 @@ public final class WXMCommandVMAddAHCIOptical extends ) private WXMDeviceSlot deviceSlot; + @Parameter( + names = "--replace", + description = "Replace an existing device, if one exists", + required = false, + arity = 1 + ) + private boolean replace; + /** * Construct a command. * @@ -89,12 +96,6 @@ protected Status executeActualWithConfiguration( { try (var client = WXMServices.clients().open(configurationPath)) { final var machine = client.vmFind(this.id); - this.deviceSlot = - WXMDeviceSlots.checkDeviceSlotNotUsed( - WXMMachineMessages.create(), - machine, - this.deviceSlot - ); final WXMDeviceType disk = WXMDeviceAHCIOpticalDisk.builder() @@ -103,10 +104,12 @@ protected Status executeActualWithConfiguration( .build(); final var updatedMachine = - WXMVirtualMachine.builder() - .from(machine) - .addDevices(disk) - .build(); + WXMVirtualMachines.updateWithDevice( + WXMMachineMessages.create(), + machine, + disk, + this.replace + ); client.vmUpdate(updatedMachine); } diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddE1000NetworkDevice.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddE1000NetworkDevice.java index 0206ad2..6e997c1 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddE1000NetworkDevice.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddE1000NetworkDevice.java @@ -21,11 +21,11 @@ import com.io7m.claypot.core.CLPCommandContextType; import com.io7m.waxmill.machines.WXMDeviceE1000; import com.io7m.waxmill.machines.WXMDeviceSlot; -import com.io7m.waxmill.machines.WXMDeviceSlots; import com.io7m.waxmill.machines.WXMMachineMessages; +import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; import com.io7m.waxmill.machines.WXMTap; import com.io7m.waxmill.machines.WXMVMNet; -import com.io7m.waxmill.machines.WXMVirtualMachine; +import com.io7m.waxmill.machines.WXMVirtualMachines; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,8 +35,6 @@ import static com.io7m.claypot.core.CLPCommandType.Status.SUCCESS; -import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; - @Parameters(commandDescription = "Add an e1000 network device to a virtual machine.") public final class WXMCommandVMAddE1000NetworkDevice extends WXMAbstractCommandWithConfiguration @@ -75,6 +73,14 @@ public final class WXMCommandVMAddE1000NetworkDevice ) private WXMNetworkDeviceBackendType backend; + @Parameter( + names = "--replace", + description = "Replace an existing device, if one exists", + required = false, + arity = 1 + ) + private boolean replace; + /** * Construct a command. * @@ -110,12 +116,6 @@ protected Status executeActualWithConfiguration( { try (var client = WXMServices.clients().open(configurationPath)) { final var machine = client.vmFind(this.id); - this.deviceSlot = - WXMDeviceSlots.checkDeviceSlotNotUsed( - WXMMachineMessages.create(), - machine, - this.deviceSlot - ); final var virtio = WXMDeviceE1000.builder() @@ -125,10 +125,12 @@ protected Status executeActualWithConfiguration( .build(); final var updatedMachine = - WXMVirtualMachine.builder() - .from(machine) - .addDevices(virtio) - .build(); + WXMVirtualMachines.updateWithDevice( + WXMMachineMessages.create(), + machine, + virtio, + this.replace + ); client.vmUpdate(updatedMachine); diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddFramebufferDevice.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddFramebufferDevice.java index 1575854..2a9d1e5 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddFramebufferDevice.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddFramebufferDevice.java @@ -21,9 +21,8 @@ import com.io7m.claypot.core.CLPCommandContextType; import com.io7m.waxmill.machines.WXMDeviceFramebuffer; import com.io7m.waxmill.machines.WXMDeviceSlot; -import com.io7m.waxmill.machines.WXMDeviceSlots; import com.io7m.waxmill.machines.WXMMachineMessages; -import com.io7m.waxmill.machines.WXMVirtualMachine; +import com.io7m.waxmill.machines.WXMVirtualMachines; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -109,6 +108,14 @@ public final class WXMCommandVMAddFramebufferDevice ) private boolean waitForVNC; + @Parameter( + names = "--replace", + description = "Replace an existing device, if one exists", + required = false, + arity = 1 + ) + private boolean replace; + /** * Construct a command. * @@ -143,12 +150,6 @@ protected Status executeActualWithConfiguration( { try (var client = WXMServices.clients().open(configurationPath)) { final var machine = client.vmFind(this.id); - this.deviceSlot = - WXMDeviceSlots.checkDeviceSlotNotUsed( - WXMMachineMessages.create(), - machine, - this.deviceSlot - ); final var virtio = WXMDeviceFramebuffer.builder() @@ -163,10 +164,12 @@ protected Status executeActualWithConfiguration( .build(); final var updatedMachine = - WXMVirtualMachine.builder() - .from(machine) - .addDevices(virtio) - .build(); + WXMVirtualMachines.updateWithDevice( + WXMMachineMessages.create(), + machine, + virtio, + this.replace + ); client.vmUpdate(updatedMachine); } diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddLPC.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddLPC.java index 3de912f..92c32aa 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddLPC.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddLPC.java @@ -21,13 +21,12 @@ import com.io7m.claypot.core.CLPCommandContextType; import com.io7m.waxmill.machines.WXMDeviceLPC; import com.io7m.waxmill.machines.WXMDeviceSlot; -import com.io7m.waxmill.machines.WXMDeviceSlots; import com.io7m.waxmill.machines.WXMMachineMessages; import com.io7m.waxmill.machines.WXMTTYBackendFile; import com.io7m.waxmill.machines.WXMTTYBackendNMDM; import com.io7m.waxmill.machines.WXMTTYBackendStdio; import com.io7m.waxmill.machines.WXMTTYBackends; -import com.io7m.waxmill.machines.WXMVirtualMachine; +import com.io7m.waxmill.machines.WXMVirtualMachines; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,6 +80,14 @@ public final class WXMCommandVMAddLPC extends ) private List backends = List.of(); + @Parameter( + names = "--replace", + description = "Replace an existing device, if one exists", + required = false, + arity = 1 + ) + private boolean replace; + /** * Construct a command. * @@ -116,12 +123,6 @@ protected Status executeActualWithConfiguration( { try (var client = WXMServices.clients().open(configurationPath)) { final var machine = client.vmFind(this.id); - this.deviceSlot = - WXMDeviceSlots.checkDeviceSlotNotUsed( - WXMMachineMessages.create(), - machine, - this.deviceSlot - ); final Map backendMap = new HashMap<>(3); for (final WXMTTYBackendType backend : this.backends) { @@ -140,10 +141,12 @@ protected Status executeActualWithConfiguration( .build(); final var updatedMachine = - WXMVirtualMachine.builder() - .from(machine) - .addDevices(lpc) - .build(); + WXMVirtualMachines.updateWithDevice( + WXMMachineMessages.create(), + machine, + lpc, + this.replace + ); client.vmUpdate(updatedMachine); diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddPassthru.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddPassthru.java index 3907e39..f8771f7 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddPassthru.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddPassthru.java @@ -21,9 +21,8 @@ import com.io7m.claypot.core.CLPCommandContextType; import com.io7m.waxmill.machines.WXMDevicePassthru; import com.io7m.waxmill.machines.WXMDeviceSlot; -import com.io7m.waxmill.machines.WXMDeviceSlots; import com.io7m.waxmill.machines.WXMMachineMessages; -import com.io7m.waxmill.machines.WXMVirtualMachine; +import com.io7m.waxmill.machines.WXMVirtualMachines; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,6 +70,14 @@ public final class WXMCommandVMAddPassthru extends ) private WXMDeviceSlot hostDeviceSlot; + @Parameter( + names = "--replace", + description = "Replace an existing device, if one exists", + required = false, + arity = 1 + ) + private boolean replace; + /** * Construct a command. * @@ -106,12 +113,6 @@ protected Status executeActualWithConfiguration( { try (var client = WXMServices.clients().open(configurationPath)) { final var machine = client.vmFind(this.id); - this.deviceSlot = - WXMDeviceSlots.checkDeviceSlotNotUsed( - WXMMachineMessages.create(), - machine, - this.deviceSlot - ); final var passthru = WXMDevicePassthru.builder() @@ -121,10 +122,12 @@ protected Status executeActualWithConfiguration( .build(); final var updatedMachine = - WXMVirtualMachine.builder() - .from(machine) - .addDevices(passthru) - .build(); + WXMVirtualMachines.updateWithDevice( + WXMMachineMessages.create(), + machine, + passthru, + this.replace + ); client.vmUpdate(updatedMachine); } diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddVirtioDisk.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddVirtioDisk.java index 8cb2a76..93a3fe7 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddVirtioDisk.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddVirtioDisk.java @@ -22,7 +22,6 @@ import com.io7m.junreachable.UnimplementedCodeException; import com.io7m.waxmill.client.api.WXMClientType; import com.io7m.waxmill.machines.WXMDeviceSlot; -import com.io7m.waxmill.machines.WXMDeviceSlots; import com.io7m.waxmill.machines.WXMDeviceType; import com.io7m.waxmill.machines.WXMDeviceVirtioBlockStorage; import com.io7m.waxmill.machines.WXMMachineMessages; @@ -30,6 +29,7 @@ import com.io7m.waxmill.machines.WXMStorageBackendFile; import com.io7m.waxmill.machines.WXMStorageBackendZFSVolume; import com.io7m.waxmill.machines.WXMVirtualMachine; +import com.io7m.waxmill.machines.WXMVirtualMachines; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -87,6 +87,14 @@ public final class WXMCommandVMAddVirtioDisk extends ) private WXMStorageBackendType backend; + @Parameter( + names = "--replace", + description = "Replace an existing device, if one exists", + required = false, + arity = 1 + ) + private boolean replace; + /** * Construct a command. * @@ -161,12 +169,6 @@ protected Status executeActualWithConfiguration( { try (var client = WXMServices.clients().open(configurationPath)) { final var machine = client.vmFind(this.id); - this.deviceSlot = - WXMDeviceSlots.checkDeviceSlotNotUsed( - WXMMachineMessages.create(), - machine, - this.deviceSlot - ); switch (this.backend.kind()) { case WXM_STORAGE_FILE: @@ -196,10 +198,12 @@ protected Status executeActualWithConfiguration( .build(); final var updatedMachine = - WXMVirtualMachine.builder() - .from(machine) - .addDevices(disk) - .build(); + WXMVirtualMachines.updateWithDevice( + WXMMachineMessages.create(), + machine, + disk, + this.replace + ); client.vmUpdate(updatedMachine); this.showCreated(client, machine); diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddVirtioNetworkDevice.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddVirtioNetworkDevice.java index d4b77d0..da1745d 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddVirtioNetworkDevice.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMCommandVMAddVirtioNetworkDevice.java @@ -20,12 +20,12 @@ import com.beust.jcommander.Parameters; import com.io7m.claypot.core.CLPCommandContextType; import com.io7m.waxmill.machines.WXMDeviceSlot; -import com.io7m.waxmill.machines.WXMDeviceSlots; import com.io7m.waxmill.machines.WXMDeviceVirtioNetwork; import com.io7m.waxmill.machines.WXMMachineMessages; +import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; import com.io7m.waxmill.machines.WXMTap; import com.io7m.waxmill.machines.WXMVMNet; -import com.io7m.waxmill.machines.WXMVirtualMachine; +import com.io7m.waxmill.machines.WXMVirtualMachines; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,8 +35,6 @@ import static com.io7m.claypot.core.CLPCommandType.Status.SUCCESS; -import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; - @Parameters(commandDescription = "Add a virtio network device to a virtual machine.") public final class WXMCommandVMAddVirtioNetworkDevice extends WXMAbstractCommandWithConfiguration @@ -75,6 +73,14 @@ public final class WXMCommandVMAddVirtioNetworkDevice ) private WXMNetworkDeviceBackendType backend; + @Parameter( + names = "--replace", + description = "Replace an existing device, if one exists", + required = false, + arity = 1 + ) + private boolean replace; + /** * Construct a command. * @@ -110,12 +116,6 @@ protected Status executeActualWithConfiguration( { try (var client = WXMServices.clients().open(configurationPath)) { final var machine = client.vmFind(this.id); - this.deviceSlot = - WXMDeviceSlots.checkDeviceSlotNotUsed( - WXMMachineMessages.create(), - machine, - this.deviceSlot - ); final var virtio = WXMDeviceVirtioNetwork.builder() @@ -125,10 +125,12 @@ protected Status executeActualWithConfiguration( .build(); final var updatedMachine = - WXMVirtualMachine.builder() - .from(machine) - .addDevices(virtio) - .build(); + WXMVirtualMachines.updateWithDevice( + WXMMachineMessages.create(), + machine, + virtio, + this.replace + ); client.vmUpdate(updatedMachine); diff --git a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMNetworkBackendConverter.java b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMNetworkBackendConverter.java index 664eb70..55d37c4 100644 --- a/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMNetworkBackendConverter.java +++ b/com.io7m.waxmill.cmdline/src/main/java/com/io7m/waxmill/cmdline/internal/WXMNetworkBackendConverter.java @@ -19,13 +19,12 @@ import com.beust.jcommander.IStringConverter; import com.io7m.waxmill.machines.WXMInterfaceGroupName; import com.io7m.waxmill.machines.WXMMACAddress; +import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; import com.io7m.waxmill.machines.WXMTAPDeviceName; import com.io7m.waxmill.machines.WXMTap; import com.io7m.waxmill.machines.WXMVMNet; import com.io7m.waxmill.machines.WXMVMNetDeviceName; -import com.io7m.waxmill.machines.WXMNetworkDeviceBackendType; - import java.util.List; import java.util.stream.Collectors; diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-ahci-disk.xml b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-ahci-disk.xml index 95bd6e8..3be1383 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-ahci-disk.xml +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-ahci-disk.xml @@ -113,6 +113,18 @@ Set the minimum logging verbosity level. + + + --replace + + + Boolean + + + false + + true if an existing device in the given slot should be replaced. + diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-ahci-optical.xml b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-ahci-optical.xml index c3ca6a8..dbc2f51 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-ahci-optical.xml +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-ahci-optical.xml @@ -92,6 +92,18 @@ Set the minimum logging verbosity level. + + + --replace + + + Boolean + + + false + + true if an existing device in the given slot should be replaced. + diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-e1000-network-device.xml b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-e1000-network-device.xml index e4f5320..9cd9010 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-e1000-network-device.xml +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-e1000-network-device.xml @@ -104,6 +104,18 @@ Set the minimum logging verbosity level. + + + --replace + + + Boolean + + + false + + true if an existing device in the given slot should be replaced. + diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-framebuffer-device.xml b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-framebuffer-device.xml index afda741..464900c 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-framebuffer-device.xml +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-framebuffer-device.xml @@ -158,6 +158,18 @@ The framebuffer width + + + --replace + + + Boolean + + + false + + true if an existing device in the given slot should be replaced. + diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-lpc.xml b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-lpc.xml index 305fae8..26f3f72 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-lpc.xml +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-lpc.xml @@ -103,6 +103,18 @@ Set the minimum logging verbosity level. + + + --replace + + + Boolean + + + false + + true if an existing device in the given slot should be replaced. + diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-passthru.xml b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-passthru.xml index 4f3d5ff..b460f6f 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-passthru.xml +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-passthru.xml @@ -112,6 +112,18 @@ Set the minimum logging verbosity level. + + + --replace + + + Boolean + + + false + + true if an existing device in the given slot should be replaced. + diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-virtio-disk.xml b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-virtio-disk.xml index 8d803b6..3ee0936 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-virtio-disk.xml +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-virtio-disk.xml @@ -110,6 +110,18 @@ Set the minimum logging verbosity level. + + + --replace + + + Boolean + + + false + + true if an existing device in the given slot should be replaced. + diff --git a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-virtio-network-device.xml b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-virtio-network-device.xml index 81b7d06..47b04fc 100644 --- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-virtio-network-device.xml +++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/cmdline-vm-add-virtio-network-device.xml @@ -101,6 +101,18 @@ Set the minimum logging verbosity level. + + + --replace + + + Boolean + + + false + + true if an existing device in the given slot should be replaced. + diff --git a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMDeviceSlots.java b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMDeviceSlots.java deleted file mode 100644 index d956e4f..0000000 --- a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMDeviceSlots.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2020 Mark Raynsford http://io7m.com - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR - * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package com.io7m.waxmill.machines; - -import com.io7m.waxmill.exceptions.WXMExceptionDuplicate; - -import java.util.Objects; - -/** - * Functions over device slots. - */ - -public final class WXMDeviceSlots -{ - private WXMDeviceSlots() - { - - } - - public static WXMDeviceSlot checkDeviceSlotNotUsed( - final WXMMachineMessages messages, - final WXMVirtualMachine machine, - final WXMDeviceSlot deviceSlot) - throws WXMExceptionDuplicate - { - Objects.requireNonNull(messages, "messages"); - Objects.requireNonNull(machine, "machine"); - Objects.requireNonNull(deviceSlot, "deviceSlot"); - - final var device = machine.deviceMap().get(deviceSlot); - if (device != null) { - throw new WXMExceptionDuplicate( - messages.format( - "errorDeviceSlotAlreadyUsed", - machine.id(), - deviceSlot, - device.kind() - ) - ); - } - return deviceSlot; - } -} diff --git a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMVirtualMachines.java b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMVirtualMachines.java index 8dec890..6015060 100644 --- a/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMVirtualMachines.java +++ b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMVirtualMachines.java @@ -16,6 +16,9 @@ package com.io7m.waxmill.machines; +import com.io7m.waxmill.exceptions.WXMExceptionDuplicate; + +import java.util.HashMap; import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -56,4 +59,36 @@ public static Set bootConfigurationsUsingDevice( } return Set.copyOf(referencing); } + + public static WXMVirtualMachine updateWithDevice( + final WXMMachineMessages messages, + final WXMVirtualMachine machine, + final WXMDeviceType device, + final boolean replace) + throws WXMExceptionDuplicate + { + Objects.requireNonNull(machine, "machine"); + Objects.requireNonNull(device, "device"); + + final var deviceMap = new HashMap<>(machine.deviceMap()); + final var existing = deviceMap.get(device.deviceSlot()); + if (existing != null) { + if (!replace) { + throw new WXMExceptionDuplicate( + messages.format( + "errorDeviceSlotAlreadyUsed", + machine.id(), + existing.deviceSlot(), + existing.kind() + ) + ); + } + } + + deviceMap.put(device.deviceSlot(), device); + return WXMVirtualMachine.builder() + .from(machine) + .setDevices(deviceMap.values()) + .build(); + } } diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddAHCIDiskTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddAHCIDiskTest.java index 6572efd..9123a3c 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddAHCIDiskTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddAHCIDiskTest.java @@ -282,6 +282,67 @@ public void addAHCIDiskAlreadyUsed() }); } + @Test + public void addAHCIDiskReplace() + throws Exception + { + final var id = UUID.randomUUID(); + + MainExitless.main( + new String[]{ + "vm-define", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--name", + "com.io7m.example", + "--memory-gigabytes", + "1", + "--memory-megabytes", + "128", + "--cpu-count", + "2", + "--machine", + id.toString() + } + ); + + MainExitless.main( + new String[]{ + "vm-add-ahci-disk", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--backend", + "file;/tmp/xyz", + "--device-slot", + "0:1:0" + } + ); + + MainExitless.main( + new String[]{ + "vm-add-ahci-disk", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--backend", + "file;/tmp/xyz", + "--device-slot", + "0:1:0", + "--replace", + "true" + } + ); + } + @Test public void addAHCIDiskMalformedSlot0() throws Exception diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddAHCIOpticalTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddAHCIOpticalTest.java index 4a79a2c..469b000 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddAHCIOpticalTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddAHCIOpticalTest.java @@ -248,6 +248,63 @@ public void addAHCIDiskAlreadyUsed() }); } + @Test + public void addAHCIDiskAlreadyUsedReplace() + throws Exception + { + final var id = UUID.randomUUID(); + + MainExitless.main( + new String[]{ + "vm-define", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--name", + "com.io7m.example", + "--memory-gigabytes", + "1", + "--memory-megabytes", + "128", + "--cpu-count", + "2", + "--machine", + id.toString() + } + ); + + MainExitless.main( + new String[]{ + "vm-add-ahci-optical", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--device-slot", + "0:1:0" + } + ); + + MainExitless.main( + new String[]{ + "vm-add-ahci-optical", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--device-slot", + "0:1:0", + "--replace", + "true" + } + ); + } + @Test public void addAHCIDiskMalformedSlot0() throws Exception diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddE1000NetworkTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddE1000NetworkTest.java index 1ea06e2..42c2a92 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddE1000NetworkTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddE1000NetworkTest.java @@ -151,6 +151,67 @@ public void addE1000NetworkAlreadyUsed() }); } + @Test + public void addE1000NetworkAlreadyUsedReplace() + throws Exception + { + final var id = UUID.randomUUID(); + + MainExitless.main( + new String[]{ + "vm-define", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--name", + "com.io7m.example", + "--memory-gigabytes", + "1", + "--memory-megabytes", + "128", + "--cpu-count", + "2", + "--machine", + id.toString() + } + ); + + MainExitless.main( + new String[]{ + "vm-add-e1000-network-device", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--backend", + "tap;tap23;a3:26:9c:74:79:34", + "--device-slot", + "0:1:0" + } + ); + + MainExitless.main( + new String[]{ + "vm-add-e1000-network-device", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--backend", + "tap;tap23;a3:26:9c:74:79:34", + "--device-slot", + "0:1:0", + "--replace", + "true" + } + ); + } + @Test public void addE1000NetworkOKTap() throws Exception diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddFramebufferTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddFramebufferTest.java index c920729..ef1beb1 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddFramebufferTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddFramebufferTest.java @@ -137,4 +137,61 @@ public void addFramebufferAlreadyUsed() ); }); } + + @Test + public void addFramebufferAlreadyUsedReplace() + throws Exception + { + final var id = UUID.randomUUID(); + + MainExitless.main( + new String[]{ + "vm-define", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--name", + "com.io7m.example", + "--memory-gigabytes", + "1", + "--memory-megabytes", + "128", + "--cpu-count", + "2", + "--machine", + id.toString() + } + ); + + MainExitless.main( + new String[]{ + "vm-add-framebuffer-device", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--device-slot", + "0:1:0" + } + ); + + MainExitless.main( + new String[]{ + "vm-add-framebuffer-device", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--device-slot", + "0:1:0", + "--replace", + "true" + } + ); + } } diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddLPCTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddLPCTest.java index c728fd6..31514a8 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddLPCTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddLPCTest.java @@ -156,6 +156,67 @@ public void addLPCAlreadyUsed() }); } + @Test + public void addLPCAlreadyUsedReplace() + throws IOException + { + final var id = UUID.randomUUID(); + + MainExitless.main( + new String[]{ + "vm-define", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--name", + "com.io7m.example", + "--memory-gigabytes", + "1", + "--memory-megabytes", + "128", + "--cpu-count", + "2", + "--machine", + id.toString() + } + ); + + MainExitless.main( + new String[]{ + "vm-add-ahci-disk", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--backend", + "file;/tmp/xyz", + "--device-slot", + "0:1:0" + } + ); + + MainExitless.main( + new String[]{ + "vm-add-ahci-disk", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--backend", + "file;/tmp/xyz", + "--device-slot", + "0:1:0", + "--replace", + "true" + } + ); + } + @Test public void addLPCDevicesNotUnique() throws IOException diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddPassthruTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddPassthruTest.java index 3d5bc62..bb87a24 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddPassthruTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddPassthruTest.java @@ -230,4 +230,77 @@ public void addPassthruAlreadyUsed() ); }); } + + @Test + public void addPassthruAlreadyUsedReplace() + throws Exception + { + final var id = UUID.randomUUID(); + + MainExitless.main( + new String[]{ + "vm-define", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--name", + "com.io7m.example", + "--memory-gigabytes", + "1", + "--memory-megabytes", + "128", + "--cpu-count", + "2", + "--machine", + id.toString() + } + ); + + MainExitless.main( + new String[]{ + "vm-set", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--wire-guest-memory", + "true" + } + ); + + MainExitless.main( + new String[]{ + "vm-add-passthru-device", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--device-slot", + "0:1:0", + "--host-device-slot", + "1:2:3" + } + ); + + MainExitless.main( + new String[]{ + "vm-add-passthru-device", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--device-slot", + "0:1:0", + "--host-device-slot", + "1:2:3", + "--replace", + "true" + } + ); + } } diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddVirtioDiskTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddVirtioDiskTest.java index a709e87..911cfca 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddVirtioDiskTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddVirtioDiskTest.java @@ -70,7 +70,6 @@ public void setup() ); } - @Test public void addVirtioDiskOK() throws Exception @@ -282,4 +281,65 @@ public void addVirtioDiskAlreadyUsed() ); }); } + + @Test + public void addVirtioDiskAlreadyUsedReplace() + throws Exception + { + final var id = UUID.randomUUID(); + + MainExitless.main( + new String[]{ + "vm-define", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--name", + "com.io7m.example", + "--memory-gigabytes", + "1", + "--memory-megabytes", + "128", + "--cpu-count", + "2", + "--machine", + id.toString() + } + ); + + MainExitless.main( + new String[]{ + "vm-add-virtio-disk", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--backend", + "file;/tmp/xyz", + "--device-slot", + "0:1:0" + } + ); + + MainExitless.main( + new String[]{ + "vm-add-virtio-disk", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--backend", + "file;/tmp/xyz", + "--device-slot", + "0:1:0", + "--replace", + "true" + } + ); + } } diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddVirtioNetworkTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddVirtioNetworkTest.java index f62a6f2..2079570 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddVirtioNetworkTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMAddVirtioNetworkTest.java @@ -151,6 +151,67 @@ public void addVirtioNetworkAlreadyUsed() }); } + @Test + public void addVirtioNetworkAlreadyUsedReplace() + throws Exception + { + final var id = UUID.randomUUID(); + + MainExitless.main( + new String[]{ + "vm-define", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--name", + "com.io7m.example", + "--memory-gigabytes", + "1", + "--memory-megabytes", + "128", + "--cpu-count", + "2", + "--machine", + id.toString() + } + ); + + MainExitless.main( + new String[]{ + "vm-add-virtio-network-device", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--backend", + "tap;tap23;a3:26:9c:74:79:34", + "--device-slot", + "0:1:0" + } + ); + + MainExitless.main( + new String[]{ + "vm-add-virtio-network-device", + "--verbose", + "trace", + "--configuration", + this.configFile.toString(), + "--machine", + id.toString(), + "--backend", + "tap;tap23;a3:26:9c:74:79:34", + "--device-slot", + "0:1:0", + "--replace", + "true" + } + ); + } + @Test public void addVirtioNetworkOKTap0() throws Exception From 73a376099b0cab657fafda02f01cd8b3a5b0637e Mon Sep 17 00:00:00 2001 From: Mark Raynsford Date: Sun, 19 Jul 2020 20:00:13 +0000 Subject: [PATCH 6/9] Fix an issue related to LPC bootrom formatting The argument list was being incorrectly constructed when attempting to produce UEFI boot instructions. Fix: https://github.com/io7m/waxmill/issues/26 --- .../boot/WXMBootConfigurationEvaluator.java | 3 +- ...WXMBootConfigurationEvaluatorGRUBTest.java | 85 +++++++++++++++++++ ...WXMBootConfigurationEvaluatorUEFITest.java | 58 +++++++++++++ 3 files changed, 145 insertions(+), 1 deletion(-) diff --git a/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationEvaluator.java b/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationEvaluator.java index 9eaa7cc..c21cce4 100644 --- a/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationEvaluator.java +++ b/com.io7m.waxmill.boot/src/main/java/com/io7m/waxmill/boot/WXMBootConfigurationEvaluator.java @@ -537,8 +537,9 @@ private WXMCommandExecution generateBhyveCommand( } case UEFI: { final var uefi = (WXMBootConfigurationUEFI) bootConfiguration; + commandBuilder.addArguments("-l"); commandBuilder.addArguments( - String.format("-l bootrom,%s", uefi.firmware()) + String.format("bootrom,%s", uefi.firmware()) ); break; } diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/boot/WXMBootConfigurationEvaluatorGRUBTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/boot/WXMBootConfigurationEvaluatorGRUBTest.java index 11b6d8a..4f927e3 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/boot/WXMBootConfigurationEvaluatorGRUBTest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/boot/WXMBootConfigurationEvaluatorGRUBTest.java @@ -779,6 +779,23 @@ public void linuxVirtioNetTAP() } final var lastExec = commands.lastExecution().orElseThrow(); + final var lastArgs = new ArrayList<>(lastExec.arguments()); + assertEquals("-P", lastArgs.remove(0)); + assertEquals("-A", lastArgs.remove(0)); + assertEquals("-H", lastArgs.remove(0)); + assertEquals("-c", lastArgs.remove(0)); + assertEquals("cpus=1,sockets=1,cores=1,threads=1", lastArgs.remove(0)); + assertEquals("-m", lastArgs.remove(0)); + assertEquals("512M", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:0:0,hostbridge", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:1:0,ahci-hd,/tmp/file", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:2:0,virtio-net,tap23,mac=1b:61:cb:ba:c0:12", lastArgs.remove(0)); + assertEquals(machine.id().toString(), lastArgs.remove(0)); + assertEquals(0, lastArgs.size()); + assertEquals( String.format( "/usr/sbin/bhyve -P -A -H -c cpus=1,sockets=1,cores=1,threads=1 -m 512M -s 0:0:0,hostbridge -s 0:1:0,ahci-hd,/tmp/file -s 0:2:0,virtio-net,tap23,mac=1b:61:cb:ba:c0:12 %s", @@ -917,6 +934,23 @@ public void linuxVirtioNetTAPGroups() } final var lastExec = commands.lastExecution().orElseThrow(); + final var lastArgs = new ArrayList<>(lastExec.arguments()); + assertEquals("-P", lastArgs.remove(0)); + assertEquals("-A", lastArgs.remove(0)); + assertEquals("-H", lastArgs.remove(0)); + assertEquals("-c", lastArgs.remove(0)); + assertEquals("cpus=1,sockets=1,cores=1,threads=1", lastArgs.remove(0)); + assertEquals("-m", lastArgs.remove(0)); + assertEquals("512M", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:0:0,hostbridge", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:1:0,ahci-hd,/tmp/file", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:2:0,virtio-net,tap23,mac=1b:61:cb:ba:c0:12", lastArgs.remove(0)); + assertEquals(machine.id().toString(), lastArgs.remove(0)); + assertEquals(0, lastArgs.size()); + assertEquals( String.format( "/usr/sbin/bhyve -P -A -H -c cpus=1,sockets=1,cores=1,threads=1 -m 512M -s 0:0:0,hostbridge -s 0:1:0,ahci-hd,/tmp/file -s 0:2:0,virtio-net,tap23,mac=1b:61:cb:ba:c0:12 %s", @@ -1033,6 +1067,23 @@ public void linuxVirtioNetVMNet() } final var lastExec = commands.lastExecution().orElseThrow(); + final var lastArgs = new ArrayList<>(lastExec.arguments()); + assertEquals("-P", lastArgs.remove(0)); + assertEquals("-A", lastArgs.remove(0)); + assertEquals("-H", lastArgs.remove(0)); + assertEquals("-c", lastArgs.remove(0)); + assertEquals("cpus=1,sockets=1,cores=1,threads=1", lastArgs.remove(0)); + assertEquals("-m", lastArgs.remove(0)); + assertEquals("512M", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:0:0,hostbridge", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:1:0,ahci-hd,/tmp/file", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:2:0,virtio-net,vmnet23,mac=1b:61:cb:ba:c0:12", lastArgs.remove(0)); + assertEquals(machine.id().toString(), lastArgs.remove(0)); + assertEquals(0, lastArgs.size()); + assertEquals( String.format( "/usr/sbin/bhyve -P -A -H -c cpus=1,sockets=1,cores=1,threads=1 -m 512M -s 0:0:0,hostbridge -s 0:1:0,ahci-hd,/tmp/file -s 0:2:0,virtio-net,vmnet23,mac=1b:61:cb:ba:c0:12 %s", @@ -1171,6 +1222,23 @@ public void linuxVirtioNetVMNetGroups() } final var lastExec = commands.lastExecution().orElseThrow(); + final var lastArgs = new ArrayList<>(lastExec.arguments()); + assertEquals("-P", lastArgs.remove(0)); + assertEquals("-A", lastArgs.remove(0)); + assertEquals("-H", lastArgs.remove(0)); + assertEquals("-c", lastArgs.remove(0)); + assertEquals("cpus=1,sockets=1,cores=1,threads=1", lastArgs.remove(0)); + assertEquals("-m", lastArgs.remove(0)); + assertEquals("512M", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:0:0,hostbridge", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:1:0,ahci-hd,/tmp/file", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:2:0,virtio-net,vmnet23,mac=1b:61:cb:ba:c0:12", lastArgs.remove(0)); + assertEquals(machine.id().toString(), lastArgs.remove(0)); + assertEquals(0, lastArgs.size()); + assertEquals( String.format( "/usr/sbin/bhyve -P -A -H -c cpus=1,sockets=1,cores=1,threads=1 -m 512M -s 0:0:0,hostbridge -s 0:1:0,ahci-hd,/tmp/file -s 0:2:0,virtio-net,vmnet23,mac=1b:61:cb:ba:c0:12 %s", @@ -1287,6 +1355,23 @@ public void linuxE1000Net() } final var lastExec = commands.lastExecution().orElseThrow(); + final var lastArgs = new ArrayList<>(lastExec.arguments()); + assertEquals("-P", lastArgs.remove(0)); + assertEquals("-A", lastArgs.remove(0)); + assertEquals("-H", lastArgs.remove(0)); + assertEquals("-c", lastArgs.remove(0)); + assertEquals("cpus=1,sockets=1,cores=1,threads=1", lastArgs.remove(0)); + assertEquals("-m", lastArgs.remove(0)); + assertEquals("512M", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:0:0,hostbridge", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:1:0,ahci-hd,/tmp/file", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:2:0,e1000,vmnet23,mac=1b:61:cb:ba:c0:12", lastArgs.remove(0)); + assertEquals(machine.id().toString(), lastArgs.remove(0)); + assertEquals(0, lastArgs.size()); + assertEquals( String.format( "/usr/sbin/bhyve -P -A -H -c cpus=1,sockets=1,cores=1,threads=1 -m 512M -s 0:0:0,hostbridge -s 0:1:0,ahci-hd,/tmp/file -s 0:2:0,e1000,vmnet23,mac=1b:61:cb:ba:c0:12 %s", diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/boot/WXMBootConfigurationEvaluatorUEFITest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/boot/WXMBootConfigurationEvaluatorUEFITest.java index 9bba746..fb981aa 100644 --- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/boot/WXMBootConfigurationEvaluatorUEFITest.java +++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/boot/WXMBootConfigurationEvaluatorUEFITest.java @@ -52,6 +52,7 @@ import java.net.UnknownHostException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Set; import java.util.UUID; @@ -147,6 +148,25 @@ public void openbsdSimpleAHCIHD() assertEquals(0, configs.size()); final var lastExec = commands.lastExecution().orElseThrow(); + final var lastArgs = new ArrayList<>(lastExec.arguments()); + assertEquals("-P", lastArgs.remove(0)); + assertEquals("-A", lastArgs.remove(0)); + assertEquals("-H", lastArgs.remove(0)); + assertEquals("-c", lastArgs.remove(0)); + assertEquals("cpus=1,sockets=1,cores=1,threads=1", lastArgs.remove(0)); + assertEquals("-m", lastArgs.remove(0)); + assertEquals("512M", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:0:0,ahci-hd,/tmp/file,nocache,direct,ro,sectorsize=2048/4096", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:1:0,lpc", lastArgs.remove(0)); + assertEquals("-l", lastArgs.remove(0)); + assertEquals("com1,stdio", lastArgs.remove(0)); + assertEquals("-l", lastArgs.remove(0)); + assertEquals("bootrom,/tmp/firmware", lastArgs.remove(0)); + assertEquals(machine.id().toString(), lastArgs.remove(0)); + assertEquals(0, lastArgs.size()); + assertEquals( String.format( "/usr/sbin/bhyve -P -A -H -c cpus=1,sockets=1,cores=1,threads=1 -m 512M -s 0:0:0,ahci-hd,/tmp/file,nocache,direct,ro,sectorsize=2048/4096 -s 0:1:0,lpc -l com1,stdio -l bootrom,/tmp/firmware %s", @@ -217,6 +237,25 @@ public void openbsdVNC6() assertEquals(0, configs.size()); final var lastExec = commands.lastExecution().orElseThrow(); + final var lastArgs = new ArrayList<>(lastExec.arguments()); + assertEquals("-P", lastArgs.remove(0)); + assertEquals("-A", lastArgs.remove(0)); + assertEquals("-H", lastArgs.remove(0)); + assertEquals("-c", lastArgs.remove(0)); + assertEquals("cpus=1,sockets=1,cores=1,threads=1", lastArgs.remove(0)); + assertEquals("-m", lastArgs.remove(0)); + assertEquals("512M", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:1:0,lpc", lastArgs.remove(0)); + assertEquals("-l", lastArgs.remove(0)); + assertEquals("com1,stdio", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:2:0,fbuf,tcp=[0:0:0:0:0:0:0:1]:5901,w=1200,h=1400,vga=off,wait", lastArgs.remove(0)); + assertEquals("-l", lastArgs.remove(0)); + assertEquals("bootrom,/tmp/firmware", lastArgs.remove(0)); + assertEquals(machine.id().toString(), lastArgs.remove(0)); + assertEquals(0, lastArgs.size()); + assertEquals( String.format( "/usr/sbin/bhyve -P -A -H -c cpus=1,sockets=1,cores=1,threads=1 -m 512M -s 0:1:0,lpc -l com1,stdio -s 0:2:0,fbuf,tcp=[0:0:0:0:0:0:0:1]:5901,w=1200,h=1400,vga=off,wait -l bootrom,/tmp/firmware %s", @@ -284,6 +323,25 @@ public void openbsdVNC4() assertEquals(0, configs.size()); final var lastExec = commands.lastExecution().orElseThrow(); + final var lastArgs = new ArrayList<>(lastExec.arguments()); + assertEquals("-P", lastArgs.remove(0)); + assertEquals("-A", lastArgs.remove(0)); + assertEquals("-H", lastArgs.remove(0)); + assertEquals("-c", lastArgs.remove(0)); + assertEquals("cpus=1,sockets=1,cores=1,threads=1", lastArgs.remove(0)); + assertEquals("-m", lastArgs.remove(0)); + assertEquals("512M", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:1:0,lpc", lastArgs.remove(0)); + assertEquals("-l", lastArgs.remove(0)); + assertEquals("com1,stdio", lastArgs.remove(0)); + assertEquals("-s", lastArgs.remove(0)); + assertEquals("0:2:0,fbuf,tcp=127.0.0.1:5901,w=1200,h=1400,vga=off,wait", lastArgs.remove(0)); + assertEquals("-l", lastArgs.remove(0)); + assertEquals("bootrom,/tmp/firmware", lastArgs.remove(0)); + assertEquals(machine.id().toString(), lastArgs.remove(0)); + assertEquals(0, lastArgs.size()); + assertEquals( String.format( "/usr/sbin/bhyve -P -A -H -c cpus=1,sockets=1,cores=1,threads=1 -m 512M -s 0:1:0,lpc -l com1,stdio -s 0:2:0,fbuf,tcp=127.0.0.1:5901,w=1200,h=1400,vga=off,wait -l bootrom,/tmp/firmware %s", From f3217052275ae01fd40c44ef71249e987e840a3b Mon Sep 17 00:00:00 2001 From: Mark Raynsford Date: Sat, 25 Jul 2020 08:57:50 +0000 Subject: [PATCH 7/9] Implement `zfs create` for new machines When realizing a virtual machine, if the virtual machine runtime directory is a ZFS filesystem, then a new ZFS filesystem will be created for the virtual machine. Fix: https://github.com/io7m/waxmill/issues/23 --- .../WXMRealizationInstructionsType.java | 16 ++ .../realize/WXMRealizationStepType.java | 20 +++ .../waxmill/realize/WXMRealizationType.java | 11 ++ .../io7m/waxmill/realize/WXMRealizations.java | 24 +++ .../realize/internal/WXMFileCheck.java | 7 + .../internal/WXMRuntimeDirectoryCreate.java | 150 ++++++++++++++++ .../realize/internal/WXMZFSVolumeCheck.java | 2 +- .../io7m/waxmill/realize/internal/Realize.xml | 8 + .../cmdline/WXMCommandVMRealizeTest.java | 1 + .../tests/realize/WXMRealizationsTest.java | 165 ++++++++++++++++-- 10 files changed, 393 insertions(+), 11 deletions(-) create mode 100644 com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/internal/WXMRuntimeDirectoryCreate.java diff --git a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizationInstructionsType.java b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizationInstructionsType.java index a74b90d..2f2194d 100644 --- a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizationInstructionsType.java +++ b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizationInstructionsType.java @@ -25,12 +25,28 @@ import java.util.List; import java.util.Objects; +/** + * The instructions that comprise a realization. + */ + @Value.Immutable @ImmutablesStyleType public interface WXMRealizationInstructionsType { + /** + * @return The realization steps + */ + List steps(); + /** + * Execute the realization. + * + * @param dryRun If this is a dry run + * + * @throws WXMException On errors + */ + default void execute( final WXMDryRun dryRun) throws WXMException diff --git a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizationStepType.java b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizationStepType.java index 616020a..4e16f6f 100644 --- a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizationStepType.java +++ b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizationStepType.java @@ -22,12 +22,32 @@ import java.util.List; +/** + * A single step within a realization. + */ + public interface WXMRealizationStepType { + /** + * @return The description of the step + */ + String description(); + /** + * @return The list of processes that will be executed + */ + List processes(); + /** + * Execute the step. + * + * @param dryRun A specification of whether this is a dry run or not + * + * @throws WXMException On errors + */ + void execute( WXMDryRun dryRun) throws WXMException; diff --git a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizationType.java b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizationType.java index 5c1ce45..9e7e6ea 100644 --- a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizationType.java +++ b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizationType.java @@ -16,7 +16,18 @@ package com.io7m.waxmill.realize; +/** + * A realization operation. + */ + public interface WXMRealizationType { + /** + * Evaluate the realization operation, returning the list of instructions + * to be performed to realize the virtual machine. + * + * @return A list of instructions + */ + WXMRealizationInstructions evaluate(); } diff --git a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizations.java b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizations.java index 6f68a17..e9fa61f 100644 --- a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizations.java +++ b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/WXMRealizations.java @@ -27,10 +27,15 @@ import com.io7m.waxmill.process.api.WXMProcessesType; import com.io7m.waxmill.realize.internal.WXMFileCheck; import com.io7m.waxmill.realize.internal.WXMRealizeMessages; +import com.io7m.waxmill.realize.internal.WXMRuntimeDirectoryCreate; import com.io7m.waxmill.realize.internal.WXMZFSVolumeCheck; import java.util.Objects; +/** + * Functions to construct realizations. + */ + public final class WXMRealizations implements WXMRealizationType { private final WXMProcessesType processes; @@ -54,6 +59,16 @@ private WXMRealizations( Objects.requireNonNull(inMachine, "machine"); } + /** + * Create a realization operation for the given virtual machine. + * + * @param processes A process interface + * @param clientConfiguration The client configuration + * @param machine The virtual machine + * + * @return A realization operation + */ + public static WXMRealizationType create( final WXMProcessesType processes, final WXMClientConfiguration clientConfiguration, @@ -72,6 +87,15 @@ public WXMRealizationInstructions evaluate() { final var builder = WXMRealizationInstructions.builder(); + builder.addSteps( + new WXMRuntimeDirectoryCreate( + this.clientConfiguration, + this.messages, + this.processes, + this.machine.id() + ) + ); + for (final var device : this.machine.devices()) { switch (device.kind()) { case WXM_AHCI_CD: diff --git a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/internal/WXMFileCheck.java b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/internal/WXMFileCheck.java index 8053c15..cfdadef 100644 --- a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/internal/WXMFileCheck.java +++ b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/internal/WXMFileCheck.java @@ -21,6 +21,8 @@ import com.io7m.waxmill.machines.WXMDryRun; import com.io7m.waxmill.process.api.WXMProcessDescription; import com.io7m.waxmill.realize.WXMRealizationStepType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.nio.file.Files; import java.nio.file.Path; @@ -33,6 +35,9 @@ public final class WXMFileCheck implements WXMRealizationStepType { + private static final Logger LOG = + LoggerFactory.getLogger(WXMFileCheck.class); + private final WXMRealizeMessages messages; private final WXMDeviceSlot slot; private final UUID machineId; @@ -75,6 +80,8 @@ public void execute( return; } + LOG.info("checking {} is regular file", this.file); + if (!Files.isRegularFile(this.file, NOFOLLOW_LINKS)) { throw new WXMException(this.errorMissingFile(this.file)); } diff --git a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/internal/WXMRuntimeDirectoryCreate.java b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/internal/WXMRuntimeDirectoryCreate.java new file mode 100644 index 0000000..deea84e --- /dev/null +++ b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/internal/WXMRuntimeDirectoryCreate.java @@ -0,0 +1,150 @@ +/* + * Copyright © 2020 Mark Raynsford http://io7m.com + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package com.io7m.waxmill.realize.internal; + +import com.io7m.waxmill.client.api.WXMClientConfiguration; +import com.io7m.waxmill.exceptions.WXMException; +import com.io7m.waxmill.machines.WXMDryRun; +import com.io7m.waxmill.process.api.WXMProcessDescription; +import com.io7m.waxmill.process.api.WXMProcessesType; +import com.io7m.waxmill.realize.WXMRealizationStepType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import static com.io7m.waxmill.machines.WXMDryRun.DRY_RUN; +import static java.util.Locale.ROOT; + +public final class WXMRuntimeDirectoryCreate implements WXMRealizationStepType +{ + private static final Logger LOG = + LoggerFactory.getLogger(WXMRuntimeDirectoryCreate.class); + + private final WXMRealizeMessages messages; + private final UUID machineId; + private final WXMClientConfiguration clientConfiguration; + private final WXMProcessesType processes; + private List processesList; + + public WXMRuntimeDirectoryCreate( + final WXMClientConfiguration inClientConfiguration, + final WXMRealizeMessages inMessages, + final WXMProcessesType inProcesses, + final UUID inMachineId) + { + this.clientConfiguration = + Objects.requireNonNull(inClientConfiguration, "clientConfiguration"); + this.messages = + Objects.requireNonNull(inMessages, "messages"); + this.processes = + Objects.requireNonNull(inProcesses, "inProcesses"); + this.machineId = + Objects.requireNonNull(inMachineId, "inMachineId"); + + this.processesList = List.of(); + } + + @Override + public String description() + { + return this.messages.format( + "runtimeDirectoryCreate", + this.clientConfiguration.virtualMachineRuntimeDirectory() + .resolve(this.machineId.toString()) + ); + } + + @Override + public List processes() + { + return List.copyOf(this.processesList); + } + + @Override + public void execute( + final WXMDryRun dryRun) + throws WXMException + { + if (dryRun == DRY_RUN) { + return; + } + + final var baseDirectory = + this.clientConfiguration.virtualMachineRuntimeDirectory(); + final var path = + baseDirectory.resolve(this.machineId.toString()); + + LOG.info("checking if {} is a directory", path); + + if (Files.isDirectory(path)) { + LOG.info("{} is a directory", path); + return; + } + + if (Files.exists(path)) { + throw new WXMException(this.notADirectory(path)); + } + + try { + final var store = Files.getFileStore(baseDirectory); + if ("ZFS".equals(store.type().toUpperCase(ROOT))) { + final var createPath = + String.format("%s/%s", store.name(), this.machineId); + + LOG.info("creating ZFS filesystem {}", createPath); + final var process = + WXMProcessDescription.builder() + .setExecutable(this.clientConfiguration.zfsExecutable()) + .addArguments("create") + .addArguments(createPath) + .build(); + + this.processesList = List.of(process); + this.processes.processStartAndWait(process); + } else { + LOG.info("creating directory {}", path); + Files.createDirectories(path); + } + } catch (final IOException | InterruptedException e) { + throw new WXMException(e); + } + } + + private String notADirectory( + final Path path) + { + return this.messages.format( + "runtimeDirectoryNotADirectory", + path + ); + } + + @Override + public String toString() + { + return String.format( + "[WXMRuntimeDirectoryCreate 0x%s]", + Long.toUnsignedString(System.identityHashCode(this), 16) + ); + } +} diff --git a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/internal/WXMZFSVolumeCheck.java b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/internal/WXMZFSVolumeCheck.java index 1c1d8ae..ce266ef 100644 --- a/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/internal/WXMZFSVolumeCheck.java +++ b/com.io7m.waxmill.realize/src/main/java/com/io7m/waxmill/realize/internal/WXMZFSVolumeCheck.java @@ -72,9 +72,9 @@ public WXMZFSVolumeCheck( Objects.requireNonNull(inSlot, "slot"); this.zfsVolume = Objects.requireNonNull(inZFSVolume, "zfsVolume"); - final var machineId = Objects.requireNonNull(inMachineId, "machineId"); + this.volumePath = WXMStorageBackends.determineZFSVolumePath( this.clientConfiguration.virtualMachineRuntimeDirectory(), diff --git a/com.io7m.waxmill.realize/src/main/resources/com/io7m/waxmill/realize/internal/Realize.xml b/com.io7m.waxmill.realize/src/main/resources/com/io7m/waxmill/realize/internal/Realize.xml index 0153960..b6931fc 100644 --- a/com.io7m.waxmill.realize/src/main/resources/com/io7m/waxmill/realize/internal/Realize.xml +++ b/com.io7m.waxmill.realize/src/main/resources/com/io7m/waxmill/realize/internal/Realize.xml @@ -29,6 +29,14 @@ Action: Create the zfs volume manually (zfs create -V). ]]> + + + + (instructions.steps()); + + { + final var step = steps.remove(0); + final var description = step.description(); + LOG.debug("{}", description); + } + + assertEquals(0, steps.size()); instructions.execute(EXECUTE); } @Test public void realizeAHCIZFSOK() + throws IOException { final var machine = WXMVirtualMachine.builder() @@ -133,6 +152,8 @@ public void realizeAHCIZFSOK() .build()) .build(); + Files.createDirectories(this.vms.resolve(machine.id().toString())); + final var clientConfiguration = WXMClientConfiguration.builder() .setVirtualMachineConfigurationDirectory(this.configs) @@ -144,10 +165,16 @@ public void realizeAHCIZFSOK() final var instructions = realizations.evaluate(); - assertEquals(1, instructions.steps().size()); + final var steps = new ArrayList<>(instructions.steps()); { - final var step = instructions.steps().get(0); + final var step = steps.remove(0); + final var description = step.description(); + LOG.debug("{}", description); + } + + { + final var step = steps.remove(0); final var description = step.description(); LOG.debug("{}", description); assertTrue(description.contains(DEVICE_SLOT_0.toString())); @@ -173,10 +200,13 @@ public void realizeAHCIZFSOK() machine.id() ), arguments.get(3)); } + + assertEquals(0, steps.size()); } @Test public void realizeVirtioBlockZFSOK() + throws IOException { final var machine = WXMVirtualMachine.builder() @@ -189,6 +219,8 @@ public void realizeVirtioBlockZFSOK() .build()) .build(); + Files.createDirectories(this.vms.resolve(machine.id().toString())); + final var clientConfiguration = WXMClientConfiguration.builder() .setVirtualMachineConfigurationDirectory(this.configs) @@ -200,10 +232,16 @@ public void realizeVirtioBlockZFSOK() final var instructions = realizations.evaluate(); - assertEquals(1, instructions.steps().size()); + final var steps = new ArrayList<>(instructions.steps()); + + { + final var step = steps.remove(0); + final var description = step.description(); + LOG.debug("{}", description); + } { - final var step = instructions.steps().get(0); + final var step = steps.remove(0); final var description = step.description(); LOG.debug("{}", description); assertTrue(description.contains(DEVICE_SLOT_0.toString())); @@ -229,6 +267,8 @@ public void realizeVirtioBlockZFSOK() machine.id() ), arguments.get(3)); } + + assertEquals(0, steps.size()); } @Test @@ -246,6 +286,8 @@ public void realizeVirtioBlockZFSFails() .build()) .build(); + Files.createDirectories(this.vms.resolve(machine.id().toString())); + final var clientConfiguration = WXMClientConfiguration.builder() .setVirtualMachineConfigurationDirectory(this.configs) @@ -300,6 +342,8 @@ public void realizeAHCIBlockZFSFails() .build()) .build(); + Files.createDirectories(this.vms.resolve(machine.id().toString())); + final var clientConfiguration = WXMClientConfiguration.builder() .setVirtualMachineConfigurationDirectory(this.configs) @@ -341,6 +385,7 @@ public void realizeAHCIBlockZFSFails() @Test public void realizeAHCIBlockZFSFailsUnsized() + throws IOException { final var machine = WXMVirtualMachine.builder() @@ -353,6 +398,8 @@ public void realizeAHCIBlockZFSFailsUnsized() .build()) .build(); + Files.createDirectories(this.vms.resolve(machine.id().toString())); + final var clientConfiguration = WXMClientConfiguration.builder() .setVirtualMachineConfigurationDirectory(this.configs) @@ -393,6 +440,8 @@ public void realizeAHCIFileOK() .build()) .build(); + Files.createDirectories(this.vms.resolve(machine.id().toString())); + final var clientConfiguration = WXMClientConfiguration.builder() .setVirtualMachineConfigurationDirectory(this.configs) @@ -404,13 +453,19 @@ public void realizeAHCIFileOK() final var instructions = realizations.evaluate(); + final var steps = new ArrayList<>(instructions.steps()); + Files.write(file, new byte[100]); instructions.execute(EXECUTE); - assertEquals(1, instructions.steps().size()); + { + final var step = steps.remove(0); + final var description = step.description(); + LOG.debug("{}", description); + } { - final var step = instructions.steps().get(0); + final var step = steps.remove(0); final var description = step.description(); LOG.debug("{}", description); assertTrue(description.contains(DEVICE_SLOT_0.toString())); @@ -419,6 +474,8 @@ public void realizeAHCIFileOK() final var processes = step.processes(); assertEquals(0, processes.size()); } + + assertEquals(0, steps.size()); } @Test @@ -442,6 +499,8 @@ public void realizeAHCIFileFails() .build()) .build(); + Files.createDirectories(this.vms.resolve(machine.id().toString())); + final var clientConfiguration = WXMClientConfiguration.builder() .setVirtualMachineConfigurationDirectory(this.configs) @@ -485,6 +544,8 @@ public void realizeVirtioBlockStorageFileOK() .build()) .build(); + Files.createDirectories(this.vms.resolve(machine.id().toString())); + final var clientConfiguration = WXMClientConfiguration.builder() .setVirtualMachineConfigurationDirectory(this.configs) @@ -499,10 +560,16 @@ public void realizeVirtioBlockStorageFileOK() Files.write(file, new byte[100]); instructions.execute(EXECUTE); - assertEquals(1, instructions.steps().size()); + final var steps = new ArrayList<>(instructions.steps()); { - final var step = instructions.steps().get(0); + final var step = steps.remove(0); + final var description = step.description(); + LOG.debug("{}", description); + } + + { + final var step = steps.remove(0); final var description = step.description(); LOG.debug("{}", description); assertTrue(description.contains(DEVICE_SLOT_0.toString())); @@ -534,6 +601,8 @@ public void realizeVirtioBlockStorageFileFails() .build()) .build(); + Files.createDirectories(this.vms.resolve(machine.id().toString())); + final var clientConfiguration = WXMClientConfiguration.builder() .setVirtualMachineConfigurationDirectory(this.configs) @@ -554,4 +623,80 @@ public void realizeVirtioBlockStorageFileFails() final var exc = (Exception) ex.getSuppressed()[0]; assertTrue(exc.getMessage().contains(file.toString())); } + + @Test + public void realizeZFSCreate() + throws WXMException, IOException + { + final var machine = + WXMVirtualMachine.builder() + .setId(UUID.randomUUID()) + .setName(WXMMachineName.of("vm")) + .build(); + + final var vmFilesystemProvider = + mock(FileSystemProvider.class); + final var vmFilesystem = + mock(FileSystem.class); + final var vmFileStore = + mock(FileStore.class); + final var attributes = + mock(BasicFileAttributes.class); + final var vmPath = + mock(Path.class); + + when(Boolean.valueOf(attributes.isDirectory())) + .thenReturn(Boolean.FALSE); + when(vmFilesystemProvider.getFileStore(any())) + .thenReturn(vmFileStore); + when(vmFilesystem.provider()) + .thenReturn(vmFilesystemProvider); + doThrow(IOException.class) + .when(vmFilesystemProvider) + .checkAccess(any(), any()); + when(vmFilesystemProvider.readAttributes( + any(), eq(BasicFileAttributes.class), any())) + .thenReturn(attributes); + when(vmPath.resolve(anyString())) + .thenReturn(vmPath); + when(vmPath.getFileSystem()) + .thenReturn(vmFilesystem); + when(Boolean.valueOf(vmPath.isAbsolute())) + .thenReturn(Boolean.TRUE); + when(vmPath.toString()) + .thenReturn("/storage/x/y/z"); + when(vmFileStore.type()) + .thenReturn("zfs"); + when(vmFileStore.name()) + .thenReturn("storage/x/y/z"); + + final var clientConfiguration = + WXMClientConfiguration.builder() + .setVirtualMachineConfigurationDirectory(this.configs) + .setVirtualMachineRuntimeDirectory(vmPath) + .build(); + + final var realizations = + WXMRealizations.create(this.processes, clientConfiguration, machine); + final var instructions = + realizations.evaluate(); + + final var steps = new ArrayList<>(instructions.steps()); + instructions.execute(EXECUTE); + + { + final var step = steps.remove(0); + final var description = step.description(); + LOG.debug("{}", description); + + final var proc = step.processes().get(0); + final var arguments = proc.arguments(); + assertEquals(clientConfiguration.zfsExecutable(), proc.executable()); + assertEquals("create", arguments.get(0)); + assertEquals(String.format("storage/x/y/z/%s", machine.id()), arguments.get(1)); + assertEquals(1, step.processes().size()); + } + + assertEquals(0, steps.size()); + } } From 37597c622ffb8713d59879e452b9b7899df71174 Mon Sep 17 00:00:00 2001 From: Mark Raynsford Date: Sat, 25 Jul 2020 08:58:56 +0000 Subject: [PATCH 8/9] Mark 0.0.3 --- com.io7m.waxmill.boot/pom.xml | 2 +- com.io7m.waxmill.client.api/pom.xml | 2 +- com.io7m.waxmill.client.vanilla/pom.xml | 2 +- com.io7m.waxmill.cmdline/pom.xml | 2 +- com.io7m.waxmill.database.api/pom.xml | 2 +- com.io7m.waxmill.database.vanilla/pom.xml | 2 +- com.io7m.waxmill.documentation/pom.xml | 2 +- com.io7m.waxmill.exceptions/pom.xml | 2 +- com.io7m.waxmill.locks/pom.xml | 2 +- com.io7m.waxmill.machines/pom.xml | 2 +- com.io7m.waxmill.parser.api/pom.xml | 2 +- com.io7m.waxmill.process.api/pom.xml | 2 +- com.io7m.waxmill.process.posix/pom.xml | 2 +- com.io7m.waxmill.realize/pom.xml | 2 +- com.io7m.waxmill.serializer.api/pom.xml | 2 +- com.io7m.waxmill.strings.api/pom.xml | 2 +- com.io7m.waxmill.tests/pom.xml | 2 +- com.io7m.waxmill.xml/pom.xml | 2 +- pom.xml | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/com.io7m.waxmill.boot/pom.xml b/com.io7m.waxmill.boot/pom.xml index 0918afc..2817f27 100644 --- a/com.io7m.waxmill.boot/pom.xml +++ b/com.io7m.waxmill.boot/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.boot diff --git a/com.io7m.waxmill.client.api/pom.xml b/com.io7m.waxmill.client.api/pom.xml index 8a853ae..48122ae 100644 --- a/com.io7m.waxmill.client.api/pom.xml +++ b/com.io7m.waxmill.client.api/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.client.api diff --git a/com.io7m.waxmill.client.vanilla/pom.xml b/com.io7m.waxmill.client.vanilla/pom.xml index c8d4591..53720de 100644 --- a/com.io7m.waxmill.client.vanilla/pom.xml +++ b/com.io7m.waxmill.client.vanilla/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.client.vanilla diff --git a/com.io7m.waxmill.cmdline/pom.xml b/com.io7m.waxmill.cmdline/pom.xml index c801e04..558b9bc 100644 --- a/com.io7m.waxmill.cmdline/pom.xml +++ b/com.io7m.waxmill.cmdline/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.cmdline diff --git a/com.io7m.waxmill.database.api/pom.xml b/com.io7m.waxmill.database.api/pom.xml index e21279a..aaf01eb 100644 --- a/com.io7m.waxmill.database.api/pom.xml +++ b/com.io7m.waxmill.database.api/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.database.api diff --git a/com.io7m.waxmill.database.vanilla/pom.xml b/com.io7m.waxmill.database.vanilla/pom.xml index ae21320..b5a484d 100644 --- a/com.io7m.waxmill.database.vanilla/pom.xml +++ b/com.io7m.waxmill.database.vanilla/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.database.vanilla diff --git a/com.io7m.waxmill.documentation/pom.xml b/com.io7m.waxmill.documentation/pom.xml index 0a78338..f7c1468 100644 --- a/com.io7m.waxmill.documentation/pom.xml +++ b/com.io7m.waxmill.documentation/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.documentation diff --git a/com.io7m.waxmill.exceptions/pom.xml b/com.io7m.waxmill.exceptions/pom.xml index c0d7948..56c8d04 100644 --- a/com.io7m.waxmill.exceptions/pom.xml +++ b/com.io7m.waxmill.exceptions/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.exceptions diff --git a/com.io7m.waxmill.locks/pom.xml b/com.io7m.waxmill.locks/pom.xml index c958caf..6b94f4b 100644 --- a/com.io7m.waxmill.locks/pom.xml +++ b/com.io7m.waxmill.locks/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.locks diff --git a/com.io7m.waxmill.machines/pom.xml b/com.io7m.waxmill.machines/pom.xml index 8c829a3..e4505d2 100644 --- a/com.io7m.waxmill.machines/pom.xml +++ b/com.io7m.waxmill.machines/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.machines diff --git a/com.io7m.waxmill.parser.api/pom.xml b/com.io7m.waxmill.parser.api/pom.xml index 5685aa1..22906f5 100644 --- a/com.io7m.waxmill.parser.api/pom.xml +++ b/com.io7m.waxmill.parser.api/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.parser.api diff --git a/com.io7m.waxmill.process.api/pom.xml b/com.io7m.waxmill.process.api/pom.xml index 9266579..8bf9388 100644 --- a/com.io7m.waxmill.process.api/pom.xml +++ b/com.io7m.waxmill.process.api/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.process.api diff --git a/com.io7m.waxmill.process.posix/pom.xml b/com.io7m.waxmill.process.posix/pom.xml index e57dd54..d27f759 100644 --- a/com.io7m.waxmill.process.posix/pom.xml +++ b/com.io7m.waxmill.process.posix/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.process.posix diff --git a/com.io7m.waxmill.realize/pom.xml b/com.io7m.waxmill.realize/pom.xml index 3db512c..70787dc 100644 --- a/com.io7m.waxmill.realize/pom.xml +++ b/com.io7m.waxmill.realize/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.realize diff --git a/com.io7m.waxmill.serializer.api/pom.xml b/com.io7m.waxmill.serializer.api/pom.xml index 73343e7..b546e81 100644 --- a/com.io7m.waxmill.serializer.api/pom.xml +++ b/com.io7m.waxmill.serializer.api/pom.xml @@ -8,7 +8,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.serializer.api diff --git a/com.io7m.waxmill.strings.api/pom.xml b/com.io7m.waxmill.strings.api/pom.xml index 7c6542f..8296859 100644 --- a/com.io7m.waxmill.strings.api/pom.xml +++ b/com.io7m.waxmill.strings.api/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.strings.api diff --git a/com.io7m.waxmill.tests/pom.xml b/com.io7m.waxmill.tests/pom.xml index 8e84cd9..1161276 100644 --- a/com.io7m.waxmill.tests/pom.xml +++ b/com.io7m.waxmill.tests/pom.xml @@ -9,7 +9,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.tests diff --git a/com.io7m.waxmill.xml/pom.xml b/com.io7m.waxmill.xml/pom.xml index 6f5d314..31e339a 100644 --- a/com.io7m.waxmill.xml/pom.xml +++ b/com.io7m.waxmill.xml/pom.xml @@ -8,7 +8,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 com.io7m.waxmill.xml diff --git a/pom.xml b/pom.xml index 020fc77..4ceb6ec 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.io7m.waxmill com.io7m.waxmill - 0.0.3-SNAPSHOT + 0.0.3 pom FreeBSD BHyve Manager From 0830e4f72e4780e64a40dbeca1bc1d41ad4997fb Mon Sep 17 00:00:00 2001 From: Mark Raynsford Date: Sat, 25 Jul 2020 09:00:55 +0000 Subject: [PATCH 9/9] Note changes --- README-CHANGES.xml | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/README-CHANGES.xml b/README-CHANGES.xml index d1f9e76..58113e5 100644 --- a/README-CHANGES.xml +++ b/README-CHANGES.xml @@ -6,9 +6,33 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + +