summaryrefslogtreecommitdiff
path: root/simulator/opendc-compute/opendc-compute-simulator
diff options
context:
space:
mode:
Diffstat (limited to 'simulator/opendc-compute/opendc-compute-simulator')
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts3
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ComputeSimExecutionContext.kt36
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/HypervisorView.kt37
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimBareMetalDriver.kt107
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt303
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHostProvisioner.kt69
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriver.kt253
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtProvisioningService.kt418
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimWorkloadImage.kt43
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AllocationPolicy.kt50
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AvailableCoreMemoryAllocationPolicy.kt38
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AvailableMemoryAllocationPolicy.kt37
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ComparableAllocationPolicyLogic.kt49
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/NumberOfActiveServersAllocationPolicy.kt37
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ProvisionedCoresAllocationPolicy.kt40
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/RandomAllocationPolicy.kt48
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ReplayAllocationPolicy.kt56
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/api/CpuPowerModel.kt2
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/models/ZeroIdlePowerDecorator.kt6
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimBareMetalDriverTest.kt25
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt (renamed from simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimVirtDriverTest.kt)82
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimProvisioningServiceTest.kt7
-rw-r--r--simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/power/CpuPowerModelTest.kt2
23 files changed, 469 insertions, 1279 deletions
diff --git a/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts b/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts
index f52d0f97..d7d5f002 100644
--- a/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts
+++ b/simulator/opendc-compute/opendc-compute-simulator/build.gradle.kts
@@ -30,7 +30,8 @@ plugins {
dependencies {
api(platform(project(":opendc-platform")))
- api(project(":opendc-compute:opendc-compute-core"))
+ api(project(":opendc-compute:opendc-compute-service"))
+ api(project(":opendc-metal"))
api(project(":opendc-simulator:opendc-simulator-compute"))
api(project(":opendc-simulator:opendc-simulator-failures"))
implementation(project(":opendc-utils"))
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ComputeSimExecutionContext.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ComputeSimExecutionContext.kt
deleted file mode 100644
index 153a86b3..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/ComputeSimExecutionContext.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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
-
-import org.opendc.compute.core.Server
-import org.opendc.simulator.compute.SimExecutionContext
-
-/**
- * Extended [SimExecutionContext] in which workloads within the OpenDC Compute module run.
- */
-public interface ComputeSimExecutionContext : SimExecutionContext {
- /**
- * The server on which the image runs.
- */
- public val server: Server
-}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/HypervisorView.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/HypervisorView.kt
deleted file mode 100644
index 1a79523e..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/HypervisorView.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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
-
-import org.opendc.compute.core.Server
-import org.opendc.compute.core.virt.driver.VirtDriver
-import java.util.UUID
-
-public class HypervisorView(
- public val uid: UUID,
- public var server: Server,
- public var numberOfActiveServers: Int,
- public var availableMemory: Long,
- public var provisionedCores: Int
-) {
- public lateinit var driver: VirtDriver
-}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimBareMetalDriver.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimBareMetalDriver.kt
index 1e459e6f..2405a8f9 100644
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimBareMetalDriver.kt
+++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimBareMetalDriver.kt
@@ -24,31 +24,23 @@ package org.opendc.compute.simulator
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
-import org.opendc.compute.core.Flavor
-import org.opendc.compute.core.Server
-import org.opendc.compute.core.ServerEvent
-import org.opendc.compute.core.ServerState
-import org.opendc.compute.core.image.EmptyImage
-import org.opendc.compute.core.image.Image
-import org.opendc.compute.core.metal.Node
-import org.opendc.compute.core.metal.NodeEvent
-import org.opendc.compute.core.metal.NodeState
-import org.opendc.compute.core.metal.driver.BareMetalDriver
+import org.opendc.compute.api.Flavor
+import org.opendc.compute.api.Image
import org.opendc.compute.simulator.power.api.CpuPowerModel
import org.opendc.compute.simulator.power.api.Powerable
import org.opendc.compute.simulator.power.models.ConstantPowerModel
-import org.opendc.core.services.ServiceRegistry
+import org.opendc.metal.Node
+import org.opendc.metal.NodeEvent
+import org.opendc.metal.NodeState
+import org.opendc.metal.driver.BareMetalDriver
import org.opendc.simulator.compute.SimBareMetalMachine
-import org.opendc.simulator.compute.SimExecutionContext
import org.opendc.simulator.compute.SimMachineModel
-import org.opendc.simulator.compute.workload.SimResourceCommand
import org.opendc.simulator.compute.workload.SimWorkload
import org.opendc.simulator.failures.FailureDomain
import org.opendc.utils.flow.EventFlow
import org.opendc.utils.flow.StateFlow
import java.time.Clock
import java.util.UUID
-import kotlin.random.Random
/**
* A basic implementation of the [BareMetalDriver] that simulates an [Image] running on a bare-metal machine.
@@ -88,7 +80,7 @@ public class SimBareMetalDriver(
* The machine state.
*/
private val nodeState =
- StateFlow(Node(uid, name, metadata + ("driver" to this), NodeState.SHUTOFF, EmptyImage, null, events))
+ StateFlow(Node(uid, name, metadata + ("driver" to this), NodeState.SHUTOFF, flavor, Image.EMPTY, events))
/**
* The [SimBareMetalMachine] we use to run the workload.
@@ -103,20 +95,10 @@ public class SimBareMetalDriver(
override val powerDraw: Flow<Double> = cpuPowerModel.getPowerDraw(this)
/**
- * The internal random instance.
- */
- private val random = Random(uid.leastSignificantBits xor uid.mostSignificantBits)
-
- /**
* The [Job] that runs the simulated workload.
*/
private var job: Job? = null
- /**
- * The event stream to publish to for the server.
- */
- private var serverEvents: EventFlow<ServerEvent>? = null
-
override suspend fun init(): Node {
return nodeState.value
}
@@ -127,51 +109,13 @@ public class SimBareMetalDriver(
return node
}
- val events = EventFlow<ServerEvent>()
- serverEvents = events
- val server = Server(
- UUID(random.nextLong(), random.nextLong()),
- node.name,
- emptyMap(),
- flavor,
- node.image,
- ServerState.BUILD,
- ServiceRegistry().put(BareMetalDriver, this@SimBareMetalDriver),
- events
- )
-
- val delegate = (node.image as SimWorkloadImage).workload
- // Wrap the workload to pass in a ComputeSimExecutionContext
- val workload = object : SimWorkload {
- lateinit var wrappedCtx: ComputeSimExecutionContext
-
- override fun onStart(ctx: SimExecutionContext) {
- wrappedCtx = object : ComputeSimExecutionContext, SimExecutionContext by ctx {
- override val server: Server
- get() = nodeState.value.server!!
-
- override fun toString(): String = "WrappedSimExecutionContext"
- }
-
- delegate.onStart(wrappedCtx)
- }
-
- override fun onStart(ctx: SimExecutionContext, cpu: Int): SimResourceCommand {
- return delegate.onStart(wrappedCtx, cpu)
- }
-
- override fun onNext(ctx: SimExecutionContext, cpu: Int, remainingWork: Double): SimResourceCommand {
- return delegate.onNext(wrappedCtx, cpu, remainingWork)
- }
-
- override fun toString(): String = "SimWorkloadWrapper(delegate=$delegate)"
- }
+ val workload = node.image.tags["workload"] as SimWorkload
job = coroutineScope.launch {
delay(1) // TODO Introduce boot time
initMachine()
try {
- machine.run(workload)
+ machine.run(workload, mapOf("driver" to this@SimBareMetalDriver, "node" to node))
exitMachine(null)
} catch (_: CancellationException) {
// Ignored
@@ -180,31 +124,21 @@ public class SimBareMetalDriver(
}
}
- setNode(node.copy(state = NodeState.BOOT, server = server))
+ setNode(node.copy(state = NodeState.BOOT))
return nodeState.value
}
private fun initMachine() {
- val server = nodeState.value.server?.copy(state = ServerState.ACTIVE)
- setNode(nodeState.value.copy(state = NodeState.ACTIVE, server = server))
+ setNode(nodeState.value.copy(state = NodeState.ACTIVE))
}
private fun exitMachine(cause: Throwable?) {
- val newServerState =
- if (cause == null)
- ServerState.SHUTOFF
- else
- ServerState.ERROR
val newNodeState =
if (cause == null)
- nodeState.value.state
+ NodeState.SHUTOFF
else
NodeState.ERROR
- val server = nodeState.value.server?.copy(state = newServerState)
- setNode(nodeState.value.copy(state = newNodeState, server = server))
-
- serverEvents?.close()
- serverEvents = null
+ setNode(nodeState.value.copy(state = newNodeState))
}
override suspend fun stop(): Node {
@@ -214,7 +148,7 @@ public class SimBareMetalDriver(
}
job?.cancelAndJoin()
- setNode(node.copy(state = NodeState.SHUTOFF, server = null))
+ setNode(node.copy(state = NodeState.SHUTOFF))
return node
}
@@ -236,13 +170,6 @@ public class SimBareMetalDriver(
events.emit(NodeEvent.StateChanged(value, field.state))
}
- val oldServer = field.server
- val newServer = value.server
-
- if (oldServer != null && newServer != null && oldServer.state != newServer.state) {
- serverEvents?.emit(ServerEvent.StateChanged(newServer, oldServer.state))
- }
-
nodeState.value = value
}
@@ -250,13 +177,11 @@ public class SimBareMetalDriver(
get() = coroutineScope
override suspend fun fail() {
- val server = nodeState.value.server?.copy(state = ServerState.ERROR)
- setNode(nodeState.value.copy(state = NodeState.ERROR, server = server))
+ setNode(nodeState.value.copy(state = NodeState.ERROR))
}
override suspend fun recover() {
- val server = nodeState.value.server?.copy(state = ServerState.ACTIVE)
- setNode(nodeState.value.copy(state = NodeState.ACTIVE, server = server))
+ setNode(nodeState.value.copy(state = NodeState.ACTIVE))
}
override fun toString(): String = "SimBareMetalDriver(node = ${nodeState.value.uid})"
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt
new file mode 100644
index 00000000..fd547d3d
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt
@@ -0,0 +1,303 @@
+/*
+ * 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
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.Flow
+import mu.KotlinLogging
+import org.opendc.compute.api.Flavor
+import org.opendc.compute.api.Server
+import org.opendc.compute.api.ServerState
+import org.opendc.compute.service.driver.*
+import org.opendc.metal.Node
+import org.opendc.simulator.compute.*
+import org.opendc.simulator.compute.interference.IMAGE_PERF_INTERFERENCE_MODEL
+import org.opendc.simulator.compute.interference.PerformanceInterferenceModel
+import org.opendc.simulator.compute.model.MemoryUnit
+import org.opendc.simulator.compute.workload.SimResourceCommand
+import org.opendc.simulator.compute.workload.SimWorkload
+import org.opendc.utils.flow.EventFlow
+import java.util.*
+import kotlin.coroutines.resume
+
+/**
+ * A [Host] that is simulates virtual machines on a physical machine using [SimHypervisor].
+ */
+public class SimHost(
+ public val node: Node,
+ private val coroutineScope: CoroutineScope,
+ hypervisor: SimHypervisorProvider
+) : Host, SimWorkload {
+ /**
+ * The logger instance of this server.
+ */
+ private val logger = KotlinLogging.logger {}
+
+ /**
+ * The execution context in which the [Host] runs.
+ */
+ private lateinit var ctx: SimExecutionContext
+
+ override val events: Flow<HostEvent>
+ get() = _events
+ internal val _events = EventFlow<HostEvent>()
+
+ /**
+ * The event listeners registered with this host.
+ */
+ private val listeners = mutableListOf<HostListener>()
+
+ /**
+ * Current total memory use of the images on this hypervisor.
+ */
+ private var availableMemory: Long = 0
+
+ /**
+ * The hypervisor to run multiple workloads.
+ */
+ private val hypervisor = hypervisor.create(
+ object : SimHypervisor.Listener {
+ override fun onSliceFinish(
+ hypervisor: SimHypervisor,
+ requestedWork: Long,
+ grantedWork: Long,
+ overcommittedWork: Long,
+ interferedWork: Long,
+ cpuUsage: Double,
+ cpuDemand: Double
+ ) {
+ _events.emit(
+ HostEvent.SliceFinished(
+ this@SimHost,
+ requestedWork,
+ grantedWork,
+ overcommittedWork,
+ interferedWork,
+ cpuUsage,
+ cpuDemand,
+ guests.size
+ )
+ )
+ }
+ }
+ )
+
+ /**
+ * The virtual machines running on the hypervisor.
+ */
+ private val guests = HashMap<Server, Guest>()
+
+ override val uid: UUID
+ get() = node.uid
+
+ override val name: String
+ get() = node.name
+
+ override val model: HostModel
+ get() = HostModel(node.flavor.cpuCount, node.flavor.memorySize)
+
+ override val state: HostState
+ get() = _state
+ private var _state: HostState = HostState.UP
+ set(value) {
+ listeners.forEach { it.onStateChanged(this, value) }
+ field = value
+ }
+
+ override fun canFit(server: Server): Boolean {
+ val sufficientMemory = availableMemory > server.flavor.memorySize
+ val enoughCpus = ctx.machine.cpus.size >= server.flavor.cpuCount
+ val canFit = hypervisor.canFit(server.flavor.toMachineModel())
+
+ return sufficientMemory && enoughCpus && canFit
+ }
+
+ override suspend fun spawn(server: Server, start: Boolean) {
+ // Return if the server already exists on this host
+ if (server in this) {
+ return
+ }
+
+ require(canFit(server)) { "Server does not fit" }
+ val guest = Guest(server, hypervisor.createMachine(server.flavor.toMachineModel()))
+ guests[server] = guest
+
+ if (start) {
+ guest.start()
+ }
+
+ _events.emit(HostEvent.VmsUpdated(this, guests.count { it.value.state == ServerState.ACTIVE }, availableMemory))
+ }
+
+ override fun contains(server: Server): Boolean {
+ return server in guests
+ }
+
+ override suspend fun start(server: Server) {
+ val guest = requireNotNull(guests[server]) { "Unknown server ${server.uid} at host $uid" }
+ guest.start()
+ }
+
+ override suspend fun stop(server: Server) {
+ val guest = requireNotNull(guests[server]) { "Unknown server ${server.uid} at host $uid" }
+ guest.stop()
+ }
+
+ override suspend fun terminate(server: Server) {
+ val guest = guests.remove(server) ?: return
+ guest.terminate()
+ }
+
+ override fun addListener(listener: HostListener) {
+ listeners.add(listener)
+ }
+
+ override fun removeListener(listener: HostListener) {
+ listeners.remove(listener)
+ }
+
+ /**
+ * Convert flavor to machine model.
+ */
+ private fun Flavor.toMachineModel(): SimMachineModel {
+ val originalCpu = ctx.machine.cpus[0]
+ val processingNode = originalCpu.node.copy(coreCount = cpuCount)
+ val processingUnits = (0 until cpuCount).map { originalCpu.copy(id = it, node = processingNode) }
+ val memoryUnits = listOf(MemoryUnit("Generic", "Generic", 3200.0, memorySize))
+
+ return SimMachineModel(processingUnits, memoryUnits)
+ }
+
+ private fun onGuestStart(vm: Guest) {
+ guests.forEach { _, guest ->
+ if (guest.state == ServerState.ACTIVE) {
+ vm.performanceInterferenceModel?.onStart(vm.server.image.name)
+ }
+ }
+
+ listeners.forEach { it.onStateChanged(this, vm.server, vm.state) }
+ }
+
+ private fun onGuestStop(vm: Guest) {
+ guests.forEach { _, guest ->
+ if (guest.state == ServerState.ACTIVE) {
+ vm.performanceInterferenceModel?.onStop(vm.server.image.name)
+ }
+ }
+
+ listeners.forEach { it.onStateChanged(this, vm.server, vm.state) }
+
+ _events.emit(HostEvent.VmsUpdated(this@SimHost, guests.count { it.value.state == ServerState.ACTIVE }, availableMemory))
+ }
+
+ /**
+ * A virtual machine instance that the driver manages.
+ */
+ private inner class Guest(val server: Server, val machine: SimMachine) {
+ val performanceInterferenceModel: PerformanceInterferenceModel? = server.image.tags[IMAGE_PERF_INTERFERENCE_MODEL] as? PerformanceInterferenceModel?
+
+ var state: ServerState = ServerState.SHUTOFF
+
+ suspend fun start() {
+ when (state) {
+ ServerState.SHUTOFF -> {
+ logger.info { "User requested to start server ${server.uid}" }
+ launch()
+ }
+ ServerState.ACTIVE -> return
+ else -> assert(false) { "Invalid state transition" }
+ }
+ }
+
+ suspend fun stop() {
+ when (state) {
+ ServerState.ACTIVE, ServerState.ERROR -> {
+ val job = job ?: throw IllegalStateException("Server should be active")
+ job.cancel()
+ job.join()
+ }
+ ServerState.SHUTOFF -> return
+ else -> assert(false) { "Invalid state transition" }
+ }
+ }
+
+ suspend fun terminate() {
+ stop()
+ }
+
+ private var job: Job? = null
+
+ private suspend fun launch() = suspendCancellableCoroutine<Unit> { cont ->
+ assert(job == null) { "Concurrent job running" }
+ val workload = server.image.tags["workload"] as SimWorkload
+
+ val job = coroutineScope.launch {
+ delay(1) // TODO Introduce boot time
+ init()
+ cont.resume(Unit)
+ try {
+ machine.run(workload, mapOf("driver" to this@SimHost, "server" to server))
+ exit(null)
+ } catch (cause: Throwable) {
+ exit(cause)
+ } finally {
+ machine.close()
+ }
+ }
+ this.job = job
+ job.invokeOnCompletion {
+ this.job = null
+ }
+ }
+
+ private fun init() {
+ state = ServerState.ACTIVE
+ onGuestStart(this)
+ }
+
+ private fun exit(cause: Throwable?) {
+ state =
+ if (cause == null)
+ ServerState.SHUTOFF
+ else
+ ServerState.ERROR
+
+ availableMemory += server.flavor.memorySize
+ onGuestStop(this)
+ }
+ }
+
+ override fun onStart(ctx: SimExecutionContext) {
+ this.ctx = ctx
+ this.availableMemory = ctx.machine.memory.map { it.size }.sum()
+ this.hypervisor.onStart(ctx)
+ }
+
+ override fun onStart(ctx: SimExecutionContext, cpu: Int): SimResourceCommand {
+ return hypervisor.onStart(ctx, cpu)
+ }
+
+ override fun onNext(ctx: SimExecutionContext, cpu: Int, remainingWork: Double): SimResourceCommand {
+ return hypervisor.onNext(ctx, cpu, remainingWork)
+ }
+}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHostProvisioner.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHostProvisioner.kt
new file mode 100644
index 00000000..bb03777b
--- /dev/null
+++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHostProvisioner.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.simulator
+
+import kotlinx.coroutines.*
+import org.opendc.compute.api.Image
+import org.opendc.compute.service.driver.Host
+import org.opendc.metal.Node
+import org.opendc.metal.service.ProvisioningService
+import org.opendc.simulator.compute.SimHypervisorProvider
+import kotlin.coroutines.CoroutineContext
+
+/**
+ * A helper class to provision [SimHost]s on top of bare-metal machines using the [ProvisioningService].
+ *
+ * @param context The [CoroutineContext] to use.
+ * @param metal The [ProvisioningService] to use.
+ * @param hypervisor The type of hypervisor to use.
+ */
+public class SimHostProvisioner(
+ private val context: CoroutineContext,
+ private val metal: ProvisioningService,
+ private val hypervisor: SimHypervisorProvider
+) : AutoCloseable {
+ /**
+ * The [CoroutineScope] of the service bounded by the lifecycle of the service.
+ */
+ private val scope = CoroutineScope(context)
+
+ /**
+ * Provision all machines with a host.
+ */
+ public suspend fun provisionAll(): List<Host> = coroutineScope {
+ metal.nodes().map { node -> async { provision(node) } }.awaitAll()
+ }
+
+ /**
+ * Provision the specified [Node].
+ */
+ public suspend fun provision(node: Node): Host = coroutineScope {
+ val host = SimHost(node, scope, hypervisor)
+ metal.deploy(node, Image(node.uid, node.name, mapOf("workload" to host)))
+ host
+ }
+
+ override fun close() {
+ scope.cancel()
+ }
+}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriver.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriver.kt
deleted file mode 100644
index d7a8a8b2..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtDriver.kt
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * 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
-
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.launch
-import org.opendc.compute.core.*
-import org.opendc.compute.core.image.Image
-import org.opendc.compute.core.virt.HypervisorEvent
-import org.opendc.compute.core.virt.driver.InsufficientMemoryOnServerException
-import org.opendc.compute.core.virt.driver.VirtDriver
-import org.opendc.core.services.ServiceRegistry
-import org.opendc.simulator.compute.*
-import org.opendc.simulator.compute.interference.IMAGE_PERF_INTERFERENCE_MODEL
-import org.opendc.simulator.compute.interference.PerformanceInterferenceModel
-import org.opendc.simulator.compute.model.MemoryUnit
-import org.opendc.simulator.compute.workload.SimResourceCommand
-import org.opendc.simulator.compute.workload.SimWorkload
-import org.opendc.utils.flow.EventFlow
-import java.util.*
-
-/**
- * A [VirtDriver] that is simulates virtual machines on a physical machine using [SimHypervisor].
- */
-public class SimVirtDriver(private val coroutineScope: CoroutineScope, hypervisor: SimHypervisorProvider) : VirtDriver, SimWorkload {
- /**
- * The execution context in which the [VirtDriver] runs.
- */
- private lateinit var ctx: ComputeSimExecutionContext
-
- /**
- * The server hosting this hypervisor.
- */
- public val server: Server
- get() = ctx.server
-
- /**
- * The [EventFlow] to emit the events.
- */
- internal val eventFlow = EventFlow<HypervisorEvent>()
-
- override val events: Flow<HypervisorEvent> = eventFlow
-
- /**
- * Current total memory use of the images on this hypervisor.
- */
- private var availableMemory: Long = 0
-
- /**
- * The hypervisor to run multiple workloads.
- */
- private val hypervisor = hypervisor.create(
- object : SimHypervisor.Listener {
- override fun onSliceFinish(
- hypervisor: SimHypervisor,
- requestedWork: Long,
- grantedWork: Long,
- overcommittedWork: Long,
- interferedWork: Long,
- cpuUsage: Double,
- cpuDemand: Double
- ) {
- eventFlow.emit(
- HypervisorEvent.SliceFinished(
- this@SimVirtDriver,
- requestedWork,
- grantedWork,
- overcommittedWork,
- interferedWork,
- cpuUsage,
- cpuDemand,
- vms.size,
- ctx.server
- )
- )
- }
- }
- )
-
- /**
- * The virtual machines running on the hypervisor.
- */
- private val vms = HashSet<VirtualMachine>()
-
- override fun canFit(flavor: Flavor): Boolean {
- val sufficientMemory = availableMemory > flavor.memorySize
- val enoughCpus = ctx.machine.cpus.size >= flavor.cpuCount
- val canFit = hypervisor.canFit(flavor.toMachineModel())
-
- return sufficientMemory && enoughCpus && canFit
- }
-
- override suspend fun spawn(name: String, image: Image, flavor: Flavor): Server {
- val requiredMemory = flavor.memorySize
- if (availableMemory - requiredMemory < 0) {
- throw InsufficientMemoryOnServerException()
- }
- require(flavor.cpuCount <= ctx.machine.cpus.size) { "Machine does not fit" }
-
- val events = EventFlow<ServerEvent>()
- val server = Server(
- UUID.randomUUID(),
- name,
- emptyMap(),
- flavor,
- image,
- ServerState.BUILD,
- ServiceRegistry(),
- events
- )
- availableMemory -= requiredMemory
-
- val vm = VirtualMachine(server, events, hypervisor.createMachine(flavor.toMachineModel()))
- vms.add(vm)
- vmStarted(vm)
- eventFlow.emit(HypervisorEvent.VmsUpdated(this, vms.size, availableMemory))
- return server
- }
-
- /**
- * Convert flavor to machine model.
- */
- private fun Flavor.toMachineModel(): SimMachineModel {
- val originalCpu = ctx.machine.cpus[0]
- val processingNode = originalCpu.node.copy(coreCount = cpuCount)
- val processingUnits = (0 until cpuCount).map { originalCpu.copy(id = it, node = processingNode) }
- val memoryUnits = listOf(MemoryUnit("Generic", "Generic", 3200.0, memorySize))
-
- return SimMachineModel(processingUnits, memoryUnits)
- }
-
- private fun vmStarted(vm: VirtualMachine) {
- vms.forEach { it ->
- vm.performanceInterferenceModel?.onStart(it.server.image.name)
- }
- }
-
- private fun vmStopped(vm: VirtualMachine) {
- vms.forEach { it ->
- vm.performanceInterferenceModel?.onStop(it.server.image.name)
- }
- }
-
- /**
- * A virtual machine instance that the driver manages.
- */
- private inner class VirtualMachine(server: Server, val events: EventFlow<ServerEvent>, val machine: SimMachine) {
- val performanceInterferenceModel: PerformanceInterferenceModel? = server.image.tags[IMAGE_PERF_INTERFERENCE_MODEL] as? PerformanceInterferenceModel?
-
- val job = coroutineScope.launch {
- val delegate = (server.image as SimWorkloadImage).workload
- // Wrap the workload to pass in a ComputeSimExecutionContext
- val workload = object : SimWorkload {
- lateinit var wrappedCtx: ComputeSimExecutionContext
-
- override fun onStart(ctx: SimExecutionContext) {
- wrappedCtx = object : ComputeSimExecutionContext, SimExecutionContext by ctx {
- override val server: Server
- get() = server
-
- override fun toString(): String = "WrappedSimExecutionContext"
- }
-
- delegate.onStart(wrappedCtx)
- }
-
- override fun onStart(ctx: SimExecutionContext, cpu: Int): SimResourceCommand {
- return delegate.onStart(wrappedCtx, cpu)
- }
-
- override fun onNext(ctx: SimExecutionContext, cpu: Int, remainingWork: Double): SimResourceCommand {
- return delegate.onNext(wrappedCtx, cpu, remainingWork)
- }
-
- override fun toString(): String = "SimWorkloadWrapper(delegate=$delegate)"
- }
-
- delay(1) // TODO Introduce boot time
- init()
- try {
- machine.run(workload)
- exit(null)
- } catch (cause: Throwable) {
- exit(cause)
- } finally {
- machine.close()
- }
- }
-
- var server: Server = server
- set(value) {
- if (field.state != value.state) {
- events.emit(ServerEvent.StateChanged(value, field.state))
- }
-
- field = value
- }
-
- private fun init() {
- server = server.copy(state = ServerState.ACTIVE)
- }
-
- private fun exit(cause: Throwable?) {
- val serverState =
- if (cause == null)
- ServerState.SHUTOFF
- else
- ServerState.ERROR
- server = server.copy(state = serverState)
- availableMemory += server.flavor.memorySize
- vms.remove(this)
- vmStopped(this)
- eventFlow.emit(HypervisorEvent.VmsUpdated(this@SimVirtDriver, vms.size, availableMemory))
- events.close()
- }
- }
-
- override fun onStart(ctx: SimExecutionContext) {
- this.ctx = ctx as ComputeSimExecutionContext
- this.availableMemory = ctx.machine.memory.map { it.size }.sum()
- this.hypervisor.onStart(ctx)
- }
-
- override fun onStart(ctx: SimExecutionContext, cpu: Int): SimResourceCommand {
- return hypervisor.onStart(ctx, cpu)
- }
-
- override fun onNext(ctx: SimExecutionContext, cpu: Int, remainingWork: Double): SimResourceCommand {
- return hypervisor.onNext(ctx, cpu, remainingWork)
- }
-}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtProvisioningService.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtProvisioningService.kt
deleted file mode 100644
index defea888..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimVirtProvisioningService.kt
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * 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
-
-import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import mu.KotlinLogging
-import org.opendc.compute.core.Flavor
-import org.opendc.compute.core.Server
-import org.opendc.compute.core.ServerEvent
-import org.opendc.compute.core.ServerState
-import org.opendc.compute.core.image.Image
-import org.opendc.compute.core.metal.service.ProvisioningService
-import org.opendc.compute.core.virt.HypervisorEvent
-import org.opendc.compute.core.virt.driver.InsufficientMemoryOnServerException
-import org.opendc.compute.core.virt.driver.VirtDriver
-import org.opendc.compute.core.virt.service.VirtProvisioningEvent
-import org.opendc.compute.core.virt.service.VirtProvisioningService
-import org.opendc.compute.core.virt.service.events.*
-import org.opendc.compute.simulator.allocation.AllocationPolicy
-import org.opendc.simulator.compute.SimHypervisorProvider
-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.resume
-import kotlin.math.max
-
-@OptIn(ExperimentalCoroutinesApi::class)
-public class SimVirtProvisioningService(
- private val coroutineScope: CoroutineScope,
- private val clock: Clock,
- private val provisioningService: ProvisioningService,
- public val allocationPolicy: AllocationPolicy,
- private val tracer: EventTracer,
- private val hypervisor: SimHypervisorProvider,
- private val schedulingQuantum: Long = 300000, // 5 minutes in milliseconds
-) : VirtProvisioningService {
- /**
- * The logger instance to use.
- */
- private val logger = KotlinLogging.logger {}
-
- /**
- * The hypervisors that have been launched by the service.
- */
- private val hypervisors: MutableMap<Server, HypervisorView> = mutableMapOf()
-
- /**
- * The available hypervisors.
- */
- private val availableHypervisors: MutableSet<HypervisorView> = mutableSetOf()
-
- /**
- * The incoming images to be processed by the provisioner.
- */
- private val incomingImages: Deque<ImageView> = ArrayDeque()
-
- /**
- * The active images in the system.
- */
- private val activeImages: MutableSet<ImageView> = 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()
-
- /**
- * The [EventFlow] to emit the events.
- */
- internal val eventFlow = EventFlow<VirtProvisioningEvent>()
-
- override val events: Flow<VirtProvisioningEvent> = eventFlow
-
- /**
- * The [TimerScheduler] to use for scheduling the scheduler cycles.
- */
- private var scheduler: TimerScheduler<Unit> = TimerScheduler(coroutineScope, clock)
-
- init {
- coroutineScope.launch {
- val provisionedNodes = provisioningService.nodes()
- provisionedNodes.forEach { node ->
- val workload = SimVirtDriver(coroutineScope, hypervisor)
- val hypervisorImage = SimWorkloadImage(UUID.randomUUID(), "vmm", emptyMap(), workload)
- launch {
- var init = false
- val deployedNode = provisioningService.deploy(node, hypervisorImage)
- val server = deployedNode.server!!
- server.events.onEach { event ->
- when (event) {
- is ServerEvent.StateChanged -> {
- if (!init) {
- init = true
- }
- stateChanged(event.server)
- }
- }
- }.launchIn(this)
-
- delay(1)
- onHypervisorAvailable(server, workload)
- }
- }
- }
- }
-
- override suspend fun drivers(): Set<VirtDriver> {
- return availableHypervisors.map { it.driver }.toSet()
- }
-
- override val hostCount: Int = hypervisors.size
-
- override suspend fun deploy(
- name: String,
- image: Image,
- flavor: Flavor
- ): Server {
- tracer.commit(VmSubmissionEvent(name, image, flavor))
-
- eventFlow.emit(
- VirtProvisioningEvent.MetricsAvailable(
- this@SimVirtProvisioningService,
- hypervisors.size,
- availableHypervisors.size,
- ++submittedVms,
- runningVms,
- finishedVms,
- ++queuedVms,
- unscheduledVms
- )
- )
-
- return suspendCancellableCoroutine<Server> { cont ->
- val vmInstance = ImageView(name, image, flavor, cont)
- incomingImages += vmInstance
- requestCycle()
- }
- }
-
- override suspend fun terminate() {
- val provisionedNodes = provisioningService.nodes()
- provisionedNodes.forEach { node -> provisioningService.stop(node) }
- }
-
- 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) {
- coroutineScope.launch { schedule() }
- }
- }
-
- private suspend fun schedule() {
- while (incomingImages.isNotEmpty()) {
- val imageInstance = incomingImages.peekFirst()
- val requiredMemory = imageInstance.flavor.memorySize
- val selectedHv = allocationLogic.select(availableHypervisors, imageInstance)
-
- if (selectedHv == null || !selectedHv.driver.canFit(imageInstance.flavor)) {
- logger.trace { "Image ${imageInstance.image} selected for scheduling but no capacity available for it." }
-
- if (requiredMemory > maxMemory || imageInstance.flavor.cpuCount > maxCores) {
- tracer.commit(VmSubmissionInvalidEvent(imageInstance.name))
-
- eventFlow.emit(
- VirtProvisioningEvent.MetricsAvailable(
- this@SimVirtProvisioningService,
- hypervisors.size,
- availableHypervisors.size,
- submittedVms,
- runningVms,
- finishedVms,
- --queuedVms,
- ++unscheduledVms
- )
- )
-
- // Remove the incoming image
- incomingImages.poll()
-
- logger.warn("Failed to spawn ${imageInstance.image}: does not fit [${clock.millis()}]")
- continue
- } else {
- break
- }
- }
-
- try {
- logger.info { "[${clock.millis()}] Spawning ${imageInstance.image} on ${selectedHv.server.uid} ${selectedHv.server.name} ${selectedHv.server.flavor}" }
- incomingImages.poll()
-
- // Speculatively update the hypervisor view information to prevent other images in the queue from
- // deciding on stale values.
- selectedHv.numberOfActiveServers++
- selectedHv.provisionedCores += imageInstance.flavor.cpuCount
- selectedHv.availableMemory -= requiredMemory // XXX Temporary hack
-
- val server = selectedHv.driver.spawn(
- imageInstance.name,
- imageInstance.image,
- imageInstance.flavor
- )
- imageInstance.server = server
- imageInstance.continuation.resume(server)
-
- tracer.commit(VmScheduledEvent(imageInstance.name))
-
- eventFlow.emit(
- VirtProvisioningEvent.MetricsAvailable(
- this@SimVirtProvisioningService,
- hypervisors.size,
- availableHypervisors.size,
- submittedVms,
- ++runningVms,
- finishedVms,
- --queuedVms,
- unscheduledVms
- )
- )
- activeImages += imageInstance
-
- server.events
- .onEach { event ->
- when (event) {
- is ServerEvent.StateChanged -> {
- if (event.server.state == ServerState.SHUTOFF) {
- logger.info { "[${clock.millis()}] Server ${event.server.uid} ${event.server.name} ${event.server.flavor} finished." }
-
- tracer.commit(VmStoppedEvent(event.server.name))
-
- eventFlow.emit(
- VirtProvisioningEvent.MetricsAvailable(
- this@SimVirtProvisioningService,
- hypervisors.size,
- availableHypervisors.size,
- submittedVms,
- --runningVms,
- ++finishedVms,
- queuedVms,
- unscheduledVms
- )
- )
-
- activeImages -= imageInstance
- selectedHv.provisionedCores -= server.flavor.cpuCount
-
- // Try to reschedule if needed
- if (incomingImages.isNotEmpty()) {
- requestCycle()
- }
- }
- }
- }
- }
- .launchIn(coroutineScope)
- } catch (e: InsufficientMemoryOnServerException) {
- logger.error("Failed to deploy VM", e)
-
- selectedHv.numberOfActiveServers--
- selectedHv.provisionedCores -= imageInstance.flavor.cpuCount
- selectedHv.availableMemory += requiredMemory
- } catch (e: Throwable) {
- logger.error("Failed to deploy VM", e)
- }
- }
- }
-
- private fun stateChanged(server: Server) {
- when (server.state) {
- ServerState.ACTIVE -> {
- logger.debug { "[${clock.millis()}] Server ${server.uid} available: ${server.state}" }
-
- if (server in hypervisors) {
- // Corner case for when the hypervisor already exists
- availableHypervisors += hypervisors.getValue(server)
- } else {
- val hv = HypervisorView(
- server.uid,
- server,
- 0,
- server.flavor.memorySize,
- 0
- )
- maxCores = max(maxCores, server.flavor.cpuCount)
- maxMemory = max(maxMemory, server.flavor.memorySize)
- hypervisors[server] = hv
- }
-
- tracer.commit(HypervisorAvailableEvent(server.uid))
-
- eventFlow.emit(
- VirtProvisioningEvent.MetricsAvailable(
- this@SimVirtProvisioningService,
- hypervisors.size,
- availableHypervisors.size,
- submittedVms,
- runningVms,
- finishedVms,
- queuedVms,
- unscheduledVms
- )
- )
-
- // Re-schedule on the new machine
- if (incomingImages.isNotEmpty()) {
- requestCycle()
- }
- }
- ServerState.SHUTOFF, ServerState.ERROR -> {
- logger.debug { "[${clock.millis()}] Server ${server.uid} unavailable: ${server.state}" }
- val hv = hypervisors[server] ?: return
- availableHypervisors -= hv
-
- tracer.commit(HypervisorUnavailableEvent(hv.uid))
-
- eventFlow.emit(
- VirtProvisioningEvent.MetricsAvailable(
- this@SimVirtProvisioningService,
- hypervisors.size,
- availableHypervisors.size,
- submittedVms,
- runningVms,
- finishedVms,
- queuedVms,
- unscheduledVms
- )
- )
-
- if (incomingImages.isNotEmpty()) {
- requestCycle()
- }
- }
- else -> throw IllegalStateException()
- }
- }
-
- private fun onHypervisorAvailable(server: Server, hypervisor: SimVirtDriver) {
- val hv = hypervisors[server] ?: return
- hv.driver = hypervisor
- availableHypervisors += hv
-
- tracer.commit(HypervisorAvailableEvent(hv.uid))
-
- eventFlow.emit(
- VirtProvisioningEvent.MetricsAvailable(
- this@SimVirtProvisioningService,
- hypervisors.size,
- availableHypervisors.size,
- submittedVms,
- runningVms,
- finishedVms,
- queuedVms,
- unscheduledVms
- )
- )
-
- hv.driver.events
- .onEach { event ->
- if (event is HypervisorEvent.VmsUpdated) {
- hv.numberOfActiveServers = event.numberOfActiveServers
- hv.availableMemory = event.availableMemory
- }
- }.launchIn(coroutineScope)
-
- requestCycle()
- }
-
- public data class ImageView(
- public val name: String,
- public val image: Image,
- public val flavor: Flavor,
- public val continuation: Continuation<Server>,
- public var server: Server? = null
- )
-}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimWorkloadImage.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimWorkloadImage.kt
deleted file mode 100644
index b48de1d5..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimWorkloadImage.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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
-
-import org.opendc.compute.core.image.Image
-import org.opendc.core.resource.TagContainer
-import org.opendc.simulator.compute.workload.SimWorkload
-import java.util.*
-
-/**
- * An application [Image] that runs a [SimWorkload].
- *
- * @property uid The unique identifier of this image.
- * @property name The name of this image.
- * @property tags The tags attached to the image.
- * @property workload The workload to run for this image.
- */
-public data class SimWorkloadImage(
- public override val uid: UUID,
- public override val name: String,
- public override val tags: TagContainer,
- public val workload: SimWorkload
-) : Image
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AllocationPolicy.kt
deleted file mode 100644
index 2018b9f2..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AllocationPolicy.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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 org.opendc.compute.core.metal.Node
-import org.opendc.compute.simulator.HypervisorView
-import org.opendc.compute.simulator.SimVirtProvisioningService
-
-/**
- * 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<HypervisorView>,
- image: SimVirtProvisioningService.ImageView
- ): HypervisorView?
- }
-
- /**
- * Builds the logic of the policy.
- */
- public operator fun invoke(): Logic
-}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AvailableCoreMemoryAllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AvailableCoreMemoryAllocationPolicy.kt
deleted file mode 100644
index 38a07b2b..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AvailableCoreMemoryAllocationPolicy.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 org.opendc.compute.simulator.HypervisorView
-
-/**
- * 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<HypervisorView> =
- compareBy<HypervisorView> { -it.availableMemory / it.server.flavor.cpuCount }
- .run { if (reversed) reversed() else this }
- }
-}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AvailableMemoryAllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AvailableMemoryAllocationPolicy.kt
deleted file mode 100644
index e87abd7b..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/AvailableMemoryAllocationPolicy.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 org.opendc.compute.simulator.HypervisorView
-
-/**
- * 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<HypervisorView> = compareBy<HypervisorView> { -it.availableMemory }
- .run { if (reversed) reversed() else this }
- }
-}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ComparableAllocationPolicyLogic.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ComparableAllocationPolicyLogic.kt
deleted file mode 100644
index 4470eab9..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ComparableAllocationPolicyLogic.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 org.opendc.compute.simulator.HypervisorView
-import org.opendc.compute.simulator.SimVirtProvisioningService
-
-/**
- * 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<HypervisorView>
-
- override fun select(
- hypervisors: Set<HypervisorView>,
- image: SimVirtProvisioningService.ImageView
- ): HypervisorView? {
- return hypervisors.asSequence()
- .filter { hv ->
- val fitsMemory = hv.availableMemory >= (image.flavor.memorySize)
- val fitsCpu = hv.server.flavor.cpuCount >= image.flavor.cpuCount
- fitsMemory && fitsCpu
- }
- .minWithOrNull(comparator.thenBy { it.server.uid })
- }
-}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/NumberOfActiveServersAllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/NumberOfActiveServersAllocationPolicy.kt
deleted file mode 100644
index 5e2b895c..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/NumberOfActiveServersAllocationPolicy.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 org.opendc.compute.simulator.HypervisorView
-
-/**
- * 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<HypervisorView> = compareBy<HypervisorView> { it.numberOfActiveServers }
- .run { if (reversed) reversed() else this }
- }
-}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ProvisionedCoresAllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ProvisionedCoresAllocationPolicy.kt
deleted file mode 100644
index 4344d979..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ProvisionedCoresAllocationPolicy.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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 org.opendc.compute.simulator.HypervisorView
-
-/**
- * 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<HypervisorView> =
- compareBy<HypervisorView> { it.provisionedCores / it.server.flavor.cpuCount }
- .run { if (reversed) reversed() else this }
- }
-}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/RandomAllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/RandomAllocationPolicy.kt
deleted file mode 100644
index ac34f410..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/RandomAllocationPolicy.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 org.opendc.compute.simulator.HypervisorView
-import org.opendc.compute.simulator.SimVirtProvisioningService
-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<HypervisorView>,
- image: SimVirtProvisioningService.ImageView
- ): HypervisorView? {
- return hypervisors.asIterable()
- .filter { hv ->
- val fitsMemory = hv.availableMemory >= (image.image.tags["required-memory"] as Long)
- val fitsCpu = hv.server.flavor.cpuCount >= image.flavor.cpuCount
- fitsMemory && fitsCpu
- }
- .randomOrNull(random)
- }
- }
-}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ReplayAllocationPolicy.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ReplayAllocationPolicy.kt
deleted file mode 100644
index 5312f4da..00000000
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/allocation/ReplayAllocationPolicy.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.simulator.HypervisorView
-import org.opendc.compute.simulator.SimVirtProvisioningService
-
-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<HypervisorView>,
- image: SimVirtProvisioningService.ImageView
- ): HypervisorView? {
- val clusterName = vmPlacements[image.name]
- ?: throw IllegalStateException("Could not find placement data in VM placement file for VM ${image.name}")
- val machinesInCluster = hypervisors.filter { it.server.name.contains(clusterName) }
-
- if (machinesInCluster.isEmpty()) {
- logger.info { "Could not find any machines belonging to cluster $clusterName for image ${image.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")
- }
- }
-}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/api/CpuPowerModel.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/api/CpuPowerModel.kt
index ee9e130b..0141bc8c 100644
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/api/CpuPowerModel.kt
+++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/api/CpuPowerModel.kt
@@ -2,7 +2,7 @@ package org.opendc.compute.simulator.power.api
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
-import org.opendc.compute.core.metal.driver.BareMetalDriver
+import org.opendc.metal.driver.BareMetalDriver
public interface CpuPowerModel {
/**
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/models/ZeroIdlePowerDecorator.kt b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/models/ZeroIdlePowerDecorator.kt
index 938e5607..b0c3fa4c 100644
--- a/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/models/ZeroIdlePowerDecorator.kt
+++ b/simulator/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/power/models/ZeroIdlePowerDecorator.kt
@@ -5,10 +5,10 @@ import org.opendc.compute.simulator.power.api.CpuPowerModel
/**
* A decorator for ignoring the idle power when computing energy consumption of components.
*
- * @param cpuModelWrappee The wrappe of a [CpuPowerModel].
+ * @param delegate The [CpuPowerModel] to delegate to.
*/
-public class ZeroIdlePowerDecorator(private val cpuModelWrappee: CpuPowerModel) : CpuPowerModel {
+public class ZeroIdlePowerDecorator(private val delegate: CpuPowerModel) : CpuPowerModel {
override fun computeCpuPower(cpuUtil: Double): Double {
- return if (cpuUtil == 0.0) 0.0 else cpuModelWrappee.computeCpuPower(cpuUtil)
+ return if (cpuUtil == 0.0) 0.0 else delegate.computeCpuPower(cpuUtil)
}
}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimBareMetalDriverTest.kt b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimBareMetalDriverTest.kt
index fb8a5f47..0d90376e 100644
--- a/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimBareMetalDriverTest.kt
+++ b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimBareMetalDriverTest.kt
@@ -30,8 +30,10 @@ import kotlinx.coroutines.withContext
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
-import org.opendc.compute.core.ServerEvent
-import org.opendc.compute.core.ServerState
+import org.junit.jupiter.api.assertAll
+import org.opendc.compute.api.Image
+import org.opendc.metal.NodeEvent
+import org.opendc.metal.NodeState
import org.opendc.simulator.compute.SimMachineModel
import org.opendc.simulator.compute.model.MemoryUnit
import org.opendc.simulator.compute.model.ProcessingNode
@@ -59,22 +61,21 @@ internal class SimBareMetalDriverTest {
val testScope = TestCoroutineScope()
val clock = DelayControllerClockAdapter(testScope)
- var finalState: ServerState = ServerState.BUILD
+ var finalState: NodeState = NodeState.UNKNOWN
var finalTime = 0L
testScope.launch {
val driver = SimBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), machineModel)
- val image = SimWorkloadImage(UUID.randomUUID(), "<unnamed>", emptyMap(), SimFlopsWorkload(4_000, utilization = 1.0))
-
+ val image = Image(UUID.randomUUID(), "<unnamed>", mapOf("workload" to SimFlopsWorkload(4_000, utilization = 1.0)))
// Batch driver commands
withContext(coroutineContext) {
driver.init()
driver.setImage(image)
- val server = driver.start().server!!
- server.events.collect { event ->
+ val node = driver.start()
+ node.events.collect { event ->
when (event) {
- is ServerEvent.StateChanged -> {
- finalState = event.server.state
+ is NodeEvent.StateChanged -> {
+ finalState = event.node.state
finalTime = clock.millis()
}
}
@@ -83,7 +84,9 @@ internal class SimBareMetalDriverTest {
}
testScope.advanceUntilIdle()
- assertEquals(ServerState.SHUTOFF, finalState)
- assertEquals(501, finalTime)
+ assertAll(
+ { assertEquals(NodeState.SHUTOFF, finalState) },
+ { assertEquals(501, finalTime) }
+ )
}
}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimVirtDriverTest.kt b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt
index 1831eae0..61bff39f 100644
--- a/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimVirtDriverTest.kt
+++ b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt
@@ -24,6 +24,7 @@ package org.opendc.compute.simulator
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@@ -32,8 +33,14 @@ import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertAll
-import org.opendc.compute.core.Flavor
-import org.opendc.compute.core.virt.HypervisorEvent
+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 org.opendc.compute.service.driver.HostEvent
+import org.opendc.metal.Node
+import org.opendc.metal.NodeState
import org.opendc.simulator.compute.SimFairShareHypervisorProvider
import org.opendc.simulator.compute.SimMachineModel
import org.opendc.simulator.compute.model.MemoryUnit
@@ -48,7 +55,7 @@ import java.util.UUID
* Basic test-suite for the hypervisor.
*/
@OptIn(ExperimentalCoroutinesApi::class)
-internal class SimVirtDriverTest {
+internal class SimHostTest {
private lateinit var scope: TestCoroutineScope
private lateinit var clock: Clock
private lateinit var machineModel: SimMachineModel
@@ -75,35 +82,42 @@ internal class SimVirtDriverTest {
var grantedWork = 0L
var overcommittedWork = 0L
+ val node = Node(
+ UUID.randomUUID(), "name", emptyMap(), NodeState.SHUTOFF,
+ Flavor(machineModel.cpus.size, machineModel.memory.map { it.size }.sum()), Image.EMPTY, emptyFlow()
+ )
+
scope.launch {
- val virtDriver = SimVirtDriver(this, SimFairShareHypervisorProvider())
- val vmm = SimWorkloadImage(UUID.randomUUID(), "vmm", emptyMap(), virtDriver)
+ val virtDriver = SimHost(node, this, SimFairShareHypervisorProvider())
+ val vmm = Image(UUID.randomUUID(), "vmm", mapOf("workload" to virtDriver))
val duration = 5 * 60L
- val vmImageA = SimWorkloadImage(
+ val vmImageA = Image(
UUID.randomUUID(),
"<unnamed>",
- emptyMap(),
- SimTraceWorkload(
- sequenceOf(
- SimTraceWorkload.Fragment(duration * 1000, 28.0, 2),
- SimTraceWorkload.Fragment(duration * 1000, 3500.0, 2),
- SimTraceWorkload.Fragment(duration * 1000, 0.0, 2),
- SimTraceWorkload.Fragment(duration * 1000, 183.0, 2)
- ),
+ mapOf(
+ "workload" to SimTraceWorkload(
+ sequenceOf(
+ SimTraceWorkload.Fragment(duration * 1000, 28.0, 2),
+ SimTraceWorkload.Fragment(duration * 1000, 3500.0, 2),
+ SimTraceWorkload.Fragment(duration * 1000, 0.0, 2),
+ SimTraceWorkload.Fragment(duration * 1000, 183.0, 2)
+ ),
+ )
)
)
- val vmImageB = SimWorkloadImage(
+ val vmImageB = Image(
UUID.randomUUID(),
"<unnamed>",
- emptyMap(),
- SimTraceWorkload(
- sequenceOf(
- SimTraceWorkload.Fragment(duration * 1000, 28.0, 2),
- SimTraceWorkload.Fragment(duration * 1000, 3100.0, 2),
- SimTraceWorkload.Fragment(duration * 1000, 0.0, 2),
- SimTraceWorkload.Fragment(duration * 1000, 73.0, 2)
+ mapOf(
+ "workload" to SimTraceWorkload(
+ sequenceOf(
+ SimTraceWorkload.Fragment(duration * 1000, 28.0, 2),
+ SimTraceWorkload.Fragment(duration * 1000, 3100.0, 2),
+ SimTraceWorkload.Fragment(duration * 1000, 0.0, 2),
+ SimTraceWorkload.Fragment(duration * 1000, 73.0, 2)
+ )
)
- ),
+ )
)
val metalDriver =
@@ -119,7 +133,7 @@ internal class SimVirtDriverTest {
virtDriver.events
.onEach { event ->
when (event) {
- is HypervisorEvent.SliceFinished -> {
+ is HostEvent.SliceFinished -> {
requestedWork += event.requestedBurst
grantedWork += event.grantedBurst
overcommittedWork += event.overcommissionedBurst
@@ -128,8 +142,8 @@ internal class SimVirtDriverTest {
}
.launchIn(this)
- virtDriver.spawn("a", vmImageA, flavor)
- virtDriver.spawn("b", vmImageB, flavor)
+ launch { virtDriver.spawn(MockServer(UUID.randomUUID(), "a", flavor, vmImageA)) }
+ launch { virtDriver.spawn(MockServer(UUID.randomUUID(), "b", flavor, vmImageB)) }
}
scope.advanceUntilIdle()
@@ -142,4 +156,20 @@ internal class SimVirtDriverTest {
{ assertEquals(1200006, scope.currentTime) }
)
}
+
+ private class MockServer(
+ override val uid: UUID,
+ override val name: String,
+ override val flavor: Flavor,
+ override val image: Image
+ ) : Server {
+ override val tags: Map<String, String> = emptyMap()
+ override val state: ServerState = ServerState.BUILD
+
+ override fun watch(watcher: ServerWatcher) {}
+
+ override fun unwatch(watcher: ServerWatcher) {}
+
+ override suspend fun refresh() {}
+ }
}
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimProvisioningServiceTest.kt b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimProvisioningServiceTest.kt
index a33a4e5f..33b3db94 100644
--- a/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimProvisioningServiceTest.kt
+++ b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimProvisioningServiceTest.kt
@@ -29,7 +29,8 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestCoroutineScope
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
-import org.opendc.compute.core.metal.service.SimpleProvisioningService
+import org.opendc.compute.api.Image
+import org.opendc.metal.service.SimpleProvisioningService
import org.opendc.simulator.compute.SimMachineModel
import org.opendc.simulator.compute.model.MemoryUnit
import org.opendc.simulator.compute.model.ProcessingNode
@@ -64,7 +65,7 @@ internal class SimProvisioningServiceTest {
val clock = DelayControllerClockAdapter(testScope)
testScope.launch {
- val image = SimWorkloadImage(UUID.randomUUID(), "<unnamed>", emptyMap(), SimFlopsWorkload(1000))
+ val image = Image(UUID.randomUUID(), "<unnamed>", mapOf("machine" to SimFlopsWorkload(1000)))
val driver = SimBareMetalDriver(this, clock, UUID.randomUUID(), "test", emptyMap(), machineModel)
val provisioner = SimpleProvisioningService()
@@ -72,7 +73,7 @@ internal class SimProvisioningServiceTest {
delay(5)
val nodes = provisioner.nodes()
val node = provisioner.deploy(nodes.first(), image)
- node.server!!.events.collect { println(it) }
+ node.events.collect { println(it) }
}
testScope.advanceUntilIdle()
diff --git a/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/power/CpuPowerModelTest.kt b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/power/CpuPowerModelTest.kt
index 7b0c7515..d4d88fb1 100644
--- a/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/power/CpuPowerModelTest.kt
+++ b/simulator/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/power/CpuPowerModelTest.kt
@@ -7,9 +7,9 @@ import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource
-import org.opendc.compute.core.metal.driver.BareMetalDriver
import org.opendc.compute.simulator.power.api.CpuPowerModel
import org.opendc.compute.simulator.power.models.*
+import org.opendc.metal.driver.BareMetalDriver
import java.util.stream.Stream
import kotlin.math.pow