Skip to content
This repository has been archived by the owner on Apr 20, 2024. It is now read-only.

Commit

Permalink
Implement zfs create for new machines
Browse files Browse the repository at this point in the history
When realizing a virtual machine, if the virtual machine runtime
directory is a ZFS filesystem, then a new ZFS filesystem will be
created for the virtual machine.

Fix: #23
  • Loading branch information
io7m committed Jul 25, 2020
1 parent 73a3760 commit f321705
Show file tree
Hide file tree
Showing 10 changed files with 393 additions and 11 deletions.
Expand Up @@ -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<WXMRealizationStepType> steps();

/**
* Execute the realization.
*
* @param dryRun If this is a dry run
*
* @throws WXMException On errors
*/

default void execute(
final WXMDryRun dryRun)
throws WXMException
Expand Down
Expand Up @@ -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<WXMProcessDescription> 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;
Expand Down
Expand Up @@ -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();
}
Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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:
Expand Down
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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));
}
Expand Down
@@ -0,0 +1,150 @@
/*
* Copyright © 2020 Mark Raynsford <code@io7m.com> 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<WXMProcessDescription> 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<WXMProcessDescription> 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)
);
}
}
Expand Up @@ -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(),
Expand Down
Expand Up @@ -29,6 +29,14 @@
Action: Create the zfs volume manually (zfs create -V).
]]></entry>

<entry key="runtimeDirectoryCreate"><![CDATA[Creating a runtime directory for the virtual machine.
Filesystem: {0}
]]></entry>

<entry key="runtimeDirectoryNotADirectory"><![CDATA[The runtime directory for the virtual machine is not a directory.
Path: {0}
]]></entry>

<entry key="errorRequiredPathsMissing"><![CDATA[One or more required files are missing.
Machine: {0}
Files: {1}
Expand Down
Expand Up @@ -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()
Expand Down

0 comments on commit f321705

Please sign in to comment.