summaryrefslogtreecommitdiff
path: root/simulator/opendc-compute/opendc-compute-service/src
diff options
context:
space:
mode:
authorFabian Mastenbroek <mail.fabianm@gmail.com>2021-03-07 15:44:36 +0100
committerFabian Mastenbroek <mail.fabianm@gmail.com>2021-03-07 16:13:16 +0100
commit9bb91897404bbeac1d5f7a7f890abd3a9d5d9084 (patch)
tree9e492df044306b6230444b73f2c3db59ef45440e /simulator/opendc-compute/opendc-compute-service/src
parentdfbca195cbe1d6c4eebe7ccd4cc707c84ac43e79 (diff)
compute: Move ComputeService implementation in service module
This change introduces the ComputeService interface (previously VirtProvisioningService) and provides a central implementation in opendc-compute-service. Previously, the implementation of this interface was bound to the simulator package, which meant that independent business logic could not be re-used without importing the simulator code.
Diffstat (limited to 'simulator/opendc-compute/opendc-compute-service/src')
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/ComputeService.kt92
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/ComputeServiceEvent.kt47
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/Host.kt104
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostEvent.kt72
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostListener.kt41
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostModel.kt31
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostState.kt38
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/HypervisorAvailableEvent.kt31
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/HypervisorUnavailableEvent.kt31
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmScheduledEvent.kt30
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmStoppedEvent.kt30
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmSubmissionEvent.kt32
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmSubmissionInvalidEvent.kt30
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/internal/ClientServer.kt86
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/internal/ComputeServiceImpl.kt414
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/internal/HostView.kt35
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/AllocationPolicy.kt49
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/AvailableCoreMemoryAllocationPolicy.kt38
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/AvailableMemoryAllocationPolicy.kt37
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ComparableAllocationPolicyLogic.kt49
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/NumberOfActiveServersAllocationPolicy.kt37
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ProvisionedCoresAllocationPolicy.kt40
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/RandomAllocationPolicy.kt48
-rw-r--r--simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ReplayAllocationPolicy.kt57
24 files changed, 1499 insertions, 0 deletions
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/ComputeService.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/ComputeService.kt
new file mode 100644
index 00000000..593e4b56
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/ComputeService.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2021 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.service
+
+import kotlinx.coroutines.flow.Flow
+import org.opendc.compute.api.ComputeClient
+import org.opendc.compute.service.driver.Host
+import org.opendc.compute.service.internal.ComputeServiceImpl
+import org.opendc.compute.service.scheduler.AllocationPolicy
+import org.opendc.trace.core.EventTracer
+import java.time.Clock
+import kotlin.coroutines.CoroutineContext
+
+/**
+ * The [ComputeService] hosts the API implementation of the OpenDC Compute service.
+ */
+public interface ComputeService : AutoCloseable {
+ /**
+ * The events emitted by the service.
+ */
+ public val events: Flow<ComputeServiceEvent>
+
+ /**
+ * The hosts that are used by the compute service.
+ */
+ public val hosts: Set<Host>
+
+ /**
+ * The number of hosts available in the system.
+ */
+ public val hostCount: Int
+
+ /**
+ * Create a new [ComputeClient] to control the compute service.
+ */
+ public fun newClient(): ComputeClient
+
+ /**
+ * Add a [host] to the scheduling pool of the compute service.
+ */
+ public fun addHost(host: Host)
+
+ /**
+ * Remove a [host] from the scheduling pool of the compute service.
+ */
+ public fun removeHost(host: Host)
+
+ /**
+ * Terminate the lifecycle of the compute service, stopping all running instances.
+ */
+ public override fun close()
+
+ public companion object {
+ /**
+ * Construct a new [ComputeService] implementation.
+ *
+ * @param context The [CoroutineContext] to use in the service.
+ * @param clock The clock instance to use.
+ * @param tracer The event tracer to use.
+ * @param allocationPolicy The allocation policy to use.
+ */
+ public operator fun invoke(
+ context: CoroutineContext,
+ clock: Clock,
+ tracer: EventTracer,
+ allocationPolicy: AllocationPolicy,
+ schedulingQuantum: Long = 300000,
+ ): ComputeService {
+ return ComputeServiceImpl(context, clock, tracer, allocationPolicy, schedulingQuantum)
+ }
+ }
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/ComputeServiceEvent.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/ComputeServiceEvent.kt
new file mode 100644
index 00000000..193008a7
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/ComputeServiceEvent.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2021 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.service
+
+/**
+ * An event that is emitted by the [ComputeService].
+ */
+public sealed class ComputeServiceEvent {
+ /**
+ * The service that has emitted the event.
+ */
+ public abstract val provisioner: ComputeService
+
+ /**
+ * An event emitted for writing metrics.
+ */
+ public data class MetricsAvailable(
+ override val provisioner: ComputeService,
+ public val totalHostCount: Int,
+ public val availableHostCount: Int,
+ public val totalVmCount: Int,
+ public val activeVmCount: Int,
+ public val inactiveVmCount: Int,
+ public val waitingVmCount: Int,
+ public val failedVmCount: Int
+ ) : ComputeServiceEvent()
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/Host.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/Host.kt
new file mode 100644
index 00000000..2cd91144
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/Host.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2021 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.service.driver
+
+import kotlinx.coroutines.flow.Flow
+import org.opendc.compute.api.Server
+import java.util.*
+
+/**
+ * Base interface for representing compute resources that host virtualized [Server] instances.
+ */
+public interface Host {
+ /**
+ * A unique identifier representing the host.
+ */
+ public val uid: UUID
+
+ /**
+ * The name of this host.
+ */
+ public val name: String
+
+ /**
+ * The machine model of the host.
+ */
+ public val model: HostModel
+
+ /**
+ * The state of the host.
+ */
+ public val state: HostState
+
+ /**
+ * The events emitted by the driver.
+ */
+ public val events: Flow<HostEvent>
+
+ /**
+ * Determine whether the specified [instance][server] can still fit on this host.
+ */
+ public fun canFit(server: Server): Boolean
+
+ /**
+ * Register the specified [instance][server] on the host.
+ *
+ * Once the method returns, the instance should be running if [start] is true or else the instance should be
+ * stopped.
+ */
+ public suspend fun spawn(server: Server, start: Boolean = true)
+
+ /**
+ * Determine whether the specified [instance][server] exists on the host.
+ */
+ public operator fun contains(server: Server): Boolean
+
+ /**
+ * Stat the server [instance][server] if it is currently not running on this host.
+ *
+ * @throws IllegalArgumentException if the server is not present on the host.
+ */
+ public suspend fun start(server: Server)
+
+ /**
+ * Stop the server [instance][server] if it is currently running on this host.
+ *
+ * @throws IllegalArgumentException if the server is not present on the host.
+ */
+ public suspend fun stop(server: Server)
+
+ /**
+ * Terminate the specified [instance][server] on this host and cleanup all resources associated with it.
+ */
+ public suspend fun terminate(server: Server)
+
+ /**
+ * Add a [HostListener] to this host.
+ */
+ public fun addListener(listener: HostListener)
+
+ /**
+ * Remove a [HostListener] from this host.
+ */
+ public fun removeListener(listener: HostListener)
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostEvent.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostEvent.kt
new file mode 100644
index 00000000..97350679
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostEvent.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2021 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.service.driver
+
+/**
+ * An event that is emitted by a [Host].
+ */
+public sealed class HostEvent {
+ /**
+ * The driver that emitted the event.
+ */
+ public abstract val driver: Host
+
+ /**
+ * This event is emitted when the number of active servers on the server managed by this driver is updated.
+ *
+ * @property driver The driver that emitted the event.
+ * @property numberOfActiveServers The number of active servers.
+ * @property availableMemory The available memory, in MB.
+ */
+ public data class VmsUpdated(
+ override val driver: Host,
+ public val numberOfActiveServers: Int,
+ public val availableMemory: Long
+ ) : HostEvent()
+
+ /**
+ * This event is emitted when a slice is finished.
+ *
+ * @property driver The driver that emitted the event.
+ * @property requestedBurst The total requested CPU time (can be above capacity).
+ * @property grantedBurst The actual total granted capacity, which might be lower than the requested burst due to
+ * the hypervisor being interrupted during a slice.
+ * @property overcommissionedBurst The CPU time that the hypervisor could not grant to the virtual machine since
+ * it did not have the capacity.
+ * @property interferedBurst The sum of CPU time that virtual machines could not utilize due to performance
+ * interference.
+ * @property cpuUsage CPU use in megahertz.
+ * @property cpuDemand CPU demand in megahertz.
+ * @property numberOfDeployedImages The number of images deployed on this hypervisor.
+ */
+ public data class SliceFinished(
+ override val driver: Host,
+ public val requestedBurst: Long,
+ public val grantedBurst: Long,
+ public val overcommissionedBurst: Long,
+ public val interferedBurst: Long,
+ public val cpuUsage: Double,
+ public val cpuDemand: Double,
+ public val numberOfDeployedImages: Int,
+ ) : HostEvent()
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostListener.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostListener.kt
new file mode 100644
index 00000000..f076cae3
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostListener.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2021 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.service.driver
+
+import org.opendc.compute.api.Server
+import org.opendc.compute.api.ServerState
+
+/**
+ * Listener interface for events originating from a [Host].
+ */
+public interface HostListener {
+ /**
+ * This method is invoked when the state of an [instance][server] on [host] changes.
+ */
+ public fun onStateChanged(host: Host, server: Server, newState: ServerState) {}
+
+ /**
+ * This method is invoked when the state of a [Host] has changed.
+ */
+ public fun onStateChanged(host: Host, newState: HostState) {}
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostModel.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostModel.kt
new file mode 100644
index 00000000..5632a55e
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostModel.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021 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.service.driver
+
+/**
+ * Describes the static machine properties of the host.
+ *
+ * @property vcpuCount The number of logical processing cores available for this host.
+ * @property memorySize The amount of memory available for this host in MB.
+ */
+public data class HostModel(public val cpuCount: Int, public val memorySize: Long)
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostState.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostState.kt
new file mode 100644
index 00000000..6d85ee2d
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/driver/HostState.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021 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.service.driver
+
+/**
+ * The state of a host.
+ */
+public enum class HostState {
+ /**
+ * The host is up.
+ */
+ UP,
+
+ /**
+ * The host is down.
+ */
+ DOWN
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/HypervisorAvailableEvent.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/HypervisorAvailableEvent.kt
new file mode 100644
index 00000000..a7974062
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/HypervisorAvailableEvent.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 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.service.events
+
+import org.opendc.trace.core.Event
+import java.util.*
+
+/**
+ * This event is emitted when a hypervisor has become available.
+ */
+public class HypervisorAvailableEvent(public val uid: UUID) : Event()
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/HypervisorUnavailableEvent.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/HypervisorUnavailableEvent.kt
new file mode 100644
index 00000000..75bb09ed
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/HypervisorUnavailableEvent.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 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.service.events
+
+import org.opendc.trace.core.Event
+import java.util.*
+
+/**
+ * This event is emitted when a hypervisor has become unavailable.
+ */
+public class HypervisorUnavailableEvent(public val uid: UUID) : Event()
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmScheduledEvent.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmScheduledEvent.kt
new file mode 100644
index 00000000..f59c74b7
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmScheduledEvent.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 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.service.events
+
+import org.opendc.trace.core.Event
+
+/**
+ * This event is emitted when a virtual machine has successfully been scheduled on a hypervisor.
+ */
+public class VmScheduledEvent(public val name: String) : Event()
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmStoppedEvent.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmStoppedEvent.kt
new file mode 100644
index 00000000..eaf0736b
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmStoppedEvent.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 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.service.events
+
+import org.opendc.trace.core.Event
+
+/**
+ * This event is emitted when a virtual machine has stopped running.
+ */
+public class VmStoppedEvent(public val name: String) : Event()
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmSubmissionEvent.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmSubmissionEvent.kt
new file mode 100644
index 00000000..fa0a8a13
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmSubmissionEvent.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 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.service.events
+
+import org.opendc.compute.api.Flavor
+import org.opendc.compute.api.Image
+import org.opendc.trace.core.Event
+
+/**
+ * This event is emitted when a virtual machine is submitted to the provisioning service.
+ */
+public class VmSubmissionEvent(public val name: String, public val image: Image, public val flavor: Flavor) : Event()
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmSubmissionInvalidEvent.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmSubmissionInvalidEvent.kt
new file mode 100644
index 00000000..52b91616
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/events/VmSubmissionInvalidEvent.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 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.service.events
+
+import org.opendc.trace.core.Event
+
+/**
+ * An event that is emitted when the submission is deemed to be invalid.
+ */
+public class VmSubmissionInvalidEvent(public val name: String) : Event()
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/internal/ClientServer.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/internal/ClientServer.kt
new file mode 100644
index 00000000..f84b7435
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/internal/ClientServer.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2021 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.service.internal
+
+import org.opendc.compute.api.Flavor
+import org.opendc.compute.api.Image
+import org.opendc.compute.api.Server
+import org.opendc.compute.api.ServerState
+import org.opendc.compute.api.ServerWatcher
+import java.util.*
+
+/**
+ * A [Server] implementation that is passed to clients but delegates its implementation to another class.
+ */
+internal class ClientServer(private val delegate: Server) : Server, ServerWatcher {
+ private val watchers = mutableListOf<ServerWatcher>()
+
+ override val uid: UUID = delegate.uid
+
+ override var name: String = delegate.name
+ private set
+
+ override var flavor: Flavor = delegate.flavor
+ private set
+
+ override var image: Image = delegate.image
+ private set
+
+ override var tags: Map<String, String> = delegate.tags.toMap()
+ private set
+
+ override var state: ServerState = delegate.state
+ private set
+
+ override fun watch(watcher: ServerWatcher) {
+ if (watchers.isEmpty()) {
+ delegate.watch(this)
+ }
+
+ watchers += watcher
+ }
+
+ override fun unwatch(watcher: ServerWatcher) {
+ watchers += watcher
+
+ if (watchers.isEmpty()) {
+ delegate.unwatch(this)
+ }
+ }
+
+ override suspend fun refresh() {
+ name = delegate.name
+ flavor = delegate.flavor
+ image = delegate.image
+ tags = delegate.tags
+ state = delegate.state
+ }
+
+ override fun onStateChanged(server: Server, newState: ServerState) {
+ val watchers = watchers
+
+ for (watcher in watchers) {
+ watcher.onStateChanged(this, newState)
+ }
+ }
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/internal/ComputeServiceImpl.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/internal/ComputeServiceImpl.kt
new file mode 100644
index 00000000..69d6bb59
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/internal/ComputeServiceImpl.kt
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 2021 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.service.internal
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import mu.KotlinLogging
+import org.opendc.compute.api.*
+import org.opendc.compute.service.ComputeService
+import org.opendc.compute.service.ComputeServiceEvent
+import org.opendc.compute.service.driver.Host
+import org.opendc.compute.service.driver.HostListener
+import org.opendc.compute.service.driver.HostState
+import org.opendc.compute.service.events.*
+import org.opendc.compute.service.scheduler.AllocationPolicy
+import org.opendc.trace.core.EventTracer
+import org.opendc.utils.TimerScheduler
+import org.opendc.utils.flow.EventFlow
+import java.time.Clock
+import java.util.*
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.resume
+import kotlin.math.max
+
+/**
+ * Internal implementation of the OpenDC Compute service.
+ *
+ * @param context The [CoroutineContext] to use.
+ * @param clock The clock instance to keep track of time.
+ */
+public class ComputeServiceImpl(
+ private val context: CoroutineContext,
+ private val clock: Clock,
+ private val tracer: EventTracer,
+ private val allocationPolicy: AllocationPolicy,
+ private val schedulingQuantum: Long
+) : ComputeService, HostListener {
+ /**
+ * The [CoroutineScope] of the service bounded by the lifecycle of the service.
+ */
+ private val scope = CoroutineScope(context)
+
+ /**
+ * The logger instance of this server.
+ */
+ private val logger = KotlinLogging.logger {}
+
+ /**
+ * The [Random] instance used to generate unique identifiers for the objects.
+ */
+ private val random = Random(0)
+
+ /**
+ * A mapping from host to host view.
+ */
+ private val hostToView = mutableMapOf<Host, HostView>()
+
+ /**
+ * The available hypervisors.
+ */
+ private val availableHosts: MutableSet<HostView> = mutableSetOf()
+
+ /**
+ * The servers that should be launched by the service.
+ */
+ private val queue: Deque<LaunchRequest> = ArrayDeque()
+
+ /**
+ * The active servers in the system.
+ */
+ private val activeServers: MutableSet<Server> = mutableSetOf()
+
+ public var submittedVms: Int = 0
+ public var queuedVms: Int = 0
+ public var runningVms: Int = 0
+ public var finishedVms: Int = 0
+ public var unscheduledVms: Int = 0
+
+ private var maxCores = 0
+ private var maxMemory = 0L
+
+ /**
+ * The allocation logic to use.
+ */
+ private val allocationLogic = allocationPolicy()
+
+ override val events: Flow<ComputeServiceEvent>
+ get() = _events
+ private val _events = EventFlow<ComputeServiceEvent>()
+
+ /**
+ * The [TimerScheduler] to use for scheduling the scheduler cycles.
+ */
+ private var scheduler: TimerScheduler<Unit> = TimerScheduler(scope, clock)
+
+ override val hosts: Set<Host>
+ get() = hostToView.keys
+
+ override val hostCount: Int
+ get() = hostToView.size
+
+ override fun newClient(): ComputeClient = object : ComputeClient {
+ private var isClosed: Boolean = false
+
+ override suspend fun newServer(name: String, image: Image, flavor: Flavor): Server {
+ check(!isClosed) { "Client is closed" }
+ tracer.commit(VmSubmissionEvent(name, image, flavor))
+
+ _events.emit(
+ ComputeServiceEvent.MetricsAvailable(
+ this@ComputeServiceImpl,
+ hostCount,
+ availableHosts.size,
+ ++submittedVms,
+ runningVms,
+ finishedVms,
+ ++queuedVms,
+ unscheduledVms
+ )
+ )
+
+ return suspendCancellableCoroutine { cont ->
+ val request = LaunchRequest(createServer(name, image, flavor), cont)
+ queue += request
+ requestCycle()
+ }
+ }
+
+ override fun close() {
+ isClosed = true
+ }
+
+ override fun toString(): String = "ComputeClient"
+ }
+
+ override fun addHost(host: Host) {
+ // Check if host is already known
+ if (host in hostToView) {
+ return
+ }
+
+ val hv = HostView(host)
+ maxCores = max(maxCores, host.model.cpuCount)
+ maxMemory = max(maxMemory, host.model.memorySize)
+ hostToView[host] = hv
+
+ if (host.state == HostState.UP) {
+ availableHosts += hv
+ }
+
+ host.addListener(this)
+ }
+
+ override fun removeHost(host: Host) {
+ host.removeListener(this)
+ }
+
+ override fun close() {
+ scope.cancel()
+ }
+
+ private fun createServer(
+ name: String,
+ image: Image,
+ flavor: Flavor
+ ): Server {
+ return ServerImpl(
+ uid = UUID(random.nextLong(), random.nextLong()),
+ name = name,
+ flavor = flavor,
+ image = image
+ )
+ }
+
+ private fun requestCycle() {
+ // Bail out in case we have already requested a new cycle.
+ if (scheduler.isTimerActive(Unit)) {
+ return
+ }
+
+ // We assume that the provisioner runs at a fixed slot every time quantum (e.g t=0, t=60, t=120).
+ // This is important because the slices of the VMs need to be aligned.
+ // We calculate here the delay until the next scheduling slot.
+ val delay = schedulingQuantum - (clock.millis() % schedulingQuantum)
+
+ scheduler.startSingleTimer(Unit, delay) {
+ schedule()
+ }
+ }
+
+ private fun schedule() {
+ while (queue.isNotEmpty()) {
+ val (server, cont) = queue.peekFirst()
+ val requiredMemory = server.flavor.memorySize
+ val selectedHv = allocationLogic.select(availableHosts, server)
+
+ if (selectedHv == null || !selectedHv.host.canFit(server)) {
+ logger.trace { "Server $server selected for scheduling but no capacity available for it." }
+
+ if (requiredMemory > maxMemory || server.flavor.cpuCount > maxCores) {
+ tracer.commit(VmSubmissionInvalidEvent(server.name))
+
+ _events.emit(
+ ComputeServiceEvent.MetricsAvailable(
+ this@ComputeServiceImpl,
+ hostCount,
+ availableHosts.size,
+ submittedVms,
+ runningVms,
+ finishedVms,
+ --queuedVms,
+ ++unscheduledVms
+ )
+ )
+
+ // Remove the incoming image
+ queue.poll()
+
+ logger.warn("Failed to spawn $server: does not fit [${clock.millis()}]")
+ continue
+ } else {
+ break
+ }
+ }
+
+ logger.info { "[${clock.millis()}] Spawning $server on ${selectedHv.host.uid} ${selectedHv.host.name} ${selectedHv.host.model}" }
+ queue.poll()
+
+ // Speculatively update the hypervisor view information to prevent other images in the queue from
+ // deciding on stale values.
+ selectedHv.numberOfActiveServers++
+ selectedHv.provisionedCores += server.flavor.cpuCount
+ selectedHv.availableMemory -= requiredMemory // XXX Temporary hack
+
+ scope.launch {
+ try {
+ cont.resume(ClientServer(server))
+ selectedHv.host.spawn(server)
+ activeServers += server
+
+ tracer.commit(VmScheduledEvent(server.name))
+ _events.emit(
+ ComputeServiceEvent.MetricsAvailable(
+ this@ComputeServiceImpl,
+ hostCount,
+ availableHosts.size,
+ submittedVms,
+ ++runningVms,
+ finishedVms,
+ --queuedVms,
+ unscheduledVms
+ )
+ )
+ } catch (e: Throwable) {
+ logger.error("Failed to deploy VM", e)
+
+ selectedHv.numberOfActiveServers--
+ selectedHv.provisionedCores -= server.flavor.cpuCount
+ selectedHv.availableMemory += requiredMemory
+ }
+ }
+ }
+ }
+
+ override fun onStateChanged(host: Host, newState: HostState) {
+ when (newState) {
+ HostState.UP -> {
+ logger.debug { "[${clock.millis()}] Host ${host.uid} state changed: $newState" }
+
+ val hv = hostToView[host]
+ if (hv != null) {
+ // Corner case for when the hypervisor already exists
+ availableHosts += hv
+ }
+
+ tracer.commit(HypervisorAvailableEvent(host.uid))
+
+ _events.emit(
+ ComputeServiceEvent.MetricsAvailable(
+ this@ComputeServiceImpl,
+ hostCount,
+ availableHosts.size,
+ submittedVms,
+ runningVms,
+ finishedVms,
+ queuedVms,
+ unscheduledVms
+ )
+ )
+
+ // Re-schedule on the new machine
+ if (queue.isNotEmpty()) {
+ requestCycle()
+ }
+ }
+ HostState.DOWN -> {
+ logger.debug { "[${clock.millis()}] Host ${host.uid} state changed: $newState" }
+
+ val hv = hostToView[host] ?: return
+ availableHosts -= hv
+
+ tracer.commit(HypervisorUnavailableEvent(hv.uid))
+
+ _events.emit(
+ ComputeServiceEvent.MetricsAvailable(
+ this@ComputeServiceImpl,
+ hostCount,
+ availableHosts.size,
+ submittedVms,
+ runningVms,
+ finishedVms,
+ queuedVms,
+ unscheduledVms
+ )
+ )
+
+ if (queue.isNotEmpty()) {
+ requestCycle()
+ }
+ }
+ }
+ }
+
+ override fun onStateChanged(host: Host, server: Server, newState: ServerState) {
+ val serverImpl = server as ServerImpl
+ serverImpl.state = newState
+ serverImpl.watchers.forEach { it.onStateChanged(server, newState) }
+
+ if (newState == ServerState.SHUTOFF) {
+ logger.info { "[${clock.millis()}] Server ${server.uid} ${server.name} ${server.flavor} finished." }
+
+ tracer.commit(VmStoppedEvent(server.name))
+
+ _events.emit(
+ ComputeServiceEvent.MetricsAvailable(
+ this@ComputeServiceImpl,
+ hostCount,
+ availableHosts.size,
+ submittedVms,
+ --runningVms,
+ ++finishedVms,
+ queuedVms,
+ unscheduledVms
+ )
+ )
+
+ activeServers -= server
+ val hv = hostToView[host]
+ if (hv != null) {
+ hv.provisionedCores -= server.flavor.cpuCount
+ hv.numberOfActiveServers--
+ hv.availableMemory += server.flavor.memorySize
+ } else {
+ logger.error { "Unknown host $host" }
+ }
+
+ // Try to reschedule if needed
+ if (queue.isNotEmpty()) {
+ requestCycle()
+ }
+ }
+ }
+
+ public data class LaunchRequest(val server: Server, val cont: Continuation<Server>)
+
+ private class ServerImpl(
+ override val uid: UUID,
+ override val name: String,
+ override val flavor: Flavor,
+ override val image: Image
+ ) : Server {
+ val watchers = mutableListOf<ServerWatcher>()
+
+ override fun watch(watcher: ServerWatcher) {
+ watchers += watcher
+ }
+
+ override fun unwatch(watcher: ServerWatcher) {
+ watchers -= watcher
+ }
+
+ override suspend fun refresh() {
+ // No-op: this object is the source-of-truth
+ }
+
+ override val tags: Map<String, String> = emptyMap()
+
+ override var state: ServerState = ServerState.BUILD
+ }
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/internal/HostView.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/internal/HostView.kt
new file mode 100644
index 00000000..1bdfdf1a
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/internal/HostView.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 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.service.internal
+
+import org.opendc.compute.service.driver.Host
+import java.util.UUID
+
+public class HostView(public val host: Host) {
+ public val uid: UUID
+ get() = host.uid
+
+ public var numberOfActiveServers: Int = 0
+ public var availableMemory: Long = host.model.memorySize
+ public var provisionedCores: Int = 0
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/AllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/AllocationPolicy.kt
new file mode 100644
index 00000000..5ee4c70f
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/AllocationPolicy.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 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.service.scheduler
+
+import org.opendc.compute.api.Server
+import org.opendc.compute.service.internal.HostView
+
+/**
+ * A policy for selecting the [Node] an image should be deployed to,
+ */
+public interface AllocationPolicy {
+ /**
+ * The logic of the allocation policy.
+ */
+ public interface Logic {
+ /**
+ * Select the node on which the server should be scheduled.
+ */
+ public fun select(
+ hypervisors: Set<HostView>,
+ server: Server
+ ): HostView?
+ }
+
+ /**
+ * Builds the logic of the policy.
+ */
+ public operator fun invoke(): Logic
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/AvailableCoreMemoryAllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/AvailableCoreMemoryAllocationPolicy.kt
new file mode 100644
index 00000000..ad422415
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/AvailableCoreMemoryAllocationPolicy.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 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.service.scheduler
+
+import org.opendc.compute.service.internal.HostView
+
+/**
+ * An [AllocationPolicy] that selects the machine with the highest/lowest amount of memory per core.
+ *
+ * @param reversed An option to reverse the order of the machines (lower amount of memory scores better).
+ */
+public class AvailableCoreMemoryAllocationPolicy(private val reversed: Boolean = false) : AllocationPolicy {
+ override fun invoke(): AllocationPolicy.Logic = object : ComparableAllocationPolicyLogic {
+ override val comparator: Comparator<HostView> =
+ compareBy<HostView> { -it.availableMemory / it.host.model.cpuCount }
+ .run { if (reversed) reversed() else this }
+ }
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/AvailableMemoryAllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/AvailableMemoryAllocationPolicy.kt
new file mode 100644
index 00000000..6712b8a2
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/AvailableMemoryAllocationPolicy.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 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.service.scheduler
+
+import org.opendc.compute.service.internal.HostView
+
+/**
+ * Allocation policy that selects the node with the most available memory.
+ *
+ * @param reversed A flag to reverse the order (least amount of memory scores the best).
+ */
+public class AvailableMemoryAllocationPolicy(public val reversed: Boolean = false) : AllocationPolicy {
+ override fun invoke(): AllocationPolicy.Logic = object : ComparableAllocationPolicyLogic {
+ override val comparator: Comparator<HostView> = compareBy<HostView> { -it.availableMemory }
+ .run { if (reversed) reversed() else this }
+ }
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ComparableAllocationPolicyLogic.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ComparableAllocationPolicyLogic.kt
new file mode 100644
index 00000000..265d514d
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ComparableAllocationPolicyLogic.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 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.service.scheduler
+
+import org.opendc.compute.api.Server
+import org.opendc.compute.service.internal.HostView
+
+/**
+ * The logic for an [AllocationPolicy] that uses a [Comparator] to select the appropriate node.
+ */
+public interface ComparableAllocationPolicyLogic : AllocationPolicy.Logic {
+ /**
+ * The comparator to use.
+ */
+ public val comparator: Comparator<HostView>
+
+ override fun select(
+ hypervisors: Set<HostView>,
+ server: Server
+ ): HostView? {
+ return hypervisors.asSequence()
+ .filter { hv ->
+ val fitsMemory = hv.availableMemory >= (server.flavor.memorySize)
+ val fitsCpu = hv.host.model.cpuCount >= server.flavor.cpuCount
+ fitsMemory && fitsCpu
+ }
+ .minWithOrNull(comparator.thenBy { it.host.uid })
+ }
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/NumberOfActiveServersAllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/NumberOfActiveServersAllocationPolicy.kt
new file mode 100644
index 00000000..29eba782
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/NumberOfActiveServersAllocationPolicy.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 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.service.scheduler
+
+import org.opendc.compute.service.internal.HostView
+
+/**
+ * Allocation policy that selects the node with the least amount of active servers.
+ *
+ * @param reversed A flag to reverse the order, such that the node with the most active servers is selected.
+ */
+public class NumberOfActiveServersAllocationPolicy(public val reversed: Boolean = false) : AllocationPolicy {
+ override fun invoke(): AllocationPolicy.Logic = object : ComparableAllocationPolicyLogic {
+ override val comparator: Comparator<HostView> = compareBy<HostView> { it.numberOfActiveServers }
+ .run { if (reversed) reversed() else this }
+ }
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ProvisionedCoresAllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ProvisionedCoresAllocationPolicy.kt
new file mode 100644
index 00000000..4c196953
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ProvisionedCoresAllocationPolicy.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 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.service.scheduler
+
+import org.opendc.compute.service.internal.HostView
+
+/**
+ * An [AllocationPolicy] that takes into account the number of vCPUs that have been provisioned on this machine
+ * relative to its core count.
+ *
+ * @param reversed A flag to reverse the order of the policy, such that the machine with the most provisioned cores
+ * is selected.
+ */
+public class ProvisionedCoresAllocationPolicy(private val reversed: Boolean = false) : AllocationPolicy {
+ override fun invoke(): AllocationPolicy.Logic = object : ComparableAllocationPolicyLogic {
+ override val comparator: Comparator<HostView> =
+ compareBy<HostView> { it.provisionedCores / it.host.model.cpuCount }
+ .run { if (reversed) reversed() else this }
+ }
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/RandomAllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/RandomAllocationPolicy.kt
new file mode 100644
index 00000000..3facb182
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/RandomAllocationPolicy.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 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.service.scheduler
+
+import org.opendc.compute.api.Server
+import org.opendc.compute.service.internal.HostView
+import kotlin.random.Random
+
+/**
+ * An [AllocationPolicy] that select a random node on which the server fits.
+ */
+public class RandomAllocationPolicy(private val random: Random = Random(0)) : AllocationPolicy {
+ @OptIn(ExperimentalStdlibApi::class)
+ override fun invoke(): AllocationPolicy.Logic = object : AllocationPolicy.Logic {
+ override fun select(
+ hypervisors: Set<HostView>,
+ server: Server
+ ): HostView? {
+ return hypervisors.asIterable()
+ .filter { hv ->
+ val fitsMemory = hv.availableMemory >= (server.image.tags["required-memory"] as Long)
+ val fitsCpu = hv.host.model.cpuCount >= server.flavor.cpuCount
+ fitsMemory && fitsCpu
+ }
+ .randomOrNull(random)
+ }
+ }
+}
diff --git a/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ReplayAllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ReplayAllocationPolicy.kt
new file mode 100644
index 00000000..ed1dc662
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/ReplayAllocationPolicy.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 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.allocation
+
+import mu.KotlinLogging
+import org.opendc.compute.api.Server
+import org.opendc.compute.service.internal.HostView
+import org.opendc.compute.service.scheduler.AllocationPolicy
+
+private val logger = KotlinLogging.logger {}
+
+/**
+ * Policy replaying VM-cluster assignment.
+ *
+ * Within each cluster, the active servers on each node determine which node gets
+ * assigned the VM image.
+ */
+public class ReplayAllocationPolicy(private val vmPlacements: Map<String, String>) : AllocationPolicy {
+ override fun invoke(): AllocationPolicy.Logic = object : AllocationPolicy.Logic {
+ override fun select(
+ hypervisors: Set<HostView>,
+ server: Server
+ ): HostView? {
+ val clusterName = vmPlacements[server.name]
+ ?: throw IllegalStateException("Could not find placement data in VM placement file for VM ${server.name}")
+ val machinesInCluster = hypervisors.filter { it.host.name.contains(clusterName) }
+
+ if (machinesInCluster.isEmpty()) {
+ logger.info { "Could not find any machines belonging to cluster $clusterName for image ${server.name}, assigning randomly." }
+ return hypervisors.maxByOrNull { it.availableMemory }
+ }
+
+ return machinesInCluster.maxByOrNull { it.availableMemory }
+ ?: throw IllegalStateException("Cloud not find any machine and could not randomly assign")
+ }
+ }
+}