summaryrefslogtreecommitdiff
path: root/opendc-compute
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-compute')
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt10
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimWorkloadMapper.kt1
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/DefaultWorkloadMapper.kt44
-rw-r--r--opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt66
-rw-r--r--opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt70
5 files changed, 137 insertions, 54 deletions
diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt
index d07c50bc..660c1ccc 100644
--- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt
+++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimHost.kt
@@ -34,6 +34,7 @@ import org.opendc.compute.service.driver.telemetry.GuestCpuStats
import org.opendc.compute.service.driver.telemetry.GuestSystemStats
import org.opendc.compute.service.driver.telemetry.HostCpuStats
import org.opendc.compute.service.driver.telemetry.HostSystemStats
+import org.opendc.compute.simulator.internal.DefaultWorkloadMapper
import org.opendc.compute.simulator.internal.Guest
import org.opendc.compute.simulator.internal.GuestListener
import org.opendc.simulator.compute.SimBareMetalMachine
@@ -47,7 +48,6 @@ import org.opendc.simulator.flow2.FlowGraph
import java.time.Duration
import java.time.Instant
import java.util.UUID
-import kotlin.coroutines.CoroutineContext
/**
* A [Host] that is simulates virtual machines on a physical machine using [SimHypervisor].
@@ -56,11 +56,10 @@ public class SimHost(
override val uid: UUID,
override val name: String,
override val meta: Map<String, Any>,
- private val context: CoroutineContext,
graph: FlowGraph,
private val machine: SimBareMetalMachine,
private val hypervisor: SimHypervisor,
- private val mapper: SimWorkloadMapper = SimMetaWorkloadMapper(),
+ private val mapper: SimWorkloadMapper = DefaultWorkloadMapper,
private val optimize: Boolean = false
) : Host, AutoCloseable {
/**
@@ -129,7 +128,6 @@ public class SimHost(
val machine = hypervisor.newMachine(key.flavor.toMachineModel())
val newGuest = Guest(
- context,
clock,
this,
hypervisor,
@@ -250,7 +248,7 @@ public class SimHost(
override fun toString(): String = "SimHost[uid=$uid,name=$name,model=$model]"
- public suspend fun fail() {
+ public fun fail() {
reset(HostState.ERROR)
for (guest in _guests) {
@@ -272,7 +270,7 @@ public class SimHost(
}
/**
- * The [Job] that represents the machine running the hypervisor.
+ * The [SimMachineContext] that represents the machine running the hypervisor.
*/
private var _ctx: SimMachineContext? = null
diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimWorkloadMapper.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimWorkloadMapper.kt
index 7082c5cf..83baa61a 100644
--- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimWorkloadMapper.kt
+++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/SimWorkloadMapper.kt
@@ -22,6 +22,7 @@
package org.opendc.compute.simulator
+import org.opendc.compute.api.Image
import org.opendc.compute.api.Server
import org.opendc.simulator.compute.workload.SimWorkload
diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/DefaultWorkloadMapper.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/DefaultWorkloadMapper.kt
new file mode 100644
index 00000000..c5293a8d
--- /dev/null
+++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/DefaultWorkloadMapper.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.compute.simulator.internal
+
+import org.opendc.compute.api.Server
+import org.opendc.compute.simulator.SimMetaWorkloadMapper
+import org.opendc.compute.simulator.SimWorkloadMapper
+import org.opendc.simulator.compute.workload.SimWorkload
+import org.opendc.simulator.compute.workload.SimWorkloads
+import java.time.Duration
+
+/**
+ * A [SimWorkloadMapper] to introduces a boot delay of 1 ms. This object exists to retain the old behavior while
+ * introducing the possibility of adding custom boot delays.
+ */
+internal object DefaultWorkloadMapper : SimWorkloadMapper {
+ private val delegate = SimMetaWorkloadMapper()
+
+ override fun createWorkload(server: Server): SimWorkload {
+ val workload = delegate.createWorkload(server)
+ val bootWorkload = SimWorkloads.runtime(Duration.ofMillis(1), 0.8)
+ return SimWorkloads.chain(bootWorkload, workload)
+ }
+}
diff --git a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt
index 790d8047..6d3a5bc7 100644
--- a/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt
+++ b/opendc-compute/opendc-compute-simulator/src/main/kotlin/org/opendc/compute/simulator/internal/Guest.kt
@@ -22,12 +22,6 @@
package org.opendc.compute.simulator.internal
-import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
import mu.KotlinLogging
import org.opendc.compute.api.Server
import org.opendc.compute.api.ServerState
@@ -35,20 +29,17 @@ import org.opendc.compute.service.driver.telemetry.GuestCpuStats
import org.opendc.compute.service.driver.telemetry.GuestSystemStats
import org.opendc.compute.simulator.SimHost
import org.opendc.compute.simulator.SimWorkloadMapper
+import org.opendc.simulator.compute.SimMachineContext
import org.opendc.simulator.compute.kernel.SimHypervisor
import org.opendc.simulator.compute.kernel.SimVirtualMachine
-import org.opendc.simulator.compute.runWorkload
-import org.opendc.simulator.compute.workload.SimWorkload
import java.time.Clock
import java.time.Duration
import java.time.Instant
-import kotlin.coroutines.CoroutineContext
/**
* A virtual machine instance that is managed by a [SimHost].
*/
internal class Guest(
- context: CoroutineContext,
private val clock: Clock,
val host: SimHost,
private val hypervisor: SimHypervisor,
@@ -58,11 +49,6 @@ internal class Guest(
val machine: SimVirtualMachine
) {
/**
- * The [CoroutineScope] of the guest.
- */
- private val scope: CoroutineScope = CoroutineScope(context + Job())
-
- /**
* The logger instance of this guest.
*/
private val logger = KotlinLogging.logger {}
@@ -78,7 +64,7 @@ internal class Guest(
/**
* Start the guest.
*/
- suspend fun start() {
+ fun start() {
when (state) {
ServerState.TERMINATED, ServerState.ERROR -> {
logger.info { "User requested to start server ${server.uid}" }
@@ -96,7 +82,7 @@ internal class Guest(
/**
* Stop the guest.
*/
- suspend fun stop() {
+ fun stop() {
when (state) {
ServerState.RUNNING -> doStop(ServerState.TERMINATED)
ServerState.ERROR -> doRecover()
@@ -111,12 +97,11 @@ internal class Guest(
* This operation will stop the guest if it is running on the host and remove all resources associated with the
* guest.
*/
- suspend fun delete() {
+ fun delete() {
stop()
state = ServerState.DELETED
hypervisor.removeMachine(machine)
- scope.cancel()
}
/**
@@ -124,7 +109,7 @@ internal class Guest(
*
* This operation forcibly stops the guest and puts the server into an error state.
*/
- suspend fun fail() {
+ fun fail() {
if (state != ServerState.RUNNING) {
return
}
@@ -135,7 +120,7 @@ internal class Guest(
/**
* Recover the guest if it is in an error state.
*/
- suspend fun recover() {
+ fun recover() {
if (state != ServerState.ERROR) {
return
}
@@ -175,37 +160,34 @@ internal class Guest(
}
/**
- * The [Job] representing the current active virtual machine instance or `null` if no virtual machine is active.
+ * The [SimMachineContext] representing the current active virtual machine instance or `null` if no virtual machine
+ * is active.
*/
- private var job: Job? = null
+ private var ctx: SimMachineContext? = null
/**
* Launch the guest on the simulated
*/
- private suspend fun doStart() {
- assert(job == null) { "Concurrent job running" }
- val workload = mapper.createWorkload(server)
-
- val job = scope.launch { runMachine(workload) }
- this.job = job
+ private fun doStart() {
+ assert(ctx == null) { "Concurrent job running" }
- state = ServerState.RUNNING
onStart()
- job.invokeOnCompletion { cause ->
- this.job = null
- onStop(if (cause != null && cause !is CancellationException) ServerState.ERROR else ServerState.TERMINATED)
+ val workload = mapper.createWorkload(server)
+ val meta = mapOf("driver" to host, "server" to server) + server.meta
+ ctx = machine.startWorkload(workload, meta) { cause ->
+ onStop(if (cause != null) ServerState.ERROR else ServerState.TERMINATED)
+ ctx = null
}
}
/**
* Attempt to stop the server and put it into [target] state.
*/
- private suspend fun doStop(target: ServerState) {
- assert(job != null) { "Invalid job state" }
- val job = job ?: return
- job.cancel()
- job.join()
+ private fun doStop(target: ServerState) {
+ assert(ctx != null) { "Invalid job state" }
+ val ctx = ctx ?: return
+ ctx.shutdown()
state = target
}
@@ -218,14 +200,6 @@ internal class Guest(
}
/**
- * Converge the process that models the virtual machine lifecycle as a coroutine.
- */
- private suspend fun runMachine(workload: SimWorkload) {
- delay(1) // TODO Introduce model for boot time
- machine.runWorkload(workload, mapOf("driver" to host, "server" to server) + server.meta)
- }
-
- /**
* This method is invoked when the guest was started on the host and has booted into a running state.
*/
private fun onStart() {
diff --git a/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt b/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt
index a5999bcd..02be3f28 100644
--- a/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt
+++ b/opendc-compute/opendc-compute-simulator/src/test/kotlin/org/opendc/compute/simulator/SimHostTest.kt
@@ -70,6 +70,74 @@ internal class SimHostTest {
}
/**
+ * Test a single virtual machine hosted by the hypervisor.
+ */
+ @Test
+ fun testSingle() = runSimulation {
+ val duration = 5 * 60L
+
+ val engine = FlowEngine.create(coroutineContext, clock)
+ val graph = engine.newGraph()
+
+ val machine = SimBareMetalMachine.create(graph, machineModel)
+ val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1))
+
+ val host = SimHost(
+ uid = UUID.randomUUID(),
+ name = "test",
+ meta = emptyMap(),
+ graph,
+ machine,
+ hypervisor
+ )
+ val vmImage = MockImage(
+ UUID.randomUUID(),
+ "<unnamed>",
+ emptyMap(),
+ mapOf(
+ "workload" to
+ SimTrace.ofFragments(
+ SimTraceFragment(0, duration * 1000, 2 * 28.0, 2),
+ SimTraceFragment(duration * 1000, duration * 1000, 2 * 3500.0, 2),
+ SimTraceFragment(duration * 2000, duration * 1000, 0.0, 2),
+ SimTraceFragment(duration * 3000, duration * 1000, 2 * 183.0, 2)
+ ).createWorkload(1)
+ )
+ )
+
+ val flavor = MockFlavor(2, 0)
+
+ coroutineScope {
+ launch { host.spawn(MockServer(UUID.randomUUID(), "a", flavor, vmImage)) }
+
+ suspendCancellableCoroutine { cont ->
+ host.addListener(object : HostListener {
+ private var finished = 0
+
+ override fun onStateChanged(host: Host, server: Server, newState: ServerState) {
+ if (newState == ServerState.TERMINATED && ++finished == 1) {
+ cont.resume(Unit)
+ }
+ }
+ })
+ }
+ }
+
+ // Ensure last cycle is collected
+ delay(1000L * duration)
+ host.close()
+
+ val cpuStats = host.getCpuStats()
+
+ assertAll(
+ { assertEquals(639, cpuStats.activeTime, "Active time does not match") },
+ { assertEquals(2360, cpuStats.idleTime, "Idle time does not match") },
+ { assertEquals(56, cpuStats.stealTime, "Steal time does not match") },
+ { assertEquals(1500001, clock.millis()) }
+ )
+ }
+
+ /**
* Test overcommitting of resources by the hypervisor.
*/
@Test
@@ -86,7 +154,6 @@ internal class SimHostTest {
uid = UUID.randomUUID(),
name = "test",
meta = emptyMap(),
- coroutineContext,
graph,
machine,
hypervisor
@@ -169,7 +236,6 @@ internal class SimHostTest {
uid = UUID.randomUUID(),
name = "test",
meta = emptyMap(),
- coroutineContext,
graph,
machine,
hypervisor