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 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/com.io7m.waxmill.boot/pom.xml b/com.io7m.waxmill.boot/pom.xml
index e6e712d..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.2
+ 0.0.3
com.io7m.waxmill.boot
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..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
@@ -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,7 +71,6 @@
import java.util.stream.Collectors;
import static com.io7m.waxmill.machines.WXMBootConfigurationType.WXMEvaluatedBootConfigurationType;
-import static com.io7m.waxmill.machines.WXMDeviceType.WXMDeviceVirtioNetworkType.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;
@@ -368,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 =
@@ -429,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;
}
@@ -749,7 +858,7 @@ private void configureBhyveFlags(
}
private WXMEvaluatedBootConfigurationGRUBBhyve evaluateGRUBConfigurationOpenBSD(
- final WXMBootConfigurationType bootConfiguration,
+ final WXMBootConfigurationType config,
final WXMDeviceMap deviceMap,
final WXMGRUBKernelOpenBSD openBSD)
{
@@ -757,11 +866,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())
@@ -881,24 +990,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/pom.xml b/com.io7m.waxmill.client.api/pom.xml
index 21f8867..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.2
+ 0.0.3
com.io7m.waxmill.client.api
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.client.vanilla/pom.xml b/com.io7m.waxmill.client.vanilla/pom.xml
index cfae9ac..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.2
+ 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 0740045..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.2
+ 0.0.3
com.io7m.waxmill.cmdline
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 bffc8fe..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;
@@ -34,7 +34,6 @@
import java.util.UUID;
import static com.io7m.claypot.core.CLPCommandType.Status.SUCCESS;
-import static com.io7m.waxmill.machines.WXMDeviceType.WXMNetworkDeviceBackendType;
@Parameters(commandDescription = "Add an e1000 network device to a virtual machine.")
public final class WXMCommandVMAddE1000NetworkDevice
@@ -74,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.
*
@@ -109,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()
@@ -124,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 c18cc07..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;
@@ -34,7 +34,6 @@
import java.util.UUID;
import static com.io7m.claypot.core.CLPCommandType.Status.SUCCESS;
-import static com.io7m.waxmill.machines.WXMDeviceType.WXMNetworkDeviceBackendType;
@Parameters(commandDescription = "Add a virtio network device to a virtual machine.")
public final class WXMCommandVMAddVirtioNetworkDevice
@@ -74,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.
*
@@ -109,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()
@@ -124,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 d026d5a..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
@@ -17,13 +17,16 @@
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.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 static com.io7m.waxmill.machines.WXMDeviceType.WXMNetworkDeviceBackendType;
+import java.util.List;
+import java.util.stream.Collectors;
public final class WXMNetworkBackendConverter
implements IStringConverter
@@ -49,23 +52,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 5efd040..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
@@ -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,30 +35,39 @@ Examples:
]]>
";"
- | "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'}'"
+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 , [ ";" 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
]]>
= ":" ":"
+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.database.api/pom.xml b/com.io7m.waxmill.database.api/pom.xml
index 10405d7..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.2
+ 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 8e4c2e7..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.2
+ 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 092f615..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.2
+ 0.0.3
com.io7m.waxmill.documentation
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-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.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/config-example.xml b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/config-example.xml
index 15ed56f..db4827b 100644
--- a/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/config-example.xml
+++ b/com.io7m.waxmill.documentation/src/main/resources/com/io7m/waxmill/documentation/config-example.xml
@@ -16,5 +16,7 @@
value="/etc/waxmill/vm"/>
+
\ No newline at end of file
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..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,13 +1,22 @@
- "tap" ";" ";"
-| "vmnet" ";" ";"
+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 } ;
-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'}'"
+macPart = hexDigit , hexDigit ;
+macAddress = macPart , ":" , macPart , ":" , macPart , ":" , macPart , ":" , macPart , ":" , macPart ;
+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
\ No newline at end of file
+ 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.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
diff --git a/com.io7m.waxmill.exceptions/pom.xml b/com.io7m.waxmill.exceptions/pom.xml
index ebbd3bd..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.2
+ 0.0.3
com.io7m.waxmill.exceptions
diff --git a/com.io7m.waxmill.locks/pom.xml b/com.io7m.waxmill.locks/pom.xml
index 076be6c..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.2
+ 0.0.3
com.io7m.waxmill.locks
diff --git a/com.io7m.waxmill.machines/pom.xml b/com.io7m.waxmill.machines/pom.xml
index 93a1de6..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.2
+ 0.0.3
com.io7m.waxmill.machines
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/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.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/WXMDeviceSlots.java b/com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMNetworkDeviceBackendType.java
similarity index 51%
rename from com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMDeviceSlots.java
rename to com.io7m.waxmill.machines/src/main/java/com/io7m/waxmill/machines/WXMNetworkDeviceBackendType.java
index d956e4f..557b73d 100644
--- 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/WXMNetworkDeviceBackendType.java
@@ -16,42 +16,54 @@
package com.io7m.waxmill.machines;
-import com.io7m.waxmill.exceptions.WXMExceptionDuplicate;
+import org.immutables.value.Value;
-import java.util.Objects;
+import java.util.List;
/**
- * Functions over device slots.
+ * The type of network device backends.
*/
-public final class WXMDeviceSlots
+public interface WXMNetworkDeviceBackendType
{
- private WXMDeviceSlots()
- {
+ /**
+ * @return The backend kind
+ */
+
+ Kind kind();
+
+ /**
+ * @return A descriptive comment
+ */
+ @Value.Default
+ default String comment()
+ {
+ return "";
}
- public static WXMDeviceSlot checkDeviceSlotNotUsed(
- final WXMMachineMessages messages,
- final WXMVirtualMachine machine,
- final WXMDeviceSlot deviceSlot)
- throws WXMExceptionDuplicate
+ /**
+ * @return The groups to which this device backend belongs
+ */
+
+ List groups();
+
+ /**
+ * The kind of backend.
+ */
+
+ enum Kind
{
- 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;
+ /**
+ * 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.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.parser.api/pom.xml b/com.io7m.waxmill.parser.api/pom.xml
index 4c2f280..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.2
+ 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 d5c74e1..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.2
+ 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 e58efd5..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.2
+ 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 986a2d2..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.2
+ 0.0.3
com.io7m.waxmill.realize
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).
]]>
+
+
+
+
com.io7m.waxmill
com.io7m.waxmill
- 0.0.2
+ 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 3a6d452..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.2
+ 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 61b86fb..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.2
+ 0.0.3
com.io7m.waxmill.tests
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/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/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/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..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
@@ -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,12 +750,207 @@ 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();
+ 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",
+ 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();
+ 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",
@@ -840,12 +1038,207 @@ 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();
+ 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",
+ 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();
+ 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",
@@ -933,12 +1326,52 @@ 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();
+ 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",
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 c95d844..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
@@ -152,7 +152,7 @@ public void addVirtioNetworkAlreadyUsed()
}
@Test
- public void addVirtioNetworkOKTap()
+ public void addVirtioNetworkAlreadyUsedReplace()
throws Exception
{
final var id = UUID.randomUUID();
@@ -193,6 +193,127 @@ public void addVirtioNetworkOKTap()
}
);
+ 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
+ {
+ 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"
+ }
+ );
+
+ 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());
+ }
+
+ @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);
@@ -209,6 +330,8 @@ public void addVirtioNetworkOKTap()
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
diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMRealizeTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMRealizeTest.java
index 18e98a8..d6ca375 100644
--- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMRealizeTest.java
+++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/cmdline/WXMCommandVMRealizeTest.java
@@ -52,6 +52,7 @@ public void setup()
this.vmDirectory = this.directory.resolve("vmDirectory");
this.zfsDirectory = this.directory.resolve("zfsDirectory");
Files.createDirectories(this.vmDirectory);
+ Files.createDirectories(this.zfsDirectory);
this.configuration =
WXMClientConfiguration.builder()
diff --git a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/realize/WXMRealizationsTest.java b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/realize/WXMRealizationsTest.java
index d4a05ab..afb0c15 100644
--- a/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/realize/WXMRealizationsTest.java
+++ b/com.io7m.waxmill.tests/src/test/java/com/io7m/waxmill/tests/realize/WXMRealizationsTest.java
@@ -37,8 +37,13 @@
import java.io.IOException;
import java.math.BigInteger;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@@ -48,9 +53,12 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
public final class WXMRealizationsTest
{
@@ -96,7 +104,7 @@ public void setup()
@Test
public void realizeNothing()
- throws WXMException
+ throws WXMException, IOException
{
final var machine =
WXMVirtualMachine.builder()
@@ -104,6 +112,8 @@ public void realizeNothing()
.setName(WXMMachineName.of("vm"))
.build();
+ Files.createDirectories(this.vms.resolve(machine.id().toString()));
+
final var clientConfiguration =
WXMClientConfiguration.builder()
.setVirtualMachineConfigurationDirectory(this.configs)
@@ -115,12 +125,21 @@ public void realizeNothing()
final var instructions =
realizations.evaluate();
- assertEquals(List.of(), instructions.steps());
+ final var steps = new ArrayList<>(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());
+ }
}
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/pom.xml b/com.io7m.waxmill.xml/pom.xml
index e58ec06..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.2
+ 0.0.3
com.io7m.waxmill.xml
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/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