summaryrefslogtreecommitdiff
path: root/opendc-compute/opendc-compute-simulator/src/main/java/org
diff options
context:
space:
mode:
authorDante Niewenhuis <d.niewenhuis@hotmail.com>2024-10-25 13:32:41 +0200
committerGitHub <noreply@github.com>2024-10-25 13:32:41 +0200
commit5a365dbc068f2a8cdfa9813c39cc84bb30e15637 (patch)
tree72716d562787b85e03cdc7fe1d30c827054d25a0 /opendc-compute/opendc-compute-simulator/src/main/java/org
parent27f5b7dcb05aefdab9b762175d538931face0aba (diff)
Rewrote the FlowEngine (#256)
* Removed unused components. Updated tests. Improved checkpointing model Improved model, started with SimPowerSource implemented FailureModels and Checkpointing First working version midway commit first update All simulation are now run with a single CPU and single MemoryUnit. multi CPUs are combined into one. This is for performance and explainability. * fixed merge conflicts * Updated M3SA paths. * Fixed small typo
Diffstat (limited to 'opendc-compute/opendc-compute-simulator/src/main/java/org')
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/host/HostListener.java41
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/host/HostModel.java32
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/host/HostState.java43
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ComputeService.java652
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/HostView.java78
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ServiceFlavor.java107
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ServiceImage.java95
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ServiceTask.java230
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/GuestCpuStats.java43
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/GuestSystemStats.java35
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/HostCpuStats.java46
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/HostSystemStats.java50
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/SchedulerStats.java45
13 files changed, 1497 insertions, 0 deletions
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/host/HostListener.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/host/HostListener.java
new file mode 100644
index 00000000..01acfa97
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/host/HostListener.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.host;
+
+import org.opendc.compute.api.TaskState;
+import org.opendc.compute.simulator.service.ServiceTask;
+
+/**
+ * Listener interface for events originating from a {@link SimHost}.
+ */
+public interface HostListener {
+ /**
+ * This method is invoked when the state of <code>task</code> on <code>host</code> changes.
+ */
+ default void onStateChanged(SimHost host, ServiceTask task, TaskState newState) {}
+
+ /**
+ * This method is invoked when the state of a {@link SimHost} has changed.
+ */
+ default void onStateChanged(SimHost host, HostState newState) {}
+}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/host/HostModel.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/host/HostModel.java
new file mode 100644
index 00000000..96236c5c
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/host/HostModel.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.host;
+
+/**
+ * Record describing the static machine properties of the host.
+ *
+ * @param cpuCapacity The total CPU capacity of the host in MHz.
+ * @param coreCount The number of logical processing cores available for this host.
+ * @param memoryCapacity The amount of memory available for this host in MB.
+ */
+public record HostModel(float cpuCapacity, int coreCount, long memoryCapacity) {}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/host/HostState.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/host/HostState.java
new file mode 100644
index 00000000..29fc8cb4
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/host/HostState.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.host;
+
+/**
+ * The state of a host.
+ */
+public enum HostState {
+ /**
+ * The host is up and able to host guests.
+ */
+ UP,
+
+ /**
+ * The host is in a (forced) down state and unable to host any guests.
+ */
+ DOWN,
+
+ /**
+ * The host is in an error state and unable to host any guests.
+ */
+ ERROR
+}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ComputeService.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ComputeService.java
new file mode 100644
index 00000000..84e23516
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ComputeService.java
@@ -0,0 +1,652 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.service;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.InstantSource;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.SplittableRandom;
+import java.util.UUID;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.opendc.common.Dispatcher;
+import org.opendc.common.util.Pacer;
+import org.opendc.compute.api.Flavor;
+import org.opendc.compute.api.Image;
+import org.opendc.compute.api.TaskState;
+import org.opendc.compute.simulator.host.HostListener;
+import org.opendc.compute.simulator.host.HostModel;
+import org.opendc.compute.simulator.host.HostState;
+import org.opendc.compute.simulator.host.SimHost;
+import org.opendc.compute.simulator.scheduler.ComputeScheduler;
+import org.opendc.compute.simulator.telemetry.SchedulerStats;
+import org.opendc.simulator.compute.workload.Workload;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link ComputeService} hosts the API implementation of the OpenDC Compute Engine.
+ */
+public final class ComputeService implements AutoCloseable {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ComputeService.class);
+
+ /**
+ * The {@link InstantSource} representing the clock tracking the (simulation) time.
+ */
+ private final InstantSource clock;
+
+ /**
+ * The {@link ComputeScheduler} responsible for placing the tasks onto hosts.
+ */
+ private final ComputeScheduler scheduler;
+
+ /**
+ * The {@link Pacer} used to pace the scheduling requests.
+ */
+ private final Pacer pacer;
+
+ /**
+ * The {@link SplittableRandom} used to generate the unique identifiers for the service resources.
+ */
+ private final SplittableRandom random = new SplittableRandom(0);
+
+ private final int maxNumFailures;
+
+ /**
+ * A flag to indicate that the service is closed.
+ */
+ private boolean isClosed;
+
+ /**
+ * A mapping from host to host view.
+ */
+ private final Map<SimHost, HostView> hostToView = new HashMap<>();
+
+ /**
+ * The available hypervisors.
+ */
+ private final Set<HostView> availableHosts = new HashSet<>();
+
+ /**
+ * The tasks that should be launched by the service.
+ */
+ private final Deque<SchedulingRequest> taskQueue = new ArrayDeque<>();
+
+ /**
+ * The active tasks in the system.
+ */
+ private final Map<ServiceTask, SimHost> activeTasks = new HashMap<>();
+
+ /**
+ * The active tasks in the system.
+ */
+ private final Map<ServiceTask, SimHost> completedTasks = new HashMap<>();
+
+ /**
+ * The registered flavors for this compute service.
+ */
+ private final Map<UUID, ServiceFlavor> flavorById = new HashMap<>();
+
+ private final List<ServiceFlavor> flavors = new ArrayList<>();
+
+ /**
+ * The registered images for this compute service.
+ */
+ private final Map<UUID, ServiceImage> imageById = new HashMap<>();
+
+ private final List<ServiceImage> images = new ArrayList<>();
+
+ /**
+ * The registered tasks for this compute service.
+ */
+ private final Map<UUID, ServiceTask> taskById = new HashMap<>();
+
+ private final List<ServiceTask> tasks = new ArrayList<>();
+
+ private final List<ServiceTask> tasksToRemove = new ArrayList<>();
+
+ /**
+ * A [HostListener] used to track the active tasks.
+ */
+ private final HostListener hostListener = new HostListener() {
+ @Override
+ public void onStateChanged(@NotNull SimHost host, @NotNull HostState newState) {
+ LOGGER.debug("Host {} state changed: {}", host, newState);
+
+ final HostView hv = hostToView.get(host);
+
+ if (hv != null) {
+ if (newState == HostState.UP) {
+ availableHosts.add(hv);
+ } else {
+ availableHosts.remove(hv);
+ }
+ }
+
+ // Re-schedule on the new machine
+ requestSchedulingCycle();
+ }
+
+ @Override
+ public void onStateChanged(@NotNull SimHost host, @NotNull ServiceTask task, @NotNull TaskState newState) {
+ if (task.getHost() != host) {
+ // This can happen when a task is rescheduled and started on another machine, while being deleted from
+ // the old machine.
+ return;
+ }
+
+ task.setState(newState);
+
+ if (newState == TaskState.COMPLETED || newState == TaskState.TERMINATED || newState == TaskState.FAILED) {
+ LOGGER.info("task {} {} {} finished", task.getUid(), task.getName(), task.getFlavor());
+
+ if (activeTasks.remove(task) != null) {
+ tasksActive--;
+ }
+
+ HostView hv = hostToView.get(host);
+ final ServiceFlavor flavor = task.getFlavor();
+ if (hv != null) {
+ hv.provisionedCores -= flavor.getCoreCount();
+ hv.instanceCount--;
+ hv.availableMemory += flavor.getMemorySize();
+ } else {
+ LOGGER.error("Unknown host {}", host);
+ }
+
+ task.setHost(null);
+ host.removeTask(task);
+
+ if (newState == TaskState.COMPLETED) {
+ tasksCompleted++;
+ }
+ if (newState == TaskState.TERMINATED) {
+ tasksTerminated++;
+ }
+
+ if (task.getState() == TaskState.COMPLETED || task.getState() == TaskState.TERMINATED) {
+ tasksToRemove.add(task);
+ }
+
+ // Try to reschedule if needed
+ requestSchedulingCycle();
+ }
+ }
+ };
+
+ private int maxCores = 0;
+ private long maxMemory = 0L;
+ private long attemptsSuccess = 0L;
+ private long attemptsFailure = 0L;
+ private int tasksTotal = 0;
+ private int tasksPending = 0;
+ private int tasksActive = 0;
+ private int tasksTerminated = 0;
+ private int tasksCompleted = 0;
+
+ /**
+ * Construct a {@link ComputeService} instance.
+ */
+ public ComputeService(Dispatcher dispatcher, ComputeScheduler scheduler, Duration quantum, int maxNumFailures) {
+ this.clock = dispatcher.getTimeSource();
+ this.scheduler = scheduler;
+ this.pacer = new Pacer(dispatcher, quantum.toMillis(), (time) -> doSchedule());
+ this.maxNumFailures = maxNumFailures;
+ }
+
+ /**
+ * Create a new {@link Builder} instance.
+ */
+ public static Builder builder(Dispatcher dispatcher, ComputeScheduler scheduler) {
+ return new Builder(dispatcher, scheduler);
+ }
+
+ /**
+ * Create a new {@link ComputeClient} to control the compute service.
+ */
+ public ComputeClient newClient() {
+ if (isClosed) {
+ throw new IllegalStateException("Service is closed");
+ }
+ return new ComputeClient(this);
+ }
+
+ /**
+ * Return the {@link ServiceTask}s hosted by this service.
+ */
+ public List<ServiceTask> getTasks() {
+ return Collections.unmodifiableList(tasks);
+ }
+
+ /**
+ * Return the {@link ServiceTask}s hosted by this service.
+ */
+ public List<ServiceTask> getTasksToRemove() {
+ return Collections.unmodifiableList(tasksToRemove);
+ }
+
+ public void clearTasksToRemove() {
+ this.tasksToRemove.clear();
+ }
+
+ /**
+ * Add a {@link SimHost} to the scheduling pool of the compute service.
+ */
+ public void addHost(SimHost host) {
+ // Check if host is already known
+ if (hostToView.containsKey(host)) {
+ return;
+ }
+
+ HostView hv = new HostView(host);
+ HostModel model = host.getModel();
+
+ maxCores = Math.max(maxCores, model.coreCount());
+ maxMemory = Math.max(maxMemory, model.memoryCapacity());
+ hostToView.put(host, hv);
+
+ if (host.getState() == HostState.UP) {
+ availableHosts.add(hv);
+ }
+
+ scheduler.addHost(hv);
+ host.addListener(hostListener);
+ }
+
+ /**
+ * Remove a {@link SimHost} from the scheduling pool of the compute service.
+ */
+ public void removeHost(SimHost host) {
+ HostView view = hostToView.remove(host);
+ if (view != null) {
+ availableHosts.remove(view);
+ scheduler.removeHost(view);
+ host.removeListener(hostListener);
+ }
+ }
+
+ /**
+ * Lookup the {@link SimHost} that currently hosts the specified {@link ServiceTask}.
+ */
+ public SimHost lookupHost(ServiceTask task) {
+ return task.getHost();
+ }
+
+ /**
+ * Return the {@link SimHost}s that are registered with this service.
+ */
+ public Set<SimHost> getHosts() {
+ return Collections.unmodifiableSet(hostToView.keySet());
+ }
+
+ public InstantSource getClock() {
+ return this.clock;
+ }
+
+ /**
+ * Collect the statistics about the scheduler component of this service.
+ */
+ public SchedulerStats getSchedulerStats() {
+ return new SchedulerStats(
+ availableHosts.size(),
+ hostToView.size() - availableHosts.size(),
+ attemptsSuccess,
+ attemptsFailure,
+ tasksTotal,
+ tasksPending,
+ tasksActive,
+ tasksCompleted,
+ tasksTerminated);
+ }
+
+ @Override
+ public void close() {
+ if (isClosed) {
+ return;
+ }
+
+ isClosed = true;
+ pacer.cancel();
+ }
+
+ /**
+ * Enqueue the specified [task] to be scheduled onto a host.
+ */
+ SchedulingRequest schedule(ServiceTask task) {
+ LOGGER.debug("Enqueueing task {} to be assigned to host", task.getUid());
+
+ long now = clock.millis();
+ SchedulingRequest request = new SchedulingRequest(task, now);
+
+ task.launchedAt = Instant.ofEpochMilli(now);
+ taskQueue.add(request);
+ tasksPending++;
+ requestSchedulingCycle();
+ return request;
+ }
+
+ void delete(ServiceFlavor flavor) {
+ flavorById.remove(flavor.getUid());
+ flavors.remove(flavor);
+ }
+
+ void delete(ServiceImage image) {
+ imageById.remove(image.getUid());
+ images.remove(image);
+ }
+
+ void delete(ServiceTask task) {
+ completedTasks.remove(task);
+ taskById.remove(task.getUid());
+ tasks.remove(task);
+ }
+
+ /**
+ * Indicate that a new scheduling cycle is needed due to a change to the service's state.
+ */
+ private void requestSchedulingCycle() {
+ // Bail out in case the queue is empty.
+ if (taskQueue.isEmpty()) {
+ return;
+ }
+
+ pacer.enqueue();
+ }
+
+ /**
+ * Run a single scheduling iteration.
+ */
+ private void doSchedule() {
+ // reorder tasks
+
+ while (!taskQueue.isEmpty()) {
+ SchedulingRequest request = taskQueue.peek();
+
+ if (request.isCancelled) {
+ taskQueue.poll();
+ tasksPending--;
+ continue;
+ }
+
+ final ServiceTask task = request.task;
+
+ if (task.getNumFailures() >= maxNumFailures) {
+ LOGGER.warn("task {} has been terminated because it failed {} times", task, task.getNumFailures());
+
+ taskQueue.poll();
+ tasksPending--;
+ tasksTerminated++;
+ task.setState(TaskState.TERMINATED);
+ tasksToRemove.add(task);
+ continue;
+ }
+
+ final ServiceFlavor flavor = task.getFlavor();
+ final HostView hv = scheduler.select(request.task);
+
+ if (hv == null || !hv.getHost().canFit(task)) {
+ LOGGER.trace("Task {} selected for scheduling but no capacity available for it at the moment", task);
+
+ if (flavor.getMemorySize() > maxMemory || flavor.getCoreCount() > maxCores) {
+ // Remove the incoming image
+ taskQueue.poll();
+ tasksPending--;
+
+ LOGGER.warn("Failed to spawn {}: does not fit", task);
+
+ task.setState(TaskState.FAILED);
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ SimHost host = hv.getHost();
+
+ // Remove request from queue
+ taskQueue.poll();
+ tasksPending--;
+
+ LOGGER.info("Assigned task {} to host {}", task, host);
+
+ try {
+ task.host = host;
+
+ host.spawn(task);
+ // host.start(task);
+
+ tasksActive++;
+ attemptsSuccess++;
+
+ hv.instanceCount++;
+ hv.provisionedCores += flavor.getCoreCount();
+ hv.availableMemory -= flavor.getMemorySize();
+
+ activeTasks.put(task, host);
+ } catch (Exception cause) {
+ LOGGER.error("Failed to deploy VM", cause);
+ attemptsFailure++;
+ }
+ }
+ }
+
+ /**
+ * Builder class for a {@link ComputeService}.
+ */
+ public static class Builder {
+ private final Dispatcher dispatcher;
+ private final ComputeScheduler computeScheduler;
+ private Duration quantum = Duration.ofSeconds(1);
+ private int maxNumFailures = 10;
+
+ Builder(Dispatcher dispatcher, ComputeScheduler computeScheduler) {
+ this.dispatcher = dispatcher;
+ this.computeScheduler = computeScheduler;
+ }
+
+ /**
+ * Set the scheduling quantum of the service.
+ */
+ public Builder withQuantum(Duration quantum) {
+ this.quantum = quantum;
+ return this;
+ }
+
+ public Builder withMaxNumFailures(int maxNumFailures) {
+ this.maxNumFailures = maxNumFailures;
+ return this;
+ }
+
+ /**
+ * Build a {@link ComputeService}.
+ */
+ public ComputeService build() {
+ return new ComputeService(dispatcher, computeScheduler, quantum, maxNumFailures);
+ }
+ }
+
+ /**
+ * Implementation of {@link ComputeClient} using a {@link ComputeService}.
+ */
+ public static class ComputeClient {
+ private final ComputeService service;
+ private boolean isClosed;
+
+ ComputeClient(ComputeService service) {
+ this.service = service;
+ }
+
+ /**
+ * Method to check if the client is still open and throw an exception if it is not.
+ */
+ private void checkOpen() {
+ if (isClosed) {
+ throw new IllegalStateException("Client is already closed");
+ }
+ }
+
+ @NotNull
+ public List<Flavor> queryFlavors() {
+ checkOpen();
+ return new ArrayList<>(service.flavors);
+ }
+
+ public Flavor findFlavor(@NotNull UUID id) {
+ checkOpen();
+
+ return service.flavorById.get(id);
+ }
+
+ @NotNull
+ public Flavor newFlavor(@NotNull String name, int cpuCount, long memorySize, @NotNull Map<String, ?> meta) {
+ checkOpen();
+
+ final ComputeService service = this.service;
+ UUID uid = new UUID(service.clock.millis(), service.random.nextLong());
+ ServiceFlavor flavor = new ServiceFlavor(service, uid, name, cpuCount, memorySize, meta);
+
+ service.flavorById.put(uid, flavor);
+ service.flavors.add(flavor);
+
+ return flavor;
+ }
+
+ @NotNull
+ public List<Image> queryImages() {
+ checkOpen();
+
+ return new ArrayList<>(service.images);
+ }
+
+ public Image findImage(@NotNull UUID id) {
+ checkOpen();
+
+ return service.imageById.get(id);
+ }
+
+ public Image newImage(@NotNull String name) {
+ return newImage(name, Collections.emptyMap(), Collections.emptyMap());
+ }
+
+ @NotNull
+ public Image newImage(@NotNull String name, @NotNull Map<String, String> labels, @NotNull Map<String, ?> meta) {
+ checkOpen();
+
+ final ComputeService service = this.service;
+ UUID uid = new UUID(service.clock.millis(), service.random.nextLong());
+
+ ServiceImage image = new ServiceImage(service, uid, name, labels, meta);
+
+ service.imageById.put(uid, image);
+ service.images.add(image);
+
+ return image;
+ }
+
+ @NotNull
+ public ServiceTask newTask(
+ @NotNull String name,
+ @NotNull Flavor flavor,
+ @NotNull Workload workload,
+ @NotNull Map<String, ?> meta) {
+ checkOpen();
+
+ final ComputeService service = this.service;
+ UUID uid = new UUID(service.clock.millis(), service.random.nextLong());
+
+ final ServiceFlavor internalFlavor =
+ Objects.requireNonNull(service.flavorById.get(flavor.getUid()), "Unknown flavor");
+
+ ServiceTask task = new ServiceTask(service, uid, name, internalFlavor, workload, meta);
+
+ service.taskById.put(uid, task);
+ service.tasks.add(task);
+
+ service.tasksTotal++;
+
+ task.start();
+
+ return task;
+ }
+
+ @Nullable
+ public ServiceTask findTask(@NotNull UUID id) {
+ checkOpen();
+ return service.taskById.get(id);
+ }
+
+ @NotNull
+ public List<ServiceTask> queryTasks() {
+ checkOpen();
+
+ return new ArrayList<>(service.tasks);
+ }
+
+ public void close() {
+ isClosed = true;
+ }
+
+ @Override
+ public String toString() {
+ return "ComputeService.Client";
+ }
+
+ @Nullable
+ public void rescheduleTask(@NotNull ServiceTask task, @NotNull Workload workload) {
+ ServiceTask internalTask = findTask(task.getUid());
+ // SimHost from = service.lookupHost(internalTask);
+
+ // from.delete(internalTask);
+
+ internalTask.host = null;
+
+ internalTask.setWorkload(workload);
+ internalTask.start();
+ }
+ }
+
+ /**
+ * A request to schedule a {@link ServiceTask} onto one of the {@link SimHost}s.
+ */
+ static class SchedulingRequest {
+ final ServiceTask task;
+ final long submitTime;
+
+ boolean isCancelled;
+
+ SchedulingRequest(ServiceTask task, long submitTime) {
+ this.task = task;
+ this.submitTime = submitTime;
+ }
+ }
+}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/HostView.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/HostView.java
new file mode 100644
index 00000000..f4aa9c70
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/HostView.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.service;
+
+import org.opendc.compute.simulator.host.SimHost;
+
+/**
+ * A view of a {@link SimHost} as seen from the {@link ComputeService}.
+ */
+public class HostView {
+ private final SimHost host;
+ int instanceCount;
+ long availableMemory;
+ int provisionedCores;
+
+ /**
+ * Construct a {@link HostView} instance.
+ *
+ * @param host The host to create a view of.
+ */
+ public HostView(SimHost host) {
+ this.host = host;
+ this.availableMemory = host.getModel().memoryCapacity();
+ }
+
+ /**
+ * The {@link SimHost} this is a view of.
+ */
+ public SimHost getHost() {
+ return host;
+ }
+
+ /**
+ * Return the number of instances on this host.
+ */
+ public int getInstanceCount() {
+ return instanceCount;
+ }
+
+ /**
+ * Return the available memory of the host.
+ */
+ public long getAvailableMemory() {
+ return availableMemory;
+ }
+
+ /**
+ * Return the provisioned cores on the host.
+ */
+ public int getProvisionedCores() {
+ return provisionedCores;
+ }
+
+ @Override
+ public String toString() {
+ return "HostView[host=" + host + "]";
+ }
+}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ServiceFlavor.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ServiceFlavor.java
new file mode 100644
index 00000000..eddde87e
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ServiceFlavor.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.service;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+import org.jetbrains.annotations.NotNull;
+import org.opendc.compute.api.Flavor;
+
+/**
+ * Implementation of {@link Flavor} provided by {@link ComputeService}.
+ */
+public final class ServiceFlavor implements Flavor {
+ private final ComputeService service;
+ private final UUID uid;
+ private final String name;
+ private final int coreCount;
+ private final long memorySize;
+ private final Map<String, ?> meta;
+
+ ServiceFlavor(ComputeService service, UUID uid, String name, int coreCount, long memorySize, Map<String, ?> meta) {
+ this.service = service;
+ this.uid = uid;
+ this.name = name;
+ this.coreCount = coreCount;
+ this.memorySize = memorySize;
+ this.meta = meta;
+ }
+
+ @Override
+ public int getCoreCount() {
+ return coreCount;
+ }
+
+ @Override
+ public long getMemorySize() {
+ return memorySize;
+ }
+
+ @NotNull
+ @Override
+ public UUID getUid() {
+ return uid;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @NotNull
+ @Override
+ public Map<String, Object> getMeta() {
+ return Collections.unmodifiableMap(meta);
+ }
+
+ @Override
+ public void reload() {
+ // No-op: this object is the source-of-truth
+ }
+
+ @Override
+ public void delete() {
+ service.delete(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ServiceFlavor flavor = (ServiceFlavor) o;
+ return service.equals(flavor.service) && uid.equals(flavor.uid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(service, uid);
+ }
+
+ @Override
+ public String toString() {
+ return "Flavor[uid=" + uid + ",name=" + name + "]";
+ }
+}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ServiceImage.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ServiceImage.java
new file mode 100644
index 00000000..dffa4356
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ServiceImage.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.service;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+import org.jetbrains.annotations.NotNull;
+import org.opendc.compute.api.Image;
+
+/**
+ * Implementation of {@link Image} provided by {@link ComputeService}.
+ */
+public final class ServiceImage implements Image {
+ private final ComputeService service;
+ private final UUID uid;
+ private final String name;
+ private final Map<String, String> labels;
+ private final Map<String, ?> meta;
+
+ ServiceImage(ComputeService service, UUID uid, String name, Map<String, String> labels, Map<String, ?> meta) {
+ this.service = service;
+ this.uid = uid;
+ this.name = name;
+ this.labels = labels;
+ this.meta = meta;
+ }
+
+ @NotNull
+ @Override
+ public UUID getUid() {
+ return uid;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @NotNull
+ @Override
+ public Map<String, Object> getMeta() {
+ return Collections.unmodifiableMap(meta);
+ }
+
+ @Override
+ public void reload() {
+ // No-op: this object is the source-of-truth
+ }
+
+ @Override
+ public void delete() {
+ service.delete(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ServiceImage image = (ServiceImage) o;
+ return service.equals(image.service) && uid.equals(image.uid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(service, uid);
+ }
+
+ @Override
+ public String toString() {
+ return "Image[uid=" + uid + ",name=" + name + "]";
+ }
+}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ServiceTask.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ServiceTask.java
new file mode 100644
index 00000000..f39142eb
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/service/ServiceTask.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.service;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.opendc.compute.api.TaskState;
+import org.opendc.compute.simulator.TaskWatcher;
+import org.opendc.compute.simulator.host.SimHost;
+import org.opendc.simulator.compute.workload.Workload;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of {@link ServiceTask} provided by {@link ComputeService}.
+ */
+public class ServiceTask {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ServiceTask.class);
+
+ private final ComputeService service;
+ private final UUID uid;
+
+ private final String name;
+ private final ServiceFlavor flavor;
+ public Workload workload;
+
+ private Map<String, ?> meta; // TODO: remove this
+
+ private final List<TaskWatcher> watchers = new ArrayList<>();
+ private TaskState state = TaskState.CREATED;
+ Instant launchedAt = null;
+ Instant createdAt;
+ Instant finishedAt;
+ SimHost host = null;
+ private ComputeService.SchedulingRequest request = null;
+
+ private int numFailures = 0;
+
+ ServiceTask(
+ ComputeService service,
+ UUID uid,
+ String name,
+ ServiceFlavor flavor,
+ Workload workload,
+ Map<String, ?> meta) {
+ this.service = service;
+ this.uid = uid;
+ this.name = name;
+ this.flavor = flavor;
+ this.workload = workload;
+ this.meta = meta;
+
+ this.createdAt = this.service.getClock().instant();
+ }
+
+ @NotNull
+ public UUID getUid() {
+ return uid;
+ }
+
+ @NotNull
+ public String getName() {
+ return name;
+ }
+
+ @NotNull
+ public ServiceFlavor getFlavor() {
+ return flavor;
+ }
+
+ @NotNull
+ public Map<String, Object> getMeta() {
+ return Collections.unmodifiableMap(meta);
+ }
+
+ public void setWorkload(Workload newWorkload) {
+ this.workload = newWorkload;
+ }
+
+ @NotNull
+ public TaskState getState() {
+ return state;
+ }
+
+ @Nullable
+ public Instant getLaunchedAt() {
+ return launchedAt;
+ }
+
+ @Nullable
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ @Nullable
+ public Instant getFinishedAt() {
+ return finishedAt;
+ }
+
+ /**
+ * Return the {@link SimHost} on which the task is running or <code>null</code> if it is not running on a host.
+ */
+ public SimHost getHost() {
+ return host;
+ }
+
+ public void setHost(SimHost host) {
+ this.host = host;
+ }
+
+ public int getNumFailures() {
+ return this.numFailures;
+ }
+
+ public void start() {
+ switch (state) {
+ case PROVISIONING:
+ LOGGER.debug("User tried to start task but request is already pending: doing nothing");
+ case RUNNING:
+ LOGGER.debug("User tried to start task but task is already running");
+ break;
+ case COMPLETED:
+ case TERMINATED:
+ LOGGER.warn("User tried to start deleted task");
+ throw new IllegalStateException("Task is deleted");
+ case CREATED:
+ LOGGER.info("User requested to start task {}", uid);
+ setState(TaskState.PROVISIONING);
+ assert request == null : "Scheduling request already active";
+ request = service.schedule(this);
+ break;
+ case FAILED:
+ LOGGER.info("User requested to start task after failure {}", uid);
+ setState(TaskState.PROVISIONING);
+ request = service.schedule(this);
+ break;
+ }
+ }
+
+ public void watch(@NotNull TaskWatcher watcher) {
+ watchers.add(watcher);
+ }
+
+ public void unwatch(@NotNull TaskWatcher watcher) {
+ watchers.remove(watcher);
+ }
+
+ public void delete() {
+ cancelProvisioningRequest();
+ final SimHost host = this.host;
+ if (host != null) {
+ host.delete(this);
+ }
+ service.delete(this);
+
+ this.setState(TaskState.DELETED);
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ServiceTask task = (ServiceTask) o;
+ return service.equals(task.service) && uid.equals(task.uid);
+ }
+
+ public int hashCode() {
+ return Objects.hash(service, uid);
+ }
+
+ public String toString() {
+ return "Task[uid=" + uid + ",name=" + name + ",state=" + state + "]";
+ }
+
+ void setState(TaskState newState) {
+ if (this.state == newState) {
+ return;
+ }
+
+ for (TaskWatcher watcher : watchers) {
+ watcher.onStateChanged(this, newState);
+ }
+ if (newState == TaskState.FAILED) {
+ this.numFailures++;
+ }
+
+ if ((newState == TaskState.COMPLETED) || newState == TaskState.FAILED) {
+ this.finishedAt = this.service.getClock().instant();
+ }
+
+ this.state = newState;
+ }
+
+ /**
+ * Cancel the provisioning request if active.
+ */
+ private void cancelProvisioningRequest() {
+ final ComputeService.SchedulingRequest request = this.request;
+ if (request != null) {
+ this.request = null;
+ request.isCancelled = true;
+ }
+ }
+}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/GuestCpuStats.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/GuestCpuStats.java
new file mode 100644
index 00000000..ea37f5f2
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/GuestCpuStats.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.telemetry;
+
+/**
+ * Statistics about the CPUs of a guest.
+ *
+ * @param activeTime The cumulative time (in seconds) that the CPUs of the guest were actively running.
+ * @param idleTime The cumulative time (in seconds) the CPUs of the guest were idle.
+ * @param stealTime The cumulative CPU time (in seconds) that the guest was ready to run, but not granted time by the host.
+ * @param lostTime The cumulative CPU time (in seconds) that was lost due to interference with other machines.
+ * @param capacity The available CPU capacity of the guest (in MHz).
+ * @param usage Amount of CPU resources (in MHz) actually used by the guest.
+ * @param utilization The utilization of the CPU resources (in %) relative to the total CPU capacity.
+ */
+public record GuestCpuStats(
+ long activeTime,
+ long idleTime,
+ long stealTime,
+ long lostTime,
+ float capacity,
+ float usage,
+ float utilization) {}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/GuestSystemStats.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/GuestSystemStats.java
new file mode 100644
index 00000000..0d51e223
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/GuestSystemStats.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.telemetry;
+
+import java.time.Duration;
+import java.time.Instant;
+
+/**
+ * System-level statistics of a guest.
+ *
+ * @param uptime The cumulative uptime of the guest since last boot (in ms).
+ * @param downtime The cumulative downtime of the guest since last boot (in ms).
+ * @param bootTime The time at which the guest booted.
+ */
+public record GuestSystemStats(Duration uptime, Duration downtime, Instant bootTime) {}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/HostCpuStats.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/HostCpuStats.java
new file mode 100644
index 00000000..3f2aab78
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/HostCpuStats.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.telemetry;
+
+/**
+ * Statistics about the CPUs of a host.
+ *
+ * @param activeTime The cumulative time (in seconds) that the CPUs of the host were actively running.
+ * @param idleTime The cumulative time (in seconds) the CPUs of the host were idle.
+ * @param stealTime The cumulative CPU time (in seconds) that virtual machines were ready to run, but were not able to.
+ * @param lostTime The cumulative CPU time (in seconds) that was lost due to interference between virtual machines.
+ * @param capacity The available CPU capacity of the host (in MHz).
+ * @param demand Amount of CPU resources (in MHz) the guests would use if there were no CPU contention or CPU
+ * limits.
+ * @param usage Amount of CPU resources (in MHz) actually used by the host.
+ * @param utilization The utilization of the CPU resources (in %) relative to the total CPU capacity.
+ */
+public record HostCpuStats(
+ long activeTime,
+ long idleTime,
+ long stealTime,
+ long lostTime,
+ float capacity,
+ float demand,
+ float usage,
+ float utilization) {}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/HostSystemStats.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/HostSystemStats.java
new file mode 100644
index 00000000..353e62fa
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/HostSystemStats.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.telemetry;
+
+import java.time.Duration;
+import java.time.Instant;
+
+/**
+ * System-level statistics of a host.
+ *
+ * @param uptime The cumulative uptime of the host since last boot (in ms).
+ * @param downtime The cumulative downtime of the host since last boot (in ms).
+ * @param bootTime The time at which the task started.
+ * @param powerDraw Instantaneous power draw of the system (in W).
+ * @param energyUsage The cumulative energy usage of the system (in J).
+ * @param guestsTerminated The number of guests that are in a terminated state.
+ * @param guestsRunning The number of guests that are in a running state.
+ * @param guestsError The number of guests that are in an error state.
+ * @param guestsInvalid The number of guests that are in an unknown state.
+ */
+public record HostSystemStats(
+ Duration uptime,
+ Duration downtime,
+ Instant bootTime,
+ float powerDraw,
+ float energyUsage,
+ int guestsTerminated,
+ int guestsRunning,
+ int guestsError,
+ int guestsInvalid) {}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/SchedulerStats.java b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/SchedulerStats.java
new file mode 100644
index 00000000..9d44a4b8
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/java/org/opendc/compute/simulator/telemetry/SchedulerStats.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.telemetry;
+
+/**
+ * Statistics about the scheduling component of the [ComputeService].
+ *
+ * @param hostsAvailable The number of hosts currently available for scheduling.
+ * @param hostsUnavailable The number of hosts unavailable for scheduling.
+ * @param attemptsSuccess Scheduling attempts that resulted into an allocation onto a host.
+ * @param attemptsFailure The number of failed scheduling attempt due to any reason
+ * @param tasksTotal The number of tasks registered with the service.
+ * @param tasksPending The number of tasks that are pending to be scheduled.
+ * @param tasksActive The number of tasks that are currently managed by the service and running.
+ */
+public record SchedulerStats(
+ int hostsAvailable,
+ int hostsUnavailable,
+ long attemptsSuccess,
+ long attemptsFailure,
+ int tasksTotal,
+ int tasksPending,
+ int tasksActive,
+ int tasksCompleted,
+ int tasksTerminated) {}